diff --git a/DEPS b/DEPS
index b2d92a2..6ed44aa 100644
--- a/DEPS
+++ b/DEPS
@@ -172,11 +172,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': 'b1edfde279feedad4fbf00d0589e61ad8228f58a',
+  'skia_revision': 'db80f6936cbca34496d87c6754167a11286f9c0c',
   # 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': '0fffc5ea1cd63c5fa6e66b82456ffb620a158550',
+  'v8_revision': 'ccb488f8de3dcd53a0ab2d6d5f02438499e27fb2',
   # 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.
@@ -184,7 +184,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'b868861c3478a8a71db49abedd74704d4bb718c0',
+  'angle_revision': '4c7db77e0185d4c41465c4d0fd97845fe775c44d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -192,7 +192,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'bcdac2a1318b898813da55a488e49f2a9e38ad21',
+  'pdfium_revision': 'e87972e30da4cb87f0620baf32cea698f43dd093',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -235,15 +235,15 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '258f61029f406e8552b37f29cf46f2754ce8ccf9',
+  'catapult_revision': '8412598cca10edd8bcb24e31bc522d7465d911fb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
-  'libfuzzer_revision': 'b9f51dc8c98065df0c8da13c051046f5bab833db',
+  'libfuzzer_revision': 'debe7d2d1982e540fbd6bd78604bf001753f9e74',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '3944e7cd71d6f1db78f8548a439ce9d51de1e7de',
+  'devtools_frontend_revision': '693bde6a69a2717a7fbfa5793489f4af6f8a13e5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -303,7 +303,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.
-  'dawn_revision': '540ababb6bee89c96e4eb7beed14e1dbcc377148',
+  'dawn_revision': 'f6eb890f4c9c3d67478c832103e1332e585aa132',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -959,7 +959,7 @@
   },
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + 'e01c847a7042cac7b3b20a6268813c29b802f32e',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '8b5853d4f91a235470be66a73e555b5be7e82dbb',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1050,7 +1050,7 @@
     Var('chromium_git') + '/chromium/deps/hunspell_dictionaries.git' + '@' + '681ca92480ecc11d35feae8c1c00e4e035630f43',
 
   'src/third_party/icu':
-    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + '88ea42af73f4cd3b8b0d0837403d75a989d80504',
+    Var('chromium_git') + '/chromium/deps/icu.git' + '@' + 'dbd3825b31041d782c5b504c59dcfb5ac7dda08c',
 
   'src/third_party/icu4j': {
       'packages': [
@@ -1293,7 +1293,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '1874a9fbecdd141b3cd65a9fa442618edf196c45',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '498ba7042fc118315bf4242988fd1befb4026985',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1561,7 +1561,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@1b6b54b82d868ac00c5eaa2d4d821a5abb80123f',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e75a355406dc899990a31228a33f5e379254c5e1',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/app_list/app_list_presenter_delegate_impl.cc b/ash/app_list/app_list_presenter_delegate_impl.cc
index b57bddea..f385b18 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.cc
+++ b/ash/app_list/app_list_presenter_delegate_impl.cc
@@ -65,6 +65,7 @@
     case SHELF_BACKGROUND_HOME_LAUNCHER:
     case SHELF_BACKGROUND_LOGIN:
     case SHELF_BACKGROUND_LOGIN_NONBLURRED_WALLPAPER:
+    case SHELF_BACKGROUND_IN_APP:
       return false;
   }
 }
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc
index 1f233af..aa587b1 100644
--- a/ash/app_list/app_list_presenter_delegate_unittest.cc
+++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -910,10 +910,10 @@
   EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_HOME_LAUNCHER,
             shelf_layout_manager->GetShelfBackgroundType());
 
-  // Add a window. It should be maximized because it is in tablet mode.
+  // Add a window. It should be in-app because it is in tablet mode.
   auto window = CreateTestWindow();
   wm::ActivateWindow(window.get());
-  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_MAXIMIZED,
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_IN_APP,
             shelf_layout_manager->GetShelfBackgroundType());
 }
 
diff --git a/ash/home_screen/home_launcher_gesture_handler_unittest.cc b/ash/home_screen/home_launcher_gesture_handler_unittest.cc
index 7a40dda0..cd2b428e 100644
--- a/ash/home_screen/home_launcher_gesture_handler_unittest.cc
+++ b/ash/home_screen/home_launcher_gesture_handler_unittest.cc
@@ -79,6 +79,30 @@
   DISALLOW_COPY_AND_ASSIGN(HomeLauncherGestureHandlerTest);
 };
 
+// Records ShelfBackgroundType changes.
+class ShelfBackgroundTypeWatcher : public ShelfObserver {
+ public:
+  ShelfBackgroundTypeWatcher(Shelf* shelf) : shelf_(shelf) {
+    shelf_->AddObserver(this);
+  }
+  ~ShelfBackgroundTypeWatcher() override { shelf_->RemoveObserver(this); }
+
+  void OnBackgroundTypeChanged(ShelfBackgroundType background_type,
+                               AnimationChangeType change_type) override {
+    background_changes_.push_back(background_type);
+  }
+
+  void CheckEqual(std::vector<ShelfBackgroundType> background_changes) {
+    EXPECT_EQ(background_changes_, background_changes);
+  }
+
+  void Reset() { background_changes_.clear(); }
+
+ private:
+  Shelf* shelf_;
+  std::vector<ShelfBackgroundType> background_changes_;
+};
+
 // Tests that the gesture handler will not have a window to act on if there are
 // none in the mru list.
 TEST_F(HomeLauncherGestureHandlerTest, NeedsOneWindowToShow) {
@@ -389,18 +413,20 @@
 }
 
 // Tests that the shelf background is transparent when the home launcher is
-// dragged, and does not shift to opaque until the home launcher is completely
-// hidden from view and the finger is released.
-TEST_F(HomeLauncherGestureHandlerTest, TransparentShelfWileDragging) {
+// dragged, and shift to opaque when the in-app shelf is starting to show until
+// finger is released.
+TEST_F(HomeLauncherGestureHandlerTest, TransparentToOpaqueShelfWileDragging) {
   UpdateDisplay("400x456");
 
   auto window = CreateWindowForTesting();
   ASSERT_TRUE(window->IsVisible());
-  ASSERT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_MAXIMIZED,
+  ASSERT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_IN_APP,
             AshTestBase::GetPrimaryShelf()
                 ->shelf_layout_manager()
                 ->GetShelfBackgroundType());
 
+  ShelfBackgroundTypeWatcher watcher(AshTestBase::GetPrimaryShelf());
+
   // Begin to show the home launcher, the shelf should become transparent.
   DoPress(Mode::kSlideUpToShow);
   EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_HOME_LAUNCHER,
@@ -417,6 +443,11 @@
             AshTestBase::GetPrimaryShelf()
                 ->shelf_layout_manager()
                 ->GetShelfBackgroundType());
+  watcher.CheckEqual({ShelfBackgroundType::SHELF_BACKGROUND_HOME_LAUNCHER});
+
+  // Reset the watcher queue to start recording the changes during the slide
+  // down.
+  watcher.Reset();
 
   // Begin to hide the home launcher, the background should still be
   // transparent.
@@ -426,22 +457,31 @@
                 ->shelf_layout_manager()
                 ->GetShelfBackgroundType());
 
-  // Fling down to hide the home launcher, the shelf should still be
-  // transparent.
+  // Fling down to hide the home launcher before the middle of the screen, the
+  // shelf should still be transparent.
   GetGestureHandler()->OnScrollEvent(gfx::PointF(0.f, 100.f), 0.f, -10.f);
   EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_HOME_LAUNCHER,
             AshTestBase::GetPrimaryShelf()
                 ->shelf_layout_manager()
                 ->GetShelfBackgroundType());
 
-  // The shelf should transition to opauqe when the gesture sequence has
-  // completed.
-  GetGestureHandler()->OnReleaseEvent(gfx::PointF(0.f, 300.f),
-                                      /*velocity_y=*/base::nullopt);
-  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_MAXIMIZED,
+  // Continue the fling down after the middle of the screen, the shelf
+  // background should now be the in-app shelf.
+  GetGestureHandler()->OnScrollEvent(gfx::PointF(0.f, 250.f), 0.f, -10.f);
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_IN_APP,
             AshTestBase::GetPrimaryShelf()
                 ->shelf_layout_manager()
                 ->GetShelfBackgroundType());
+
+  // The shelf background should be the in-app shelf when the gesture sequence
+  // has completed.
+  GetGestureHandler()->OnReleaseEvent(gfx::PointF(0.f, 300.f),
+                                      /*velocity_y=*/base::nullopt);
+  EXPECT_EQ(ShelfBackgroundType::SHELF_BACKGROUND_IN_APP,
+            AshTestBase::GetPrimaryShelf()
+                ->shelf_layout_manager()
+                ->GetShelfBackgroundType());
+  watcher.CheckEqual({ShelfBackgroundType::SHELF_BACKGROUND_IN_APP});
 }
 
 class HomeLauncherModeGestureHandlerTest
diff --git a/ash/login/ui/parent_access_view.cc b/ash/login/ui/parent_access_view.cc
index baaed57..1cbc3dd 100644
--- a/ash/login/ui/parent_access_view.cc
+++ b/ash/login/ui/parent_access_view.cc
@@ -19,6 +19,7 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/style/ash_color_provider.h"
 #include "ash/wallpaper/wallpaper_controller_impl.h"
 #include "base/bind.h"
 #include "base/logging.h"
@@ -39,6 +40,7 @@
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_analysis.h"
 #include "ui/gfx/color_palette.h"
+#include "ui/gfx/color_utils.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_list.h"
 #include "ui/gfx/geometry/size.h"
@@ -91,8 +93,11 @@
 constexpr int kArrowButtonSizeDp = 40;
 constexpr int kArrowSizeDp = 20;
 
+constexpr int kAlpha70Percent = 178;
+constexpr int kAlpha74Percent = 189;
+
 constexpr SkColor kTextColor = SK_ColorWHITE;
-constexpr SkColor kErrorColor = SkColorSetARGB(0xFF, 0xF2, 0x8B, 0x82);
+constexpr SkColor kErrorColor = gfx::kGoogleRed300;
 constexpr SkColor kArrowButtonColor = SkColorSetARGB(0x2B, 0xFF, 0xFF, 0xFF);
 
 bool IsTabletMode() {
@@ -110,6 +115,28 @@
                                   : kParentAccessViewHeightDp);
 }
 
+// Returns background color for parent access dialog. |using_blur| should be
+// true if the dialog is using background blur (color transparency depends on
+// it).
+SkColor GetBackgroundColor(bool using_blur) {
+  SkColor color = AshColorProvider::Get()->GetBaseLayerColor(
+      AshColorProvider::BaseLayerType::kOpaque,
+      AshColorProvider::AshColorMode::kDark);
+
+  SkColor extracted_color =
+      Shell::Get()->wallpaper_controller()->GetProminentColor(
+          color_utils::ColorProfile(color_utils::LumaRange::DARK,
+                                    color_utils::SaturationRange::MUTED));
+
+  if (extracted_color != kInvalidWallpaperColor &&
+      extracted_color != SK_ColorTRANSPARENT) {
+    color = color_utils::GetResultingPaintColor(
+        SkColorSetA(SK_ColorBLACK, kAlpha70Percent), extracted_color);
+  }
+
+  return using_blur ? SkColorSetA(color, kAlpha74Percent) : color;
+}
+
 base::string16 GetTitle(ParentAccessRequestReason reason) {
   int title_id;
   switch (reason) {
@@ -555,7 +582,9 @@
     : callbacks_(callbacks),
       account_id_(account_id),
       request_reason_(reason),
-      validation_time_(validation_time) {
+      validation_time_(validation_time),
+      blur_background_(Shell::Get()->session_controller()->GetSessionState() ==
+                       session_manager::SessionState::ACTIVE) {
   DCHECK(callbacks.on_finished);
   // Main view contains all other views aligned vertically and centered.
   auto layout = std::make_unique<views::BoxLayout>(
@@ -571,6 +600,10 @@
   SetPreferredSize(GetParentAccessViewSize());
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
+  layer()->SetRoundedCornerRadius(
+      gfx::RoundedCornersF(kParentAccessViewRoundedCornerRadiusDp));
+  if (blur_background_)
+    layer()->SetBackgroundBlur(ShelfConfig::Get()->shelf_blur_radius());
 
   const int child_view_width =
       kParentAccessViewWidthDp - 2 * kParentAccessViewHorizontalInsetDp;
@@ -727,22 +760,9 @@
 void ParentAccessView::OnPaint(gfx::Canvas* canvas) {
   views::View::OnPaint(canvas);
 
-  SkColor color = gfx::kGoogleGrey900;
-  if (Shell::Get()->session_controller()->GetSessionState() !=
-      session_manager::SessionState::ACTIVE) {
-    SkColor extracted_color =
-        Shell::Get()->wallpaper_controller()->GetProminentColor(
-            color_utils::ColorProfile(color_utils::LumaRange::NORMAL,
-                                      color_utils::SaturationRange::MUTED));
-    if (extracted_color != kInvalidWallpaperColor &&
-        extracted_color != SK_ColorTRANSPARENT) {
-      color = extracted_color;
-    }
-  }
-
   cc::PaintFlags flags;
   flags.setStyle(cc::PaintFlags::kFill_Style);
-  flags.setColor(color);
+  flags.setColor(GetBackgroundColor(blur_background_));
   canvas->DrawRoundRect(GetContentsBounds(),
                         kParentAccessViewRoundedCornerRadiusDp, flags);
 }
diff --git a/ash/login/ui/parent_access_view.h b/ash/login/ui/parent_access_view.h
index 2028d17..5fceb6d 100644
--- a/ash/login/ui/parent_access_view.h
+++ b/ash/login/ui/parent_access_view.h
@@ -187,6 +187,9 @@
   // Auto submit code when the last input has been inserted.
   bool auto_submit_enabled_ = true;
 
+  // Whether view background should be blurred.
+  bool blur_background_ = false;
+
   views::Label* title_label_ = nullptr;
   views::Label* description_label_ = nullptr;
   AccessCodeInput* access_code_view_ = nullptr;
diff --git a/ash/public/cpp/shelf_types.h b/ash/public/cpp/shelf_types.h
index 246a028..d83b8ea 100644
--- a/ash/public/cpp/shelf_types.h
+++ b/ash/public/cpp/shelf_types.h
@@ -90,6 +90,9 @@
 
   // The background when overview is active.
   SHELF_BACKGROUND_OVERVIEW,
+
+  // The background for the in-app shelf in tablet mode.
+  SHELF_BACKGROUND_IN_APP,
 };
 
 // Source of the launch or activation request, for tracking.
diff --git a/ash/shelf/hotseat_transition_animator.cc b/ash/shelf/hotseat_transition_animator.cc
index 93e7288e..177328cf 100644
--- a/ash/shelf/hotseat_transition_animator.cc
+++ b/ash/shelf/hotseat_transition_animator.cc
@@ -10,6 +10,7 @@
 #include "ash/shelf/shelf_widget.h"
 #include "ash/shell.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
+#include "base/metrics/histogram_macros.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
@@ -19,8 +20,40 @@
 
 namespace ash {
 
+class HotseatTransitionAnimator::TransitionAnimationMetricsReporter
+    : public ui::AnimationMetricsReporter {
+ public:
+  TransitionAnimationMetricsReporter() {}
+
+  ~TransitionAnimationMetricsReporter() override = default;
+
+  void animating_to_shown_hotseat(bool animating_to_shown_hotseat) {
+    animating_to_shown_hotseat_ = animating_to_shown_hotseat;
+  }
+
+  // ui::AnimationMetricsReporter:
+  void Report(int value) override {
+    if (animating_to_shown_hotseat_) {
+      UMA_HISTOGRAM_PERCENTAGE(
+          "Ash.HotseatTransition.AnimationSmoothness.TransitionToShownHotseat",
+          value);
+    } else {
+      UMA_HISTOGRAM_PERCENTAGE(
+          "Ash.HotseatTransition.AnimationSmoothness."
+          "TransitionFromShownHotseat",
+          value);
+    }
+  }
+
+ private:
+  // Whether the animation reported is transitioning state into a shown hotseat.
+  bool animating_to_shown_hotseat_ = false;
+};
+
 HotseatTransitionAnimator::HotseatTransitionAnimator(ShelfWidget* shelf_widget)
-    : shelf_widget_(shelf_widget) {
+    : shelf_widget_(shelf_widget),
+      animation_metrics_reporter_(
+          std::make_unique<TransitionAnimationMetricsReporter>()) {
   Shell::Get()->tablet_mode_controller()->AddObserver(this);
 }
 
@@ -120,6 +153,8 @@
                                : shelf_widget_->hotseat_widget()
                                      ->GetOpaqueBackground()
                                      ->rounded_corner_radii());
+  animation_metrics_reporter_->animating_to_shown_hotseat(new_state ==
+                                                          HotseatState::kShown);
 }
 
 void HotseatTransitionAnimator::StartAnimation(HotseatState old_state,
@@ -132,6 +167,8 @@
   shelf_bg_animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
   shelf_bg_animation_setter.SetPreemptionStrategy(
       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+  shelf_bg_animation_setter.SetAnimationMetricsReporter(
+      animation_metrics_reporter_.get());
   animation_complete_callback_ = base::BindOnce(
       &HotseatTransitionAnimator::NotifyHotseatTransitionAnimationEnded,
       weak_ptr_factory_.GetWeakPtr(), old_state, new_state);
diff --git a/ash/shelf/hotseat_transition_animator.h b/ash/shelf/hotseat_transition_animator.h
index eb698b5f..56b7074 100644
--- a/ash/shelf/hotseat_transition_animator.h
+++ b/ash/shelf/hotseat_transition_animator.h
@@ -49,6 +49,8 @@
   void OnTabletModeEnded() override;
 
  private:
+  class TransitionAnimationMetricsReporter;
+
   // Starts the animation between |old_state_| and |target_state_|.
   void DoAnimation(HotseatState old_state, HotseatState new_state);
 
@@ -79,6 +81,10 @@
 
   base::ObserverList<Observer> observers_;
 
+  // Metric reporter for hotseat transitions.
+  const std::unique_ptr<TransitionAnimationMetricsReporter>
+      animation_metrics_reporter_;
+
   base::WeakPtrFactory<HotseatTransitionAnimator> weak_ptr_factory_{this};
 };
 
diff --git a/ash/shelf/shelf_background_animator.cc b/ash/shelf/shelf_background_animator.cc
index cccb3460..0af3ab1 100644
--- a/ash/shelf/shelf_background_animator.cc
+++ b/ash/shelf/shelf_background_animator.cc
@@ -184,6 +184,7 @@
     case SHELF_BACKGROUND_LOGIN:
     case SHELF_BACKGROUND_LOGIN_NONBLURRED_WALLPAPER:
     case SHELF_BACKGROUND_OVERVIEW:
+    case SHELF_BACKGROUND_IN_APP:
       duration = base::TimeDelta::FromMilliseconds(250);
       break;
   }
@@ -222,6 +223,7 @@
       break;
     case SHELF_BACKGROUND_MAXIMIZED:
     case SHELF_BACKGROUND_OVERVIEW:
+    case SHELF_BACKGROUND_IN_APP:
       shelf_target_color = ShelfConfig::Get()->GetMaximizedShelfColor();
       break;
     case SHELF_BACKGROUND_OOBE:
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index ed2145c..11b5854 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -751,12 +751,26 @@
       Shell::Get()->app_list_controller() &&
       Shell::Get()->app_list_controller()->IsVisible();
   if (IsTabletModeEnabled()) {
-    // If the home launcher is shown, being animated, or dragged, show the
-    // home launcher background.
-    if (app_list_is_visible ||
-        Shell::Get()->app_list_controller()->home_launcher_transition_state() !=
-            AppListControllerImpl::HomeLauncherTransitionState::kFinished)
+    if (app_list_is_visible) {
+      // If the home launcher is shown or mostly shown, show the home launcher
+      // background. If it is mostly hidden, show the in-app background.
+      if (Shell::Get()
+              ->app_list_controller()
+              ->home_launcher_transition_state() ==
+          AppListControllerImpl::HomeLauncherTransitionState::kMostlyHidden) {
+        return SHELF_BACKGROUND_IN_APP;
+      }
       return SHELF_BACKGROUND_HOME_LAUNCHER;
+    } else if (Shell::Get()
+                   ->app_list_controller()
+                   ->home_launcher_transition_state() !=
+               AppListControllerImpl::HomeLauncherTransitionState::kFinished) {
+      return SHELF_BACKGROUND_HOME_LAUNCHER;
+    } else if (maximized) {
+      // If the home launcher is not shown but it is maximized, show the
+      // in-app shelf.
+      return SHELF_BACKGROUND_IN_APP;
+    }
   } else if (app_list_is_visible) {
     return maximized ? SHELF_BACKGROUND_MAXIMIZED_WITH_APP_LIST
                      : SHELF_BACKGROUND_APP_LIST;
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 64a724e..4fec9db5 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -2604,7 +2604,7 @@
   // |window| should be maximized, and the shelf background should match the
   // maximized state.
   EXPECT_EQ(WorkspaceWindowState::kMaximized, GetWorkspaceWindowState());
-  EXPECT_EQ(SHELF_BACKGROUND_MAXIMIZED, GetShelfWidget()->GetBackgroundType());
+  EXPECT_EQ(SHELF_BACKGROUND_IN_APP, GetShelfWidget()->GetBackgroundType());
 }
 
 // Verify that the auto-hide shelf has default background by default and has
diff --git a/ash/wm/lock_action_handler_layout_manager_unittest.cc b/ash/wm/lock_action_handler_layout_manager_unittest.cc
index 85863220..a666d94 100644
--- a/ash/wm/lock_action_handler_layout_manager_unittest.cc
+++ b/ash/wm/lock_action_handler_layout_manager_unittest.cc
@@ -80,7 +80,7 @@
     base::CommandLine::ForCurrentProcess()->AppendSwitch(
         keyboard::switches::kEnableVirtualKeyboard);
 
-    action_background_controller_factory_ = base::Bind(
+    action_background_controller_factory_ = base::BindRepeating(
         &LockActionHandlerLayoutManagerTest::CreateActionBackgroundController,
         base::Unretained(this));
     LockScreenActionBackgroundController::SetFactoryCallbackForTesting(
diff --git a/ash/wm/lock_state_controller.cc b/ash/wm/lock_state_controller.cc
index cf9e05ce..26af6145d 100644
--- a/ash/wm/lock_state_controller.cc
+++ b/ash/wm/lock_state_controller.cc
@@ -145,11 +145,11 @@
   VLOG(1) << "CancelLockAnimation";
   animating_lock_ = false;
   Shell::Get()->wallpaper_controller()->UpdateWallpaperBlur(false);
-  base::Closure next_animation_starter =
-      base::Bind(&LockStateController::LockAnimationCancelled,
-                 weak_ptr_factory_.GetWeakPtr());
+  base::OnceClosure next_animation_starter =
+      base::BindOnce(&LockStateController::LockAnimationCancelled,
+                     weak_ptr_factory_.GetWeakPtr());
   SessionStateAnimator::AnimationSequence* animation_sequence =
-      animator_->BeginAnimationSequence(next_animation_starter);
+      animator_->BeginAnimationSequence(std::move(next_animation_starter));
 
   animation_sequence->StartAnimation(
       SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
@@ -314,11 +314,11 @@
     SessionStateAnimator::AnimationSpeed speed,
     bool request_lock_on_completion) {
   Shell::Get()->wallpaper_controller()->UpdateWallpaperBlur(true);
-  base::Closure next_animation_starter =
-      base::Bind(&LockStateController::PreLockAnimationFinished,
-                 weak_ptr_factory_.GetWeakPtr(), request_lock_on_completion);
+  base::OnceClosure next_animation_starter = base::BindOnce(
+      &LockStateController::PreLockAnimationFinished,
+      weak_ptr_factory_.GetWeakPtr(), request_lock_on_completion);
   SessionStateAnimator::AnimationSequence* animation_sequence =
-      animator_->BeginAnimationSequence(next_animation_starter);
+      animator_->BeginAnimationSequence(std::move(next_animation_starter));
 
   animation_sequence->StartAnimation(
       SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
@@ -337,11 +337,11 @@
 
 void LockStateController::StartPostLockAnimation() {
   VLOG(1) << "StartPostLockAnimation";
-  base::Closure next_animation_starter =
-      base::Bind(&LockStateController::PostLockAnimationFinished,
-                 weak_ptr_factory_.GetWeakPtr());
+  base::OnceClosure next_animation_starter =
+      base::BindOnce(&LockStateController::PostLockAnimationFinished,
+                     weak_ptr_factory_.GetWeakPtr());
   SessionStateAnimator::AnimationSequence* animation_sequence =
-      animator_->BeginAnimationSequence(next_animation_starter);
+      animator_->BeginAnimationSequence(std::move(next_animation_starter));
 
   animation_sequence->StartAnimation(
       SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
@@ -375,11 +375,11 @@
 
 void LockStateController::StartUnlockAnimationAfterUIDestroyed() {
   VLOG(1) << "StartUnlockAnimationAfterUIDestroyed";
-  base::Closure next_animation_starter =
-      base::Bind(&LockStateController::UnlockAnimationAfterUIDestroyedFinished,
-                 weak_ptr_factory_.GetWeakPtr());
+  base::OnceClosure next_animation_starter = base::BindOnce(
+      &LockStateController::UnlockAnimationAfterUIDestroyedFinished,
+      weak_ptr_factory_.GetWeakPtr());
   SessionStateAnimator::AnimationSequence* animation_sequence =
-      animator_->BeginAnimationSequence(next_animation_starter);
+      animator_->BeginAnimationSequence(std::move(next_animation_starter));
 
   animation_sequence->StartAnimation(
       SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
diff --git a/ash/wm/lock_state_controller_unittest.cc b/ash/wm/lock_state_controller_unittest.cc
index 1331d36..fd92942 100644
--- a/ash/wm/lock_state_controller_unittest.cc
+++ b/ash/wm/lock_state_controller_unittest.cc
@@ -287,8 +287,8 @@
   }
 
   void SuccessfulAuthentication(bool* call_flag) {
-    base::Closure closure = base::Bind(&CheckCalledCallback, call_flag);
-    lock_state_controller_->OnLockScreenHide(closure);
+    base::OnceClosure closure = base::BindOnce(&CheckCalledCallback, call_flag);
+    lock_state_controller_->OnLockScreenHide(std::move(closure));
   }
 
   std::unique_ptr<ShutdownController::ScopedResetterForTest>
diff --git a/ash/wm/session_state_animator_impl.cc b/ash/wm/session_state_animator_impl.cc
index be81737..d0289674 100644
--- a/ash/wm/session_state_animator_impl.cc
+++ b/ash/wm/session_state_animator_impl.cc
@@ -289,27 +289,27 @@
 // finished. It is used in when undoing shutdown animation.
 class CallbackAnimationObserver : public ui::LayerAnimationObserver {
  public:
-  explicit CallbackAnimationObserver(base::Closure callback)
-      : callback_(callback) {}
+  explicit CallbackAnimationObserver(base::OnceClosure callback)
+      : callback_(std::move(callback)) {}
   ~CallbackAnimationObserver() override = default;
 
  private:
   // Overridden from ui::LayerAnimationObserver:
   void OnLayerAnimationEnded(ui::LayerAnimationSequence* seq) override {
     // Drop foreground once animation is over.
-    callback_.Run();
+    std::move(callback_).Run();
     delete this;
   }
 
   void OnLayerAnimationAborted(ui::LayerAnimationSequence* seq) override {
     // Drop foreground once animation is over.
-    callback_.Run();
+    std::move(callback_).Run();
     delete this;
   }
 
   void OnLayerAnimationScheduled(ui::LayerAnimationSequence* seq) override {}
 
-  base::Closure callback_;
+  base::OnceClosure callback_;
 
   DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
 };
@@ -550,7 +550,7 @@
     base::OnceClosure callback) {
   aura::Window::Windows containers;
   GetContainers(container_mask, &containers);
-  base::Closure animation_done_closure =
+  base::RepeatingClosure animation_done_closure =
       base::BarrierClosure(containers.size(), std::move(callback));
   for (aura::Window::Windows::const_iterator it = containers.begin();
        it != containers.end(); ++it) {
diff --git a/ash/wm/session_state_animator_impl_unittest.cc b/ash/wm/session_state_animator_impl_unittest.cc
index fa7ebc1..ec8766d9 100644
--- a/ash/wm/session_state_animator_impl_unittest.cc
+++ b/ash/wm/session_state_animator_impl_unittest.cc
@@ -96,7 +96,7 @@
       SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
       SessionStateAnimator::ANIMATION_LIFT,
       SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE,
-      base::Bind([](int* count) { ++(*count); }, &callback_count));
+      base::BindOnce([](int* count) { ++(*count); }, &callback_count));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, callback_count);
 }
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index e27f120..2b546904 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -816,7 +816,7 @@
                                window_state->drag_details()->bounds_change ==
                                    WindowResizer::kBoundsChange_Repositions;
   if (is_window_moved) {
-    EndSplitView();
+    // Ending overview will also end clamshell split view.
     Shell::Get()->overview_controller()->EndOverview();
     return;
   }
@@ -856,6 +856,7 @@
   // we'll just end splitview and overview mode.
   if (WindowState::Get(window)->drag_details()->window_component !=
       GetWindowComponentForResize(window)) {
+    // Ending overview will also end clamshell split view.
     Shell::Get()->overview_controller()->EndOverview();
   }
 }
@@ -866,7 +867,7 @@
 
   if (divider_position_ < GetDividerEndPosition() * kOneThirdPositionRatio ||
       divider_position_ > GetDividerEndPosition() * kTwoThirdPositionRatio) {
-    EndSplitView();
+    // Ending overview will also end clamshell split view.
     Shell::Get()->overview_controller()->EndOverview();
     WindowState::Get(window)->Maximize();
   }
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 714d840..a95cd9e 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -393,10 +393,10 @@
   }
 
   destroy_observer_ = std::make_unique<DestroyObserver>(
-      window, base::Bind(&TabletModeController::StopObservingAnimation,
-                         weak_factory_.GetWeakPtr(),
-                         /*record_stats=*/false,
-                         /*delete_screenshot=*/true));
+      window, base::BindOnce(&TabletModeController::StopObservingAnimation,
+                             weak_factory_.GetWeakPtr(),
+                             /*record_stats=*/false,
+                             /*delete_screenshot=*/true));
   animating_layer_ = window->layer();
   animating_layer_->GetAnimator()->AddObserver(this);
 }
@@ -996,8 +996,8 @@
 void TabletModeController::TakeScreenshot(aura::Window* top_window) {
   DCHECK(!top_window->IsRootWindow());
   destroy_observer_ = std::make_unique<DestroyObserver>(
-      top_window, base::Bind(&TabletModeController::ResetDestroyObserver,
-                             weak_factory_.GetWeakPtr()));
+      top_window, base::BindOnce(&TabletModeController::ResetDestroyObserver,
+                                 weak_factory_.GetWeakPtr()));
   screenshot_set_callback_.Reset(base::BindOnce(
       &TabletModeController::FinishInitTabletMode, weak_factory_.GetWeakPtr()));
 
diff --git a/ash/wm/test_session_state_animator.cc b/ash/wm/test_session_state_animator.cc
index 20edf41..6131aae2 100644
--- a/ash/wm/test_session_state_animator.cc
+++ b/ash/wm/test_session_state_animator.cc
@@ -9,16 +9,11 @@
 
 #include "base/barrier_closure.h"
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/stl_util.h"
 
 namespace ash {
 
-namespace {
-// A no-op callback that can be used when managing an animation that didn't
-// actually have a callback given.
-void DummyCallback() {}
-}  // namespace
-
 const SessionStateAnimator::Container
     TestSessionStateAnimator::kAllContainers[] = {
         SessionStateAnimator::WALLPAPER,
@@ -85,18 +80,22 @@
     SessionStateAnimator::Container container,
     AnimationType type,
     AnimationSpeed speed,
-    base::Closure success_callback,
-    base::Closure failed_callback)
+    base::OnceClosure success_callback,
+    base::OnceClosure failed_callback)
     : animation_epoch(animation_epoch),
       remaining_duration(duration),
       container(container),
       type(type),
       speed(speed),
-      success_callback(success_callback),
-      failed_callback(failed_callback) {}
+      success_callback(std::move(success_callback)),
+      failed_callback(std::move(failed_callback)) {}
 
 TestSessionStateAnimator::ActiveAnimation::ActiveAnimation(
-    const ActiveAnimation& other) = default;
+    ActiveAnimation&& other) = default;
+
+TestSessionStateAnimator::ActiveAnimation&
+TestSessionStateAnimator::ActiveAnimation::operator=(ActiveAnimation&& other) =
+    default;
 
 TestSessionStateAnimator::ActiveAnimation::~ActiveAnimation() = default;
 
@@ -121,7 +120,7 @@
       ActiveAnimation& active_animation = *animation_iter;
       active_animation.remaining_duration -= duration;
       if (active_animation.remaining_duration <= base::TimeDelta()) {
-        active_animation.success_callback.Run();
+        std::move(active_animation.success_callback).Run();
         animation_iter = (*container_iter).second.erase(animation_iter);
       } else {
         ++animation_iter;
@@ -137,12 +136,12 @@
        container_iter != active_animations_.end(); ++container_iter) {
     AnimationList::iterator animation_iter = (*container_iter).second.begin();
     while (animation_iter != (*container_iter).second.end()) {
-      ActiveAnimation active_animation = *animation_iter;
+      ActiveAnimation& active_animation = *animation_iter;
       if (active_animation.animation_epoch <= animation_epoch) {
         if (completed_successfully)
-          active_animation.success_callback.Run();
+          std::move(active_animation.success_callback).Run();
         else
-          active_animation.failed_callback.Run();
+          std::move(active_animation.failed_callback).Run();
         animation_iter = (*container_iter).second.erase(animation_iter);
       } else {
         ++animation_iter;
@@ -201,10 +200,8 @@
   ++last_animation_epoch_;
   for (size_t i = 0; i < base::size(kAllContainers); ++i) {
     if (container_mask & kAllContainers[i]) {
-      // Use a dummy no-op callback because one isn't required by the client
-      // but one is required when completing or aborting animations.
-      base::Closure callback = base::Bind(&DummyCallback);
-      AddAnimation(kAllContainers[i], type, speed, callback, callback);
+      AddAnimation(kAllContainers[i], type, speed, base::DoNothing(),
+                   base::DoNothing());
     }
   }
 }
@@ -259,15 +256,15 @@
   ++last_animation_epoch_;
   for (size_t i = 0; i < base::size(kAllContainers); ++i) {
     if (container_mask & kAllContainers[i]) {
-      base::Closure success_callback =
-          base::Bind(&AnimationSequence::SequenceFinished,
-                     base::Unretained(animation_sequence), true);
-      base::Closure failed_callback =
-          base::Bind(&AnimationSequence::SequenceFinished,
-                     base::Unretained(animation_sequence), false);
+      base::OnceClosure success_callback =
+          base::BindOnce(&AnimationSequence::SequenceFinished,
+                         base::Unretained(animation_sequence), true);
+      base::OnceClosure failed_callback =
+          base::BindOnce(&AnimationSequence::SequenceFinished,
+                         base::Unretained(animation_sequence), false);
       animation_sequence->SequenceAttached();
-      AddAnimation(kAllContainers[i], type, speed, success_callback,
-                   failed_callback);
+      AddAnimation(kAllContainers[i], type, speed, std::move(success_callback),
+                   std::move(failed_callback));
     }
   }
 }
@@ -276,16 +273,16 @@
     SessionStateAnimator::Container container,
     AnimationType type,
     AnimationSpeed speed,
-    base::Closure success_callback,
-    base::Closure failed_callback) {
+    base::OnceClosure success_callback,
+    base::OnceClosure failed_callback) {
   base::TimeDelta duration = GetDuration(speed);
   ActiveAnimation active_animation(last_animation_epoch_, duration, container,
-                                   type, speed, success_callback,
-                                   failed_callback);
+                                   type, speed, std::move(success_callback),
+                                   std::move(failed_callback));
   // This test double is limited to only have one animation active for a given
   // container at a time.
   AbortAnimation(container);
-  active_animations_[container].push_back(active_animation);
+  active_animations_[container].push_back(std::move(active_animation));
 }
 
 void TestSessionStateAnimator::AbortAnimation(
@@ -295,8 +292,8 @@
   if (container_iter != active_animations_.end()) {
     AnimationList::iterator animation_iter = (*container_iter).second.begin();
     while (animation_iter != (*container_iter).second.end()) {
-      ActiveAnimation active_animation = *animation_iter;
-      active_animation.failed_callback.Run();
+      ActiveAnimation& active_animation = *animation_iter;
+      std::move(active_animation.failed_callback).Run();
       animation_iter = (*container_iter).second.erase(animation_iter);
     }
   }
diff --git a/ash/wm/test_session_state_animator.h b/ash/wm/test_session_state_animator.h
index eedd61f..fb7b132e 100644
--- a/ash/wm/test_session_state_animator.h
+++ b/ash/wm/test_session_state_animator.h
@@ -88,9 +88,10 @@
                     SessionStateAnimator::Container container,
                     AnimationType type,
                     AnimationSpeed speed,
-                    base::Closure success_callback,
-                    base::Closure failed_callback);
-    ActiveAnimation(const ActiveAnimation& other);
+                    base::OnceClosure success_callback,
+                    base::OnceClosure failed_callback);
+    ActiveAnimation(ActiveAnimation&& other);
+    ActiveAnimation& operator=(ActiveAnimation&& other);
     virtual ~ActiveAnimation();
 
     // The time epoch that this animation was scheduled.
@@ -109,10 +110,10 @@
     AnimationSpeed speed;
 
     // The callback to be invoked upon a successful completion.
-    base::Closure success_callback;
+    base::OnceClosure success_callback;
 
     // The callback to be invoked upon an unsuccessful completion.
-    base::Closure failed_callback;
+    base::OnceClosure failed_callback;
   };
 
   typedef std::vector<ActiveAnimation> AnimationList;
@@ -133,8 +134,8 @@
   void AddAnimation(SessionStateAnimator::Container container,
                     AnimationType type,
                     AnimationSpeed speed,
-                    base::Closure success_callback,
-                    base::Closure failed_callback);
+                    base::OnceClosure success_callback,
+                    base::OnceClosure failed_callback);
 
   // If an animation is currently active for the given |container| it will be
   // aborted by invoking OnAnimationAborted and removed from the list of active
diff --git a/ash/wm/window_cycle_event_filter.cc b/ash/wm/window_cycle_event_filter.cc
index b31b82c..609967a 100644
--- a/ash/wm/window_cycle_event_filter.cc
+++ b/ash/wm/window_cycle_event_filter.cc
@@ -44,10 +44,11 @@
                !repeat_timer_.IsRunning()) {
       repeat_timer_.Start(
           FROM_HERE, base::TimeDelta::FromMilliseconds(180),
-          base::Bind(&WindowCycleController::HandleCycleWindow,
-                     base::Unretained(Shell::Get()->window_cycle_controller()),
-                     event->IsShiftDown() ? WindowCycleController::BACKWARD
-                                          : WindowCycleController::FORWARD));
+          base::BindRepeating(
+              &WindowCycleController::HandleCycleWindow,
+              base::Unretained(Shell::Get()->window_cycle_controller()),
+              event->IsShiftDown() ? WindowCycleController::BACKWARD
+                                   : WindowCycleController::FORWARD));
     }
   } else if (event->key_code() == ui::VKEY_ESCAPE) {
     Shell::Get()->window_cycle_controller()->CancelCycling();
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index eda4a8d5..102391a 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -1503,13 +1503,22 @@
   return os.sep.join(script_components[base_index:])
 
 
+def _RemoveExistingHeaders(path):
+  if os.path.exists(path) and os.path.isdir(path):
+    for root, _, files in os.walk(path):
+      for f in files:
+        file_path = os.path.join(root, f)
+        if os.path.isfile(file_path) and os.path.splitext(file_path)[1] == '.h':
+          os.remove(file_path)
+
+
 def main():
-  usage = """usage: %prog [OPTIONS]
+  description = """
 This script will parse the given java source code extracting the native
 declarations and print the header file to stdout (or a file).
 See SampleForTests.java for more details.
   """
-  parser = argparse.ArgumentParser(usage=usage)
+  parser = argparse.ArgumentParser(description=description)
 
   parser.add_argument(
       '-j',
@@ -1576,9 +1585,19 @@
   args = parser.parse_args()
   input_files = args.input_files
   output_files = args.output_files
-  if not output_files:
+  if output_files:
+    output_dirs = set(os.path.dirname(f) for f in output_files)
+    if len(output_dirs) != 1:
+      parser.error(
+          'jni_generator only supports a single output directory per target '
+          '(got {})'.format(output_dirs))
+    output_dir = output_dirs.pop()
+    # Remove existing headers so that moving .java source files but not updating
+    # the corresponding C++ include will be a compile failure (otherwise
+    # incremental builds will usually not catch this).
+    _RemoveExistingHeaders(output_dir)
+  else:
     output_files = [None] * len(input_files)
-
   temp_dir = tempfile.mkdtemp()
   try:
     if args.jar_file:
diff --git a/base/test/task_environment.cc b/base/test/task_environment.cc
index 393ecb6..6060ca3 100644
--- a/base/test/task_environment.cc
+++ b/base/test/task_environment.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/message_loop/message_pump.h"
 #include "base/message_loop/message_pump_type.h"
+#include "base/no_destructor.h"
 #include "base/run_loop.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
@@ -45,6 +46,12 @@
 
 namespace {
 
+ObserverList<TaskEnvironment::DestructionObserver>& GetDestructionObservers() {
+  static NoDestructor<ObserverList<TaskEnvironment::DestructionObserver>>
+      instance;
+  return *instance;
+}
+
 base::MessagePumpType GetMessagePumpTypeForMainThreadType(
     TaskEnvironment::MainThreadType main_thread_type) {
   switch (main_thread_type) {
@@ -471,6 +478,8 @@
   // If we've been moved then bail out.
   if (!owns_instance_)
     return;
+  for (auto& observer : GetDestructionObservers())
+    observer.WillDestroyCurrentTaskEnvironment();
   DestroyThreadPool();
   task_queue_ = nullptr;
   NotifyDestructionObserversAndReleaseSequenceManager();
@@ -717,6 +726,16 @@
   LOG(INFO) << sequence_manager_->DescribeAllPendingTasks();
 }
 
+// static
+void TaskEnvironment::AddDestructionObserver(DestructionObserver* observer) {
+  GetDestructionObservers().AddObserver(observer);
+}
+
+// static
+void TaskEnvironment::RemoveDestructionObserver(DestructionObserver* observer) {
+  GetDestructionObservers().RemoveObserver(observer);
+}
+
 TaskEnvironment::TestTaskTracker::TestTaskTracker()
     : internal::ThreadPoolImpl::TaskTrackerImpl(std::string()),
       can_run_tasks_cv_(&lock_),
diff --git a/base/test/task_environment.h b/base/test/task_environment.h
index 4ba48525..2a9ad1bc 100644
--- a/base/test/task_environment.h
+++ b/base/test/task_environment.h
@@ -10,6 +10,7 @@
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task/lazy_task_runner.h"
@@ -270,6 +271,23 @@
   // thread.
   void DescribePendingMainThreadTasks() const;
 
+  class DestructionObserver : public CheckedObserver {
+   public:
+    DestructionObserver() = default;
+    ~DestructionObserver() override = default;
+
+    DestructionObserver(const DestructionObserver&) = delete;
+    DestructionObserver& operator=(const DestructionObserver&) = delete;
+
+    virtual void WillDestroyCurrentTaskEnvironment() = 0;
+  };
+
+  // Adds/removes a DestructionObserver to any TaskEnvironment. Observers are
+  // notified when any TaskEnvironment goes out of scope (other than with a move
+  // operation). Must be called on the main thread.
+  static void AddDestructionObserver(DestructionObserver* observer);
+  static void RemoveDestructionObserver(DestructionObserver* observer);
+
  protected:
   explicit TaskEnvironment(TaskEnvironment&& other);
 
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 55caf3443..5deafa2 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -2946,6 +2946,18 @@
       if (defined(invoker.negative_main_dex_globs)) {
         not_needed(invoker, [ "negative_main_dex_globs" ])
       }
+      if (defined(invoker.disable_checkdiscard)) {
+        not_needed(invoker, [ "disable_checkdiscard" ])
+      }
+      if (defined(invoker.disable_r8_outlining)) {
+        not_needed(invoker, [ "disable_r8_outlining" ])
+      }
+      if (defined(invoker.negative_main_dex_globs)) {
+        not_needed(invoker, [ "negative_main_dex_globs" ])
+      }
+      if (defined(invoker.dexlayout_profile)) {
+        not_needed(invoker, [ "dexlayout_profile" ])
+      }
     } else {
       # Dex generation for app bundle modules with proguarding enabled takes
       # place later due to synchronized proguarding. For more details,
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index c47d224..089d766 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-8896099783767243232
\ No newline at end of file
+8896088220762243856
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index e044eb3..3b7b3f15 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8896101111305989936
\ No newline at end of file
+8896096715782434240
\ No newline at end of file
diff --git a/cc/input/README.md b/cc/input/README.md
new file mode 100644
index 0000000..a5337e9
--- /dev/null
+++ b/cc/input/README.md
@@ -0,0 +1,106 @@
+# cc/input
+
+This directory contains code specific to input handling and scrolling in in the
+compositor.
+
+The renderer compositor typically receives, on the compositor thread, all input
+events arriving from the browser. In some cases, the compositor can process
+input without consulting the main thread. We strive for this since it means
+input doesn't have to block on a potentially busy main thread.
+
+If the compositor determines that Blink must be consulted to correctly handle
+the event. e.g. For detailed hit-testing or correct paint output. In these
+cases, the event will be posted to the Blink main thread.
+
+See [InputHandlerProxy](../../ui/events/blink/input_handler_proxy.cc) for the
+entry point to this code.
+
+## Scrolling
+
+### Viewport
+
+Viewport scrolling is special compared to scrolling regular ScrollNodes. The
+main difference is that the viewport is composed of two scrollers: the inner
+and outer scrollers. These correspond to the visual and layout viewports in
+Blink, respectively.
+
+The reason for this composition is pinch-zoom; when a user zooms in, the layout
+viewport remains unchanged (position: fixed elements don't stick to the user's
+screen) and the user can pan the visual viewport within the layout viewport.
+See [this demo](http://bokand.github.io/viewport/index.html) for a visual,
+interactive example.
+
+This arrangement requires some special distribution and bubbling of
+scroll delta. Additionally, viewport scrolling is also responsible for
+overscroll effects like rubber-banding and gestural-navigation as well as URL
+bar movement on Android.
+
+Notably, that the UI compositor as well as renderer compositors for
+out-of-process iframes will not have an inner or an outer viewport scroll node.
+
+#### Scroll Chain Structure
+
+The inner viewport scroll node is always the first and only child of the root
+scroll node; it is the top-level scrollable node in the scroll tree.  The outer
+viewport will typically be the one child of the inner viewport scroll node;
+however, this may be changed on certain pages. This happens when a page is
+given a non-document root scroller. For more information the root
+scroller see the
+[README](../../third_party/blink/renderer/core/page/scrolling/README.md) in
+Blink's core/page/scrolling directory.
+
+#### Scrolling the Viewport
+
+Viewport scroll nodes are typically not scrolled directly, like other scroll
+nodes. Instead, they're scrolled by using the cc::Viewport object. cc::Viewport
+is an object that's lives on the LayerTreeHostImpl and operates on the active
+tree's inner and outer scroll nodes. It encapsulates the bubbling,
+distribution, top controls, etc. behavior we associate with scrolling the
+viewport.
+
+We use the outer viewport scroll node to represent cc::Viewport scrolling in
+cases where the scroller must be represented by a scroll node (e.g.
+CurrentlyScrollingNode). In these cases we make sure to check for the outer
+scroll node use cc::Viewport instead. This means that in cases where we want
+"viewport" scrolling, we must use the outer viewport scroll node. This can also
+happen when the inner viewport is reached in the scroll chain, for example, by
+scroll bubbling from a `position: fixed` subtree; we use the outer scroll node
+to scroll this case.
+
+The scroll chain is terminated once we've scrolled the cc::Viewport. i.e.
+scrolls don't bubble above the cc::Viewport.
+
+#### Root Scroller Nuances
+
+When we have a non-document root scroller, there are cases where we
+specifically wish to scroll only the inner viewport.  For example, when a
+scroll started from a non-descendant of the root scroller or a `position:
+fixed` element and bubbles up. In these cases, we shouldn't scroll using
+cc::Viewport because that would scroll the root scroller as well. Doing so
+would create a difference in how scrolls chain based on which element is the
+root scroller, something we must avoid for interop and compatibility reasons.
+
+This means that when we reach the inner viewport scroll node in the scroll
+chain we need to know whether to use cc::Viewport or not. Blink sets the
+|prevent\_viewport\_scrolling\_from\_inner| bit on the inner viewport scroll
+node so that the compositor can know that scrolls bubbling to the inner
+viewport should not use the cc::Viewport class.
+
+## Other Docs
+
+* [Blink Scrolling](../../third_party/blink/renderer/core/page/scrolling/README.md)
+  provides information about similar concepts in Blink and the web-platform.
+
+## Glossary
+
+### Inner Viewport
+
+Also called the "Visual Viewport" in web/Blink terminology. This is the
+viewport the user actually sees and corresponds to the content visible in the
+browser window.
+
+### Outer Viewport
+
+Also called the "Layout Viewport" in web/Blink terminology. This is the main
+"content scroller" in a given page, typically the document (`<html>`) element.
+This is the scroller to which position: fixed elements remain fixed to.
diff --git a/cc/input/scroll_snap_data.cc b/cc/input/scroll_snap_data.cc
index a8a2f752..730326a 100644
--- a/cc/input/scroll_snap_data.cc
+++ b/cc/input/scroll_snap_data.cc
@@ -126,63 +126,102 @@
   if (!should_snap_on_x && !should_snap_on_y)
     return false;
 
-  base::Optional<SnapSearchResult> closest_x, closest_y;
-  // A region that includes every reachable scroll position.
-  gfx::RectF scrollable_region(0, 0, max_position_.x(), max_position_.y());
+  bool should_prioritize_x_target =
+      strategy.ShouldPrioritizeSnapTargets() &&
+      target_snap_area_element_ids_.x != ElementId();
+  bool should_prioritize_y_target =
+      strategy.ShouldPrioritizeSnapTargets() &&
+      target_snap_area_element_ids_.y != ElementId();
+
+  base::Optional<SnapSearchResult> selected_x, selected_y;
   if (should_snap_on_x) {
-    // Start from current position in the cross axis. The search algorithm
-    // expects the cross axis position to be inside scroller bounds. But since
-    // we cannot always assume that the incoming value fits this criteria we
-    // clamp it to the bounds to ensure this variant.
-    SnapSearchResult initial_snap_position_y = {
-        base::ClampToRange(base_position.y(), 0.f, max_position_.y()),
-        gfx::RangeF(0, max_position_.x())};
-    closest_x =
-        FindClosestValidArea(SearchAxis::kX, strategy, initial_snap_position_y);
+    if (should_prioritize_x_target) {
+      // TODO(http://crbug.com/866127): If the target snap area is covering the
+      // snapport then we should fallback to the default "closest-area" method
+      // instead.
+      selected_x = GetTargetSnapAreaSearchResult(SearchAxis::kX);
+      DCHECK(selected_x.has_value());
+    } else {
+      // Start from current position in the cross axis. The search algorithm
+      // expects the cross axis position to be inside scroller bounds. But since
+      // we cannot always assume that the incoming value fits this criteria we
+      // clamp it to the bounds to ensure this variant.
+      SnapSearchResult initial_snap_position_y = {
+          base::ClampToRange(base_position.y(), 0.f, max_position_.y()),
+          gfx::RangeF(0, max_position_.x())};
+
+      selected_x = FindClosestValidArea(SearchAxis::kX, strategy,
+                                        initial_snap_position_y);
+    }
   }
   if (should_snap_on_y) {
-    SnapSearchResult initial_snap_position_x = {
-        base::ClampToRange(base_position.x(), 0.f, max_position_.x()),
-        gfx::RangeF(0, max_position_.y())};
-    closest_y =
-        FindClosestValidArea(SearchAxis::kY, strategy, initial_snap_position_x);
+    if (should_prioritize_y_target) {
+      selected_y = GetTargetSnapAreaSearchResult(SearchAxis::kY);
+      DCHECK(selected_y.has_value());
+    } else {
+      SnapSearchResult initial_snap_position_x = {
+          base::ClampToRange(base_position.x(), 0.f, max_position_.x()),
+          gfx::RangeF(0, max_position_.y())};
+      selected_y = FindClosestValidArea(SearchAxis::kY, strategy,
+                                        initial_snap_position_x);
+    }
   }
 
-  if (!closest_x.has_value() && !closest_y.has_value())
+  if (!selected_x.has_value() && !selected_y.has_value())
     return false;
 
   // If snapping in one axis pushes off-screen the other snap area, this snap
   // position is invalid. https://drafts.csswg.org/css-scroll-snap-1/#snap-scope
-  // In this case, we choose the axis whose snap area is closer, and find a
-  // mutual visible snap area on the other axis.
-  if (closest_x.has_value() && closest_y.has_value() &&
-      !IsMutualVisible(closest_x.value(), closest_y.value())) {
-    bool candidate_on_x_axis_is_closer =
-        std::abs(closest_x.value().snap_offset() - base_position.x()) <=
-        std::abs(closest_y.value().snap_offset() - base_position.y());
-    if (candidate_on_x_axis_is_closer) {
-      closest_y =
-          FindClosestValidArea(SearchAxis::kY, strategy, closest_x.value());
+  // In this case, first check if we need to prioritize the snap area from
+  // one axis over the other and select that axis, or if we don't prioritize an
+  // axis over the other, we choose the axis whose snap area is closer.
+  // Then find a new snap area on the other axis that is mutually visible with
+  // the selected axis' snap area.
+  if (selected_x.has_value() && selected_y.has_value() &&
+      !IsMutualVisible(selected_x.value(), selected_y.value())) {
+    bool keep_candidate_on_x = should_prioritize_x_target;
+    if (should_prioritize_x_target == should_prioritize_y_target) {
+      keep_candidate_on_x =
+          std::abs(selected_x.value().snap_offset() - base_position.x()) <=
+          std::abs(selected_y.value().snap_offset() - base_position.y());
+    }
+    if (keep_candidate_on_x) {
+      selected_y =
+          FindClosestValidArea(SearchAxis::kY, strategy, selected_x.value());
     } else {
-      closest_x =
-          FindClosestValidArea(SearchAxis::kX, strategy, closest_y.value());
+      selected_x =
+          FindClosestValidArea(SearchAxis::kX, strategy, selected_y.value());
     }
   }
 
   *snap_position = strategy.current_position();
-  if (closest_x.has_value()) {
-    snap_position->set_x(closest_x.value().snap_offset());
-    target_element_ids->x = closest_x.value().element_id();
+  if (selected_x.has_value()) {
+    snap_position->set_x(selected_x.value().snap_offset());
+    target_element_ids->x = selected_x.value().element_id();
   }
 
-  if (closest_y.has_value()) {
-    snap_position->set_y(closest_y.value().snap_offset());
-    target_element_ids->y = closest_y.value().element_id();
+  if (selected_y.has_value()) {
+    snap_position->set_y(selected_y.value().snap_offset());
+    target_element_ids->y = selected_y.value().element_id();
   }
 
   return true;
 }
 
+base::Optional<SnapSearchResult>
+SnapContainerData::GetTargetSnapAreaSearchResult(SearchAxis axis) const {
+  ElementId target_id = axis == SearchAxis::kX
+                            ? target_snap_area_element_ids_.x
+                            : target_snap_area_element_ids_.y;
+  if (target_id == ElementId())
+    return base::nullopt;
+  for (const SnapAreaData& area : snap_area_list_) {
+    if (area.element_id == target_id)
+      return GetSnapSearchResult(axis, area);
+  }
+  return base::nullopt;
+}
+
 const TargetSnapAreaElementIds& SnapContainerData::GetTargetSnapAreaElementIds()
     const {
   return target_snap_area_element_ids_;
diff --git a/cc/input/scroll_snap_data.h b/cc/input/scroll_snap_data.h
index 27b52a6..52f5acd 100644
--- a/cc/input/scroll_snap_data.h
+++ b/cc/input/scroll_snap_data.h
@@ -271,6 +271,11 @@
       const SnapSelectionStrategy& strategy,
       const SnapSearchResult& cross_axis_snap_result) const;
 
+  // Finds the snap area associated with the target snap area element id for the
+  // given axis.
+  base::Optional<SnapSearchResult> GetTargetSnapAreaSearchResult(
+      SearchAxis axis) const;
+
   // Returns all the info needed to snap at this area on the given axis,
   // including:
   // - The offset at which the snap area and the snap container meet the
diff --git a/cc/input/scroll_snap_data_unittest.cc b/cc/input/scroll_snap_data_unittest.cc
index 9fed8b27..83359578 100644
--- a/cc/input/scroll_snap_data_unittest.cc
+++ b/cc/input/scroll_snap_data_unittest.cc
@@ -468,4 +468,140 @@
             target_elements);
 }
 
+TEST_F(ScrollSnapDataTest, SnapToOneTargetElementOnX) {
+  SnapContainerData container(
+      ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory),
+      gfx::RectF(0, 0, 200, 300), gfx::ScrollOffset(600, 800));
+
+  SnapAreaData closer_area_x(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(100, 0, 1, 1), false, ElementId(10));
+  SnapAreaData target_area_x(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(200, 100, 1, 1), false, ElementId(20));
+  SnapAreaData closer_area_y(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(300, 50, 1, 1), false, ElementId(30));
+
+  container.AddSnapAreaData(closer_area_x);
+  container.AddSnapAreaData(target_area_x);
+  container.AddSnapAreaData(closer_area_y);
+  container.SetTargetSnapAreaElementIds(
+      TargetSnapAreaElementIds(ElementId(20), ElementId()));
+
+  // Even though closer_area_x is closer to the scroll offset, the container
+  // should snap to the target for the x-axis. However, since the target is not
+  // set for the y-axis, the target on the y-axis should be closer_area_y.
+  std::unique_ptr<SnapSelectionStrategy> target_element_strategy =
+      SnapSelectionStrategy::CreateForTargetElement(gfx::ScrollOffset(0, 0));
+
+  gfx::ScrollOffset snap_position = gfx::ScrollOffset();
+  EXPECT_TRUE(container.FindSnapPosition(*target_element_strategy,
+                                         &snap_position, &target_elements));
+
+  EXPECT_EQ(200, snap_position.x());
+  EXPECT_EQ(50, snap_position.y());
+  EXPECT_EQ(TargetSnapAreaElementIds(ElementId(20), ElementId(30)),
+            target_elements);
+}
+
+TEST_F(ScrollSnapDataTest, SnapToOneTargetElementOnY) {
+  SnapContainerData container(
+      ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory),
+      gfx::RectF(0, 0, 200, 300), gfx::ScrollOffset(600, 800));
+
+  SnapAreaData closer_area_y(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(0, 100, 1, 1), false, ElementId(10));
+  SnapAreaData target_area_y(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(100, 200, 1, 1), false, ElementId(20));
+  SnapAreaData closer_area_x(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(50, 300, 1, 1), false, ElementId(30));
+
+  container.AddSnapAreaData(closer_area_y);
+  container.AddSnapAreaData(target_area_y);
+  container.AddSnapAreaData(closer_area_x);
+  container.SetTargetSnapAreaElementIds(
+      TargetSnapAreaElementIds(ElementId(), ElementId(20)));
+
+  // Even though closer_area_y is closer to the scroll offset, the container
+  // should snap to the target for the y-axis. However, since the target is not
+  // set for the x-axis, the target on the x-axis should be closer_area_x.
+  std::unique_ptr<SnapSelectionStrategy> target_element_strategy =
+      SnapSelectionStrategy::CreateForTargetElement(gfx::ScrollOffset(0, 0));
+
+  gfx::ScrollOffset snap_position = gfx::ScrollOffset();
+  EXPECT_TRUE(container.FindSnapPosition(*target_element_strategy,
+                                         &snap_position, &target_elements));
+
+  EXPECT_EQ(50, snap_position.x());
+  EXPECT_EQ(200, snap_position.y());
+  EXPECT_EQ(TargetSnapAreaElementIds(ElementId(30), ElementId(20)),
+            target_elements);
+}
+
+TEST_F(ScrollSnapDataTest, SnapToTwoTargetElementsMutualVisible) {
+  SnapContainerData container(
+      ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory),
+      gfx::RectF(0, 0, 300, 300), gfx::ScrollOffset(600, 800));
+
+  SnapAreaData target_area_x(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(100, 200, 1, 1), false, ElementId(10));
+  SnapAreaData target_area_y(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(200, 100, 1, 1), false, ElementId(20));
+  SnapAreaData closer_area_both(ScrollSnapAlign(SnapAlignment::kStart),
+                                gfx::RectF(0, 0, 1, 1), false, ElementId(30));
+
+  container.AddSnapAreaData(target_area_x);
+  container.AddSnapAreaData(target_area_y);
+  container.AddSnapAreaData(closer_area_both);
+  container.SetTargetSnapAreaElementIds(
+      TargetSnapAreaElementIds(ElementId(10), ElementId(20)));
+
+  // The container should snap to both target areas since they are mutually
+  // visible, while ignoring the snap area that is closest to the scroll offset.
+  std::unique_ptr<SnapSelectionStrategy> target_element_strategy =
+      SnapSelectionStrategy::CreateForTargetElement(gfx::ScrollOffset(0, 0));
+
+  gfx::ScrollOffset snap_position = gfx::ScrollOffset();
+  EXPECT_TRUE(container.FindSnapPosition(*target_element_strategy,
+                                         &snap_position, &target_elements));
+
+  EXPECT_EQ(100, snap_position.x());
+  EXPECT_EQ(100, snap_position.y());
+  EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId(20)),
+            target_elements);
+}
+
+TEST_F(ScrollSnapDataTest, SnapToTwoTargetElementsNotMutualVisible) {
+  SnapContainerData container(
+      ScrollSnapType(false, SnapAxis::kBoth, SnapStrictness::kMandatory),
+      gfx::RectF(0, 0, 300, 300), gfx::ScrollOffset(600, 800));
+
+  SnapAreaData target_area_x(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(100, 500, 1, 1), false, ElementId(10));
+  SnapAreaData target_area_y(ScrollSnapAlign(SnapAlignment::kStart),
+                             gfx::RectF(500, 100, 1, 1), false, ElementId(20));
+  SnapAreaData area_mutually_visible_to_targets(
+      ScrollSnapAlign(SnapAlignment::kStart), gfx::RectF(350, 350, 1, 1), false,
+      ElementId(30));
+
+  container.AddSnapAreaData(target_area_x);
+  container.AddSnapAreaData(target_area_y);
+  container.AddSnapAreaData(area_mutually_visible_to_targets);
+  container.SetTargetSnapAreaElementIds(
+      TargetSnapAreaElementIds(ElementId(10), ElementId(20)));
+
+  // The container cannot snap to both targets, so it should snap to the one
+  // closer to the scroll offset, and then snap to the closest mutually visible
+  // snap area on the other axis.
+  std::unique_ptr<SnapSelectionStrategy> target_element_strategy =
+      SnapSelectionStrategy::CreateForTargetElement(gfx::ScrollOffset(10, 0));
+
+  gfx::ScrollOffset snap_position = gfx::ScrollOffset();
+  EXPECT_TRUE(container.FindSnapPosition(*target_element_strategy,
+                                         &snap_position, &target_elements));
+
+  EXPECT_EQ(100, snap_position.x());
+  EXPECT_EQ(350, snap_position.y());
+  EXPECT_EQ(TargetSnapAreaElementIds(ElementId(10), ElementId(30)),
+            target_elements);
+}
+
 }  // namespace cc
diff --git a/cc/input/snap_selection_strategy.cc b/cc/input/snap_selection_strategy.cc
index 7ea5fe4..e2df143 100644
--- a/cc/input/snap_selection_strategy.cc
+++ b/cc/input/snap_selection_strategy.cc
@@ -10,9 +10,10 @@
 SnapSelectionStrategy::CreateForEndPosition(
     const gfx::ScrollOffset& current_position,
     bool scrolled_x,
-    bool scrolled_y) {
+    bool scrolled_y,
+    SnapTargetsPrioritization prioritization) {
   return std::make_unique<EndPositionStrategy>(current_position, scrolled_x,
-                                               scrolled_y);
+                                               scrolled_y, prioritization);
 }
 
 std::unique_ptr<SnapSelectionStrategy>
@@ -30,6 +31,14 @@
                                                    displacement);
 }
 
+std::unique_ptr<SnapSelectionStrategy>
+SnapSelectionStrategy::CreateForTargetElement(
+    gfx::ScrollOffset current_position) {
+  return std::make_unique<EndPositionStrategy>(
+      current_position, true /* scrolled_x */, true /* scrolled_y */,
+      SnapTargetsPrioritization::kRequire);
+}
+
 bool SnapSelectionStrategy::HasIntendedDirection() const {
   return true;
 }
@@ -45,6 +54,10 @@
              : area.scroll_snap_align.alignment_block != SnapAlignment::kNone;
 }
 
+bool SnapSelectionStrategy::ShouldPrioritizeSnapTargets() const {
+  return false;
+}
+
 bool EndPositionStrategy::ShouldSnapOnX() const {
   return scrolled_x_;
 }
@@ -72,6 +85,10 @@
   return false;
 }
 
+bool EndPositionStrategy::ShouldPrioritizeSnapTargets() const {
+  return snap_targets_prioritization_ == SnapTargetsPrioritization::kRequire;
+}
+
 const base::Optional<SnapSearchResult>& EndPositionStrategy::PickBestResult(
     const base::Optional<SnapSearchResult>& closest,
     const base::Optional<SnapSearchResult>& covering) const {
diff --git a/cc/input/snap_selection_strategy.h b/cc/input/snap_selection_strategy.h
index 4b2fc2e..f3dca1fc 100644
--- a/cc/input/snap_selection_strategy.h
+++ b/cc/input/snap_selection_strategy.h
@@ -12,6 +12,7 @@
 namespace cc {
 
 enum class SnapStopAlwaysFilter { kIgnore, kRequire };
+enum class SnapTargetsPrioritization { kIgnore, kRequire };
 
 // This class represents an abstract strategy that decide which snap selection
 // should be considered valid. There are concrete implementations for three core
@@ -24,7 +25,9 @@
   static std::unique_ptr<SnapSelectionStrategy> CreateForEndPosition(
       const gfx::ScrollOffset& current_position,
       bool scrolled_x,
-      bool scrolled_y);
+      bool scrolled_y,
+      SnapTargetsPrioritization prioritization =
+          SnapTargetsPrioritization::kIgnore);
   static std::unique_ptr<SnapSelectionStrategy> CreateForDirection(
       gfx::ScrollOffset current_position,
       gfx::ScrollOffset step,
@@ -33,10 +36,20 @@
       gfx::ScrollOffset current_position,
       gfx::ScrollOffset displacement);
 
+  // Creates a selection strategy that attempts to snap to previously snapped
+  // targets if possible, but defaults to finding the closest snap point if
+  // the target no longer exists.
+  static std::unique_ptr<SnapSelectionStrategy> CreateForTargetElement(
+      gfx::ScrollOffset current_position);
+
   // Returns whether it's snappable on x or y depending on the scroll performed.
   virtual bool ShouldSnapOnX() const = 0;
   virtual bool ShouldSnapOnY() const = 0;
 
+  // Returns whether snapping should attempt to snap to the previously snapped
+  // area if possible.
+  virtual bool ShouldPrioritizeSnapTargets() const;
+
   // Returns the end position of the scroll if no snap interferes.
   virtual gfx::ScrollOffset intended_position() const = 0;
   // Returns the scroll position from which the snap position should minimize
@@ -88,10 +101,12 @@
  public:
   EndPositionStrategy(const gfx::ScrollOffset& current_position,
                       bool scrolled_x,
-                      bool scrolled_y)
+                      bool scrolled_y,
+                      SnapTargetsPrioritization snap_targets_prioritization)
       : SnapSelectionStrategy(current_position),
         scrolled_x_(scrolled_x),
-        scrolled_y_(scrolled_y) {}
+        scrolled_y_(scrolled_y),
+        snap_targets_prioritization_(snap_targets_prioritization) {}
   ~EndPositionStrategy() override = default;
 
   bool ShouldSnapOnX() const override;
@@ -102,6 +117,7 @@
 
   bool IsValidSnapPosition(SearchAxis axis, float position) const override;
   bool HasIntendedDirection() const override;
+  bool ShouldPrioritizeSnapTargets() const override;
 
   const base::Optional<SnapSearchResult>& PickBestResult(
       const base::Optional<SnapSearchResult>& closest,
@@ -111,6 +127,7 @@
   // Whether the x axis and y axis have been scrolled in this scroll gesture.
   const bool scrolled_x_;
   const bool scrolled_y_;
+  SnapTargetsPrioritization snap_targets_prioritization_;
 };
 
 // Examples for intended direction scrolls include
diff --git a/cc/layers/viewport.cc b/cc/layers/viewport.cc
index 5a50ee6..635d9996 100644
--- a/cc/layers/viewport.cc
+++ b/cc/layers/viewport.cc
@@ -69,18 +69,19 @@
   return result;
 }
 
-bool Viewport::CanScroll(const ScrollState& scroll_state) const {
-  auto* outer_node = OuterScrollNode();
+bool Viewport::CanScroll(const ScrollNode& node,
+                         const ScrollState& scroll_state) const {
+  DCHECK(ShouldScroll(node));
 
-  if (!outer_node)
-    return false;
+  bool result = host_impl_->CanConsumeDelta(*InnerScrollNode(), scroll_state);
 
-  bool result = false;
-  if (auto* inner_node = InnerScrollNode())
-    result |= host_impl_->CanConsumeDelta(*inner_node, scroll_state);
+  // If the passed in node is the inner viewport, we're not interested in the
+  // scrollability of the outer viewport. See LTHI::GetNodeToScroll for how the
+  // scroll chain is constructed.
+  if (node.scrolls_inner_viewport)
+    return result;
 
-  result |= host_impl_->CanConsumeDelta(*outer_node, scroll_state);
-
+  result |= host_impl_->CanConsumeDelta(*OuterScrollNode(), scroll_state);
   return result;
 }
 
@@ -229,15 +230,20 @@
   pinch_zoom_active_ = false;
 }
 
-bool Viewport::ShouldScroll(const ScrollNode& scroll_node) {
+bool Viewport::ShouldScroll(const ScrollNode& scroll_node) const {
+  // Non-main frame renderers and the UI compositor will not have viewport
+  // scrolling nodes and should thus never scroll with the Viewport object.
+  if (!InnerScrollNode() || !OuterScrollNode()) {
+    DCHECK(!InnerScrollNode());
+    DCHECK(!OuterScrollNode());
+    DCHECK(!scroll_node.scrolls_inner_viewport);
+    DCHECK(!scroll_node.scrolls_outer_viewport);
+    return false;
+  }
   return scroll_node.scrolls_inner_viewport ||
          scroll_node.scrolls_outer_viewport;
 }
 
-ScrollNode* Viewport::MainScrollNode() const {
-  return host_impl_->OuterViewportScrollNode();
-}
-
 gfx::Vector2dF Viewport::ScrollBrowserControls(const gfx::Vector2dF& delta) {
   gfx::Vector2dF excess_delta =
       host_impl_->browser_controls_manager()->ScrollBy(delta);
diff --git a/cc/layers/viewport.h b/cc/layers/viewport.h
index ac17140..55a64b7 100644
--- a/cc/layers/viewport.h
+++ b/cc/layers/viewport.h
@@ -21,6 +21,15 @@
 // outer viewport (layout) scroll layers. These layers have different scroll
 // bubbling behavior from the rest of the layer tree which is encoded in this
 // class.
+//
+// When performing any kind of scroll operations on either the inner or outer
+// scroll node, they must be done using this class. Typically, the outer
+// viewport's scroll node will be used in the scroll chain to represent a full
+// viewport scroll (i.e. one that will use this class to scroll both inner and
+// outer viewports, as appropriate). However, in some situations (see comments
+// in LayerTreeHostImpl::GetNodeToScroll) we may wish to scroll only the inner
+// viewport. In that case, the inner viewport is used in the scroll chain, but
+// we should still scroll using this class.
 class CC_EXPORT Viewport {
  public:
   // If the pinch zoom anchor on the first PinchUpdate is within this length
@@ -55,8 +64,6 @@
                         bool affect_browser_controls,
                         bool scroll_outer_viewport);
 
-  bool CanScroll(const ScrollState& scroll_state) const;
-
   // TODO(bokan): Callers can now be replaced by ScrollBy.
   void ScrollByInnerFirst(const gfx::Vector2dF& delta);
 
@@ -71,12 +78,18 @@
   void PinchEnd(const gfx::Point& anchor, bool snap_to_min);
 
   // Returns true if the given scroll node should be scrolled via this class,
-  // false if it should be scrolled directly.
-  bool ShouldScroll(const ScrollNode& scroll_node);
+  // false if it should be scrolled directly. Scrolling either the inner or
+  // outer viewport nodes must be done using this class.
+  bool ShouldScroll(const ScrollNode& scroll_node) const;
 
-  // Returns the "representative" viewport scroll node. That is, the one that's
-  // set as the currently scrolling node when the viewport scrolls.
-  ScrollNode* MainScrollNode() const;
+  // Returns true if the viewport can consume any of the delta for the given
+  // the |scroll_state|. This method takes a ScrollNode because viewport
+  // scrolling can occur for either the inner or outer scroll nodes. If it
+  // outer is used, we do a combined scroll that distributes the scroll among
+  // the inner and outer viewports. If the inner is used, only the inner
+  // viewport is scrolled. It is an error to pass any other node to this
+  // method.
+  bool CanScroll(const ScrollNode& node, const ScrollState& scroll_state) const;
 
  private:
   explicit Viewport(LayerTreeHostImpl* host_impl);
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 9beedd4c..6643f18 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -333,7 +333,8 @@
   };
 
   // Sets the collection of viewport property ids, defined to allow viewport
-  // pinch-zoom etc. on the compositor thread.
+  // pinch-zoom etc. on the compositor thread. This is set only on the
+  // main-frame's compositor, i.e., will be unset in OOPIF and UI compositors.
   void RegisterViewportPropertyIds(const ViewportPropertyIds&);
 
   LayerTreeHost::ViewportPropertyIds ViewportPropertyIdsForTesting() const {
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 42be9a3..8761b51 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -823,7 +823,7 @@
   auto* node = CurrentlyScrollingNode();
   if (!node)
     return false;
-  return node == viewport()->MainScrollNode();
+  return viewport().ShouldScroll(*node);
 }
 
 bool LayerTreeHostImpl::IsCurrentlyScrollingLayerAt(
@@ -850,12 +850,6 @@
   if (scrolling_node == test_scroll_node)
     return true;
 
-  // For active scrolling state treat the inner/outer viewports interchangeably.
-  if (scrolling_node->scrolls_inner_viewport ||
-      scrolling_node->scrolls_outer_viewport) {
-    return test_scroll_node == OuterViewportScrollNode();
-  }
-
   return false;
 }
 
@@ -2200,14 +2194,11 @@
   metadata.min_page_scale_factor = active_tree_->min_page_scale_factor();
   metadata.max_page_scale_factor = active_tree_->max_page_scale_factor();
   metadata.root_layer_size = active_tree_->ScrollableSize();
-  if (const auto* outer_viewport_scroll_node = OuterViewportScrollNode()) {
+  if (InnerViewportScrollNode()) {
+    DCHECK(OuterViewportScrollNode());
     metadata.root_overflow_y_hidden =
-        !outer_viewport_scroll_node->user_scrollable_vertical;
-  }
-  const auto* inner_viewport_scroll_node = InnerViewportScrollNode();
-  if (inner_viewport_scroll_node) {
-    metadata.root_overflow_y_hidden |=
-        !inner_viewport_scroll_node->user_scrollable_vertical;
+        !OuterViewportScrollNode()->user_scrollable_vertical ||
+        !InnerViewportScrollNode()->user_scrollable_vertical;
   }
   metadata.has_transparent_background =
       frame->render_passes.back()->has_transparent_background;
@@ -3056,8 +3047,8 @@
   if (InnerViewportScrollNode()) {
     active_tree_->property_trees()->scroll_tree.ClampScrollToMaxScrollOffset(
         InnerViewportScrollNode(), active_tree_.get());
-  }
-  if (OuterViewportScrollNode()) {
+
+    DCHECK(OuterViewportScrollNode());
     active_tree_->property_trees()->scroll_tree.ClampScrollToMaxScrollOffset(
         OuterViewportScrollNode(), active_tree_.get());
   }
@@ -3333,10 +3324,6 @@
   paint_worklet_painter_ = std::move(painter);
 }
 
-ScrollNode* LayerTreeHostImpl::ViewportMainScrollNode() {
-  return viewport()->MainScrollNode();
-}
-
 void LayerTreeHostImpl::QueueImageDecode(int request_id,
                                          const PaintImage& image) {
   TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
@@ -3659,13 +3646,13 @@
     return scroll_status;
   }
 
-  // The outer viewport should be scrolled even if it has no scroll extent
+  // The a viewport node should be scrolled even if it has no scroll extent
   // since it'll scroll using the Viewport class which will generate browser
   // controls movement and overscroll delta.
   gfx::ScrollOffset max_scroll_offset =
       scroll_tree.MaxScrollOffset(scroll_node->id);
   if (max_scroll_offset.x() <= 0 && max_scroll_offset.y() <= 0 &&
-      !scroll_node->scrolls_outer_viewport) {
+      !viewport().ShouldScroll(*scroll_node)) {
     TRACE_EVENT0("cc",
                  "LayerImpl::tryScroll: Ignored. Technically scrollable,"
                  " but has no affordance in either direction.");
@@ -3773,32 +3760,7 @@
   if (!impl_scroll_node)
     return nullptr;
 
-  // Blink has a notion of a "root scroller", which is the scroller in a page
-  // that is considered to host the main content. Typically this will be the
-  // document/LayoutView contents; however, in some situations Blink may choose
-  // a sub-scroller (div, iframe) that should scroll with "viewport" behavior.
-  // The "root scroller" is the node designated as the outer viewport in CC.
-  // See third_party/blink/renderer/core/page/scrolling/README.md for details.
-  //
-  // "Viewport" scrolling ensures generation of overscroll events, top controls
-  // movement, as well as correct multi-viewport panning in pinch-zoom and
-  // other scenarios.  We use the viewport's outer scroll node to represent the
-  // viewport in the scroll chain and apply scroll delta using CC's Viewport
-  // class.
-  //
-  // Scrolling from position: fixed layers will chain directly up to the inner
-  // viewport. Whether that should use the outer viewport (and thus the
-  // Viewport class) to scroll or not depends on the root scroller scenario
-  // because we don't want setting a root scroller to change the scroll chain
-  // order. The |prevent_viewport_scrolling_from_inner| bit is used to
-  // communicate that context.
-  DCHECK(!impl_scroll_node->prevent_viewport_scrolling_from_inner ||
-         impl_scroll_node->scrolls_inner_viewport);
-  bool should_use_viewport =
-      OuterViewportScrollNode() && impl_scroll_node->scrolls_inner_viewport &&
-      !impl_scroll_node->prevent_viewport_scrolling_from_inner;
-  if (should_use_viewport)
-    impl_scroll_node = OuterViewportScrollNode();
+  impl_scroll_node = GetNodeToScroll(impl_scroll_node);
 
   // Ensure that final scroll node scrolls on impl thread (crbug.com/625100)
   ScrollStatus status =
@@ -3873,9 +3835,11 @@
   // If the viewport is scrolling and it cannot consume any delta hints, the
   // scroll event will need to get bubbled if the viewport is for a guest or
   // oopif.
-  if (active_tree_->CurrentlyScrollingNode() == ViewportMainScrollNode() &&
-      !viewport()->CanScroll(*scroll_state)) {
-    scroll_status.bubble = true;
+  if (ScrollNode* node = active_tree_->CurrentlyScrollingNode()) {
+    if (viewport().ShouldScroll(*node) &&
+        !viewport().CanScroll(*node, *scroll_state)) {
+      scroll_status.bubble = true;
+    }
   }
 
   frame_trackers_.StartSequence(wheel_scrolling_
@@ -4002,6 +3966,38 @@
          type == InputHandler::TOUCHSCREEN;
 }
 
+ScrollNode* LayerTreeHostImpl::GetNodeToScroll(ScrollNode* node) const {
+  // Blink has a notion of a "root scroller", which is the scroller in a page
+  // that is considered to host the main content. Typically this will be the
+  // document/LayoutView contents; however, in some situations Blink may choose
+  // a sub-scroller (div, iframe) that should scroll with "viewport" behavior.
+  // The "root scroller" is the node designated as the outer viewport in CC.
+  // See third_party/blink/renderer/core/page/scrolling/README.md for details.
+  //
+  // "Viewport" scrolling ensures generation of overscroll events, top controls
+  // movement, as well as correct multi-viewport panning in pinch-zoom and
+  // other scenarios.  We use the viewport's outer scroll node to represent the
+  // viewport in the scroll chain and apply scroll delta using CC's Viewport
+  // class.
+  //
+  // Scrolling from position: fixed layers will chain directly up to the inner
+  // viewport. Whether that should use the outer viewport (and thus the
+  // Viewport class) to scroll or not depends on the root scroller scenario
+  // because we don't want setting a root scroller to change the scroll chain
+  // order. The |prevent_viewport_scrolling_from_inner| bit is used to
+  // communicate that context.
+  DCHECK(!node->prevent_viewport_scrolling_from_inner ||
+         node->scrolls_inner_viewport);
+
+  if (node->scrolls_inner_viewport &&
+      !node->prevent_viewport_scrolling_from_inner) {
+    DCHECK(OuterViewportScrollNode());
+    return OuterViewportScrollNode();
+  }
+
+  return node;
+}
+
 // Initial scroll hit testing can be unreliable in the presence of squashed
 // layers. In this case, we fall back to main thread scrolling. This function
 // compares |layer_impl| returned from a regular hit test to the layer
@@ -4014,9 +4010,8 @@
 // (since they don't scroll with the outer viewport), however, scrolls from the
 // fixed layer still chain to the outer viewport. It's also possible for a node
 // to have the inner viewport as its ancestor without going through the outer
-// viewport; however, it will still scroll using the viewport(). Hence, this
-// method needs to use the same scroll chaining logic we use in ApplyScroll by
-// looking at Viewport::ShouldScroll.
+// viewport; however, it may still scroll using the viewport(). Hence, this
+// method must use the same scroll chaining logic we use in ApplyScroll.
 bool LayerTreeHostImpl::IsInitialScrollHitTestReliable(
     LayerImpl* layer_impl,
     LayerImpl* first_scrolling_layer_or_scrollbar) {
@@ -4035,13 +4030,7 @@
   for (; scroll_tree.parent(scroll_node);
        scroll_node = scroll_tree.parent(scroll_node)) {
     if (scroll_node->scrollable) {
-      // Ensure we use scroll chaining behavior for the inner viewport node.
-      if (scroll_node->scrolls_inner_viewport &&
-          !scroll_node->prevent_viewport_scrolling_from_inner) {
-        closest_scroll_node = viewport()->MainScrollNode();
-      } else {
-        closest_scroll_node = scroll_node;
-      }
+      closest_scroll_node = GetNodeToScroll(scroll_node);
       break;
     }
   }
@@ -4267,9 +4256,7 @@
         continue;
       }
 
-      bool scrolls_main_viewport_scroll_layer =
-          scroll_node == viewport()->MainScrollNode();
-      if (scrolls_main_viewport_scroll_layer) {
+      if (viewport().ShouldScroll(*scroll_node)) {
         // Flash the overlay scrollbar even if the scroll dalta is 0.
         if (settings_.scrollbar_flash_after_any_scroll_update) {
           FlashAllScrollbars(false);
@@ -4279,9 +4266,11 @@
           if (animation_controller)
             animation_controller->WillUpdateScroll();
         }
+      }
 
+      if (scroll_node->scrolls_outer_viewport) {
         gfx::Vector2dF scrolled =
-            viewport()->ScrollAnimated(pending_delta, delayed_by);
+            viewport().ScrollAnimated(pending_delta, delayed_by);
         // Viewport::ScrollAnimated returns pending_delta as long as it starts
         // an animation.
         did_scroll_x_for_scroll_gesture_ |= scrolled.x() != 0;
@@ -4512,18 +4501,17 @@
   // details.
   const float kEpsilon = 0.1f;
 
-  if (viewport()->ShouldScroll(*scroll_node)) {
-    // |scroll_outer_vieiwport| will only ever be false if the scroll chains up
-    // to the viewport without going through the outer viewport scroll node.
+  if (viewport().ShouldScroll(*scroll_node)) {
+    // |scrolls_outer_vieiwport| will only ever be false if the scroll chains
+    // up to the viewport without going through the outer viewport scroll node.
     // This is because we normally terminate the scroll chain at the outer
     // viewport node.  For example, if we start scrolling from an element
     // that's not a descendant of the root scroller. In these cases we want to
     // scroll *only* the inner viewport -- to allow panning while zoomed -- but
     // still use Viewport::ScrollBy to also move browser controls if needed.
-    bool scroll_outer_viewport = scroll_node->scrolls_outer_viewport;
-    Viewport::ScrollResult result = viewport()->ScrollBy(
+    Viewport::ScrollResult result = viewport().ScrollBy(
         delta, viewport_point, scroll_state->is_direct_manipulation(),
-        !wheel_scrolling_, scroll_outer_viewport);
+        !wheel_scrolling_, scroll_node->scrolls_outer_viewport);
 
     applied_delta = result.consumed_delta;
     delta_applied_to_content = result.content_scrolled_delta;
@@ -4546,7 +4534,7 @@
     return;
   }
 
-  if (!viewport()->ShouldScroll(*scroll_node)) {
+  if (!viewport().ShouldScroll(*scroll_node)) {
     // If the applied delta is within 45 degrees of the input
     // delta, bail out to make it easier to scroll just one layer
     // in one direction without affecting any of its parents.
@@ -4578,23 +4566,18 @@
   std::list<ScrollNode*> current_scroll_chain;
   ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
   ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode();
-  ScrollNode* viewport_scroll_node = ViewportMainScrollNode();
   if (did_lock_scrolling_layer_) {
     DCHECK(scroll_node);
 
     // Needed for non-animated scrolls.
     current_scroll_chain.push_front(scroll_node);
   } else if (scroll_node) {
-    // TODO(bokan): The loop checks for a null parent but don't we still want to
-    // distribute to the root scroll node?
-    for (; scroll_tree.parent(scroll_node);
-         scroll_node = scroll_tree.parent(scroll_node)) {
-      if (scroll_node == viewport_scroll_node) {
-        // Don't chain scrolls past the outer viewport scroll layer. Once we
-        // reach that, we should scroll the viewport which is represented by the
-        // main viewport scroll layer.
-        DCHECK(viewport_scroll_node);
-        current_scroll_chain.push_front(viewport_scroll_node);
+    for (; scroll_node; scroll_node = scroll_tree.parent(scroll_node)) {
+      if (viewport().ShouldScroll(*scroll_node)) {
+        // Don't chain scrolls past a viewport node. Once we reach that, we
+        // should scroll using the appropriate viewport node which may not be
+        // |scroll_node|.
+        current_scroll_chain.push_front(GetNodeToScroll(scroll_node));
         break;
       }
 
@@ -4772,7 +4755,7 @@
 
   gfx::Vector2dF unused_root_delta;
   if (current_scrolling_node &&
-      current_scrolling_node == ViewportMainScrollNode()) {
+      viewport().ShouldScroll(*current_scrolling_node)) {
     unused_root_delta =
         gfx::Vector2dF(scroll_state->delta_x(), scroll_state->delta_y());
   }
@@ -4839,15 +4822,15 @@
       "offset_x", root_content_offset.x(), "offset_y", root_content_offset.y());
 
   gfx::Vector2dF physical_delta = ContentToPhysical(
-      root_content_offset.DeltaFrom(viewport()->TotalScrollOffset()),
+      root_content_offset.DeltaFrom(viewport().TotalScrollOffset()),
       active_tree()->page_scale_factor_for_scroll());
 
   bool changed = !viewport()
-                      ->ScrollBy(physical_delta,
-                                 /*viewport_point=*/gfx::Point(),
-                                 /*is_direct_manipulation=*/false,
-                                 /*affect_browser_controls=*/false,
-                                 /*scroll_outer_viewport=*/true)
+                      .ScrollBy(physical_delta,
+                                /*viewport_point=*/gfx::Point(),
+                                /*is_direct_manipulation=*/false,
+                                /*affect_browser_controls=*/false,
+                                /*scroll_outer_viewport=*/true)
                       .consumed_delta.IsZero();
   if (!changed)
     return;
@@ -4876,13 +4859,7 @@
   if (!FindSnapPositionAndSetTarget(scroll_node, *strategy, &snap_position))
     return false;
 
-  gfx::Vector2dF delta =
-      ScrollOffsetToVector2dF(snap_position - current_position);
-  bool scrolls_main_viewport_scroll_layer =
-      scroll_node == ViewportMainScrollNode();
-
-  bool did_animate = false;
-  if (scrolls_main_viewport_scroll_layer) {
+  if (viewport().ShouldScroll(*scroll_node)) {
     // Flash the overlay scrollbar even if the scroll dalta is 0.
     if (settings_.scrollbar_flash_after_any_scroll_update) {
       FlashAllScrollbars(false);
@@ -4892,10 +4869,16 @@
       if (animation_controller)
         animation_controller->WillUpdateScroll();
     }
+  }
+
+  gfx::Vector2dF delta =
+      ScrollOffsetToVector2dF(snap_position - current_position);
+  bool did_animate = false;
+  if (scroll_node->scrolls_outer_viewport) {
     gfx::Vector2dF scaled_delta(delta);
     scaled_delta.Scale(active_tree()->page_scale_factor_for_scroll());
     gfx::Vector2dF consumed_delta =
-        viewport()->ScrollAnimated(scaled_delta, base::TimeDelta());
+        viewport().ScrollAnimated(scaled_delta, base::TimeDelta());
     did_animate = !consumed_delta.IsZero();
   } else {
     did_animate = ScrollAnimationCreate(scroll_node, delta, base::TimeDelta());
@@ -4925,8 +4908,8 @@
 
 gfx::ScrollOffset LayerTreeHostImpl::GetVisualScrollOffset(
     const ScrollNode& scroll_node) const {
-  if (&scroll_node == viewport()->MainScrollNode())
-    return viewport()->TotalScrollOffset();
+  if (scroll_node.scrolls_outer_viewport)
+    return viewport().TotalScrollOffset();
   return active_tree()->property_trees()->scroll_tree.current_scroll_offset(
       scroll_node.element_id);
 }
@@ -5079,9 +5062,11 @@
       scroll_element_id = scroll_node->element_id;
 
     // Scrollbars for the viewport are registered with the outer viewport layer.
-    if (InnerViewportScrollNode() && OuterViewportScrollNode() &&
-        scroll_element_id == InnerViewportScrollNode()->element_id)
+    if (InnerViewportScrollNode() &&
+        scroll_element_id == InnerViewportScrollNode()->element_id) {
+      DCHECK(OuterViewportScrollNode());
       scroll_element_id = OuterViewportScrollNode()->element_id;
+    }
   }
 
   ScrollbarAnimationController* new_animation_controller =
@@ -5134,7 +5119,7 @@
   if (!InnerViewportScrollNode())
     return;
   has_pinch_zoomed_ = true;
-  viewport()->PinchUpdate(magnify_delta, anchor);
+  viewport().PinchUpdate(magnify_delta, anchor);
   client_->SetNeedsCommitOnImplThread();
   SetNeedsRedraw();
   client_->RenewTreePriority();
@@ -5150,7 +5135,7 @@
     pinch_gesture_end_should_clear_scrolling_node_ = false;
     ClearCurrentlyScrollingNode();
   }
-  viewport()->PinchEnd(anchor, snap_to_min);
+  viewport().PinchEnd(anchor, snap_to_min);
   browser_controls_offset_manager_->PinchEnd();
   client_->SetNeedsCommitOnImplThread();
   // When a pinch ends, we may be displaying content cached at incorrect scales,
@@ -5285,8 +5270,7 @@
   gfx::ScrollOffset next_scroll = gfx::ScrollOffset(
       page_scale_animation_->ScrollOffsetAtTime(monotonic_time));
 
-  DCHECK(viewport());
-  viewport()->ScrollByInnerFirst(next_scroll.DeltaFrom(scroll_total));
+  viewport().ScrollByInnerFirst(next_scroll.DeltaFrom(scroll_total));
 
   if (page_scale_animation_->IsAnimationCompleteAtTime(monotonic_time)) {
     page_scale_animation_ = nullptr;
@@ -5314,12 +5298,11 @@
   if (scroll_delta.IsZero())
     return false;
 
-  DCHECK(viewport());
-  viewport()->ScrollBy(scroll_delta,
-                       /*viewport_point=*/gfx::Point(),
-                       /*is_wheel_scroll=*/false,
-                       /*affect_browser_controls=*/false,
-                       /*scroll_outer_viewport=*/true);
+  viewport().ScrollBy(scroll_delta,
+                      /*viewport_point=*/gfx::Point(),
+                      /*is_wheel_scroll=*/false,
+                      /*affect_browser_controls=*/false,
+                      /*scroll_outer_viewport=*/true);
   client_->SetNeedsCommitOnImplThread();
   client_->RenewTreePriority();
   return true;
@@ -5410,7 +5393,8 @@
   // The viewport layers have only one set of scrollbars. On Android, these are
   // registered with the inner viewport, otherwise they're registered with the
   // outer viewport. If a controller for one exists, the other shouldn't.
-  if (InnerViewportScrollNode() && OuterViewportScrollNode()) {
+  if (InnerViewportScrollNode()) {
+    DCHECK(OuterViewportScrollNode());
     if (scroll_element_id == InnerViewportScrollNode()->element_id ||
         scroll_element_id == OuterViewportScrollNode()->element_id) {
       auto itr = scrollbar_animation_controllers_.find(
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index b6faedc..1bee87a 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -764,13 +764,6 @@
     return paint_worklet_painter_.get();
   }
 
-  // The viewport has two scroll nodes, corresponding to the visual and layout
-  // viewports. However, when we compute the scroll chain we include only one
-  // of these -- we call that the "main" scroll node. When scrolling it, we
-  // scroll using the Viewport class which knows how to distribute scroll
-  // between the two.
-  ScrollNode* ViewportMainScrollNode();
-
   void QueueImageDecode(int request_id, const PaintImage& image);
   std::vector<std::pair<int, bool>> TakeCompletedImageDecodeRequests();
 
@@ -849,6 +842,10 @@
   void CollectScrollDeltas(ScrollAndScaleSet* scroll_info) const;
   void CollectScrollbarUpdates(ScrollAndScaleSet* scroll_info) const;
 
+  // Returns the ScrollNode we should use to scroll, accounting for viewport
+  // scroll chaining rules.
+  ScrollNode* GetNodeToScroll(ScrollNode* node) const;
+
   // Transforms viewport start point and scroll delta to local start point and
   // local delta, respectively. If the transformation of either the start or end
   // point of a scroll is clipped, the function returns false.
@@ -898,7 +895,7 @@
   bool UpdateGpuRasterizationStatus();
   void UpdateTreeResourcesForGpuRasterizationIfNeeded();
 
-  Viewport* viewport() const { return viewport_.get(); }
+  Viewport& viewport() const { return *viewport_.get(); }
 
   InputHandler::ScrollStatus ScrollBeginImpl(
       ScrollState* scroll_state,
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 57f5ac4..bc80a91 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -451,9 +451,10 @@
     gfx::Size scroll_content_size = gfx::Size(345, 3800);
     gfx::Size scrollbar_size = gfx::Size(15, 600);
 
-    LayerImpl* root = SetupRootLayer<LayerImpl>(layer_tree_impl, content_size);
+    SetupViewportLayersNoScrolls(content_size);
+    LayerImpl* outer_scroll = OuterViewportScrollLayer();
     LayerImpl* scroll =
-        AddScrollableLayer(root, content_size, scroll_content_size);
+        AddScrollableLayer(outer_scroll, content_size, scroll_content_size);
 
     auto* squash2 = AddLayer<LayerImpl>(layer_tree_impl);
     squash2->SetBounds(gfx::Size(140, 300));
@@ -470,7 +471,7 @@
 
     auto* squash1 = AddLayer<LayerImpl>(layer_tree_impl);
     squash1->SetBounds(gfx::Size(140, 300));
-    CopyProperties(root, squash1);
+    CopyProperties(outer_scroll, squash1);
     squash1->SetOffsetToTransformParent(gfx::Vector2dF(220, 0));
     if (transparent_layer) {
       CreateEffectNode(squash1).opacity = 0.0f;
@@ -1293,9 +1294,9 @@
   gfx::Size scroll_content_size = gfx::Size(345, 3800);
   gfx::Size scrollbar_size = gfx::Size(15, 600);
 
-  LayerImpl* root = SetupDefaultRootLayer(content_size);
-  LayerImpl* scroll =
-      AddScrollableLayer(root, content_size, scroll_content_size);
+  SetupViewportLayersNoScrolls(content_size);
+  LayerImpl* scroll = AddScrollableLayer(OuterViewportScrollLayer(),
+                                         content_size, scroll_content_size);
 
   auto* drawn_scrollbar = AddLayer<PaintedScrollbarLayerImpl>(
       layer_tree_impl, VERTICAL, false, true);
@@ -7400,18 +7401,14 @@
   InputHandlerScrollResult scroll_result;
   gfx::Size scroll_container_size(5, 5);
   gfx::Size surface_size(10, 10);
-  LayerImpl* root_clip = SetupDefaultRootLayer(surface_size);
-  LayerImpl* root =
-      AddScrollableLayer(root_clip, scroll_container_size, surface_size);
+  SetupViewportLayersNoScrolls(surface_size);
+  LayerImpl* root = AddScrollableLayer(OuterViewportScrollLayer(),
+                                       scroll_container_size, surface_size);
   LayerImpl* child_layer =
       AddScrollableLayer(root, scroll_container_size, surface_size);
   LayerImpl* grand_child_layer =
       AddScrollableLayer(child_layer, scroll_container_size, surface_size);
 
-  LayerTreeImpl::ViewportPropertyIds viewport_property_ids;
-  viewport_property_ids.inner_scroll = root->scroll_tree_index();
-  host_impl_->active_tree()->SetViewportPropertyIds(viewport_property_ids);
-
   UpdateDrawProperties(host_impl_->active_tree());
   host_impl_->active_tree()->DidBecomeActive();
 
@@ -7890,10 +7887,13 @@
   // of the outer viewport scroll layer.
   LayerImpl* outer_scroll_layer =
       AddScrollableLayer(content_layer, content_size, gfx::Size(1200, 1200));
-  GetScrollNode(outer_scroll_layer)->scrolls_outer_viewport = true;
   LayerImpl* sibling_scroll_layer = AddScrollableLayer(
       content_layer, gfx::Size(600, 600), gfx::Size(1200, 1200));
 
+  GetScrollNode(InnerViewportScrollLayer())
+      ->prevent_viewport_scrolling_from_inner = true;
+  GetScrollNode(OuterViewportScrollLayer())->scrolls_outer_viewport = false;
+  GetScrollNode(outer_scroll_layer)->scrolls_outer_viewport = true;
   auto viewport_property_ids = layer_tree_impl->ViewportPropertyIdsForTesting();
   viewport_property_ids.outer_scroll = outer_scroll_layer->scroll_tree_index();
   layer_tree_impl->SetViewportPropertyIds(viewport_property_ids);
@@ -10338,7 +10338,7 @@
                                   InputHandler::TOUCHSCREEN)
                 .thread);
   EXPECT_EQ(host_impl_->CurrentlyScrollingNode(),
-            host_impl_->ViewportMainScrollNode());
+            host_impl_->OuterViewportScrollNode());
   host_impl_->ScrollEnd(EndState().get());
   EXPECT_EQ(InputHandler::SCROLL_ON_IMPL_THREAD,
             host_impl_
@@ -13388,9 +13388,9 @@
   gfx::Size scroll_content_size = gfx::Size(360, 3800);
   gfx::Size scrollbar_size = gfx::Size(15, 600);
 
-  LayerImpl* root = SetupDefaultRootLayer(viewport_size);
-  LayerImpl* content =
-      AddScrollableLayer(root, viewport_size, scroll_content_size);
+  SetupViewportLayersNoScrolls(viewport_size);
+  LayerImpl* content = AddScrollableLayer(OuterViewportScrollLayer(),
+                                          viewport_size, scroll_content_size);
 
   auto* scrollbar = AddLayer<SolidColorScrollbarLayerImpl>(
       layer_tree_impl, VERTICAL, 10, 0, false);
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index 4af1f35d..34360b8 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -221,21 +221,24 @@
                                      16, 18, 20, 22, 30, 40, 50);
   blur->SetBackdropFilterBounds(backdrop_filter_bounds);
 
-#if defined(OS_WIN) || defined(ARCH_CPU_ARM64)
-  // Windows and ARM64 have 436 pixels off by 1: crbug.com/259915
-  float percentage_pixels_large_error = 1.09f;  // 436px / (200*200)
-  float percentage_pixels_small_error = 0.0f;
-  float average_error_allowed_in_bad_pixels = 1.f;
-  int large_error_allowed = 1;
-  int small_error_allowed = 0;
+  // Skia has various algorithms for clipping by a path, depending on the
+  // available hardware, including MSAA techniques. MSAA results vary
+  // substantially by platform; with 4x MSAA, a difference of 1 sample can
+  // cause up to a 25% color difference!
+  // See http://crbug.com/259915
+  int small_error_threshold = 64;  // 25% of 255.
+  // Allow for ~1 perimeter of the clip path to have a small error.
+  float percentage_pixels_small_error = 100.f * (100*4) / (200*200);
+  int large_error_limit = 128;  // Off by two samples in 4 MSAA.
+  float percentage_pixels_large_or_small_error =
+          1.01f * percentage_pixels_small_error;
+  // Divide average error by 4 since we blur most of the result.
+  float average_error_allowed_in_bad_pixels = small_error_threshold / 4.f;
   pixel_comparator_.reset(new FuzzyPixelComparator(
       true,  // discard_alpha
-      percentage_pixels_large_error, percentage_pixels_small_error,
-      average_error_allowed_in_bad_pixels, large_error_allowed,
-      small_error_allowed));
-#else
-  pixel_comparator_ = std::make_unique<FuzzyPixelOffByOneComparator>(false);
-#endif
+      percentage_pixels_large_or_small_error, percentage_pixels_small_error,
+      average_error_allowed_in_bad_pixels, large_error_limit,
+      small_error_threshold));
 
   RunPixelTest(renderer_type(), background,
                (renderer_type() == RENDERER_SOFTWARE)
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index eb36121..528f725 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -263,16 +263,17 @@
       gfx::SizeF viewport_bounds(bounds_size);
       if (scroll_node->scrolls_inner_viewport) {
         DCHECK_EQ(scroll_node, InnerViewportScrollNode());
-        if (auto* outer_scroll_node = OuterViewportScrollNode()) {
-          // Add offset and bounds contribution of outer viewport.
-          current_offset +=
-              scroll_tree.current_scroll_offset(outer_scroll_node->element_id);
-          gfx::SizeF outer_viewport_bounds(
-              scroll_tree.container_bounds(outer_scroll_node->id));
-          viewport_bounds.SetToMin(outer_viewport_bounds);
-          // The scrolling size is only determined by the outer viewport.
-          scrolling_size = gfx::SizeF(outer_scroll_node->bounds);
-        }
+        auto* outer_scroll_node = OuterViewportScrollNode();
+        DCHECK(outer_scroll_node);
+
+        // Add offset and bounds contribution of outer viewport.
+        current_offset +=
+            scroll_tree.current_scroll_offset(outer_scroll_node->element_id);
+        gfx::SizeF outer_viewport_bounds(
+            scroll_tree.container_bounds(outer_scroll_node->id));
+        viewport_bounds.SetToMin(outer_viewport_bounds);
+        // The scrolling size is only determined by the outer viewport.
+        scrolling_size = gfx::SizeF(outer_scroll_node->bounds);
       } else {
         DCHECK_EQ(scroll_node, OuterViewportScrollNode());
         auto* inner_scroll_node = InnerViewportScrollNode();
@@ -367,6 +368,7 @@
   if (!InnerViewportScrollNode())
     return;
 
+  DCHECK(OuterViewportScrollNode());
   ViewportAnchor anchor(InnerViewportScrollNode(), OuterViewportScrollNode(),
                         this);
 
@@ -404,25 +406,24 @@
   // Adjust the outer viewport container as well, since adjusting only the
   // inner may cause its bounds to exceed those of the outer, causing scroll
   // clamping.
-  if (auto* outer_scroll = OuterViewportScrollNode()) {
-    gfx::Vector2dF scaled_bounds_delta =
-        gfx::ScaleVector2d(bounds_delta, 1.f / min_page_scale_factor());
+  gfx::Vector2dF scaled_bounds_delta =
+      gfx::ScaleVector2d(bounds_delta, 1.f / min_page_scale_factor());
 
-    property_trees->SetOuterViewportContainerBoundsDelta(scaled_bounds_delta);
-    // outer_viewport_container_bounds_delta and
-    // inner_viewport_scroll_bounds_delta are the same thing.
-    DCHECK_EQ(scaled_bounds_delta,
-              property_trees->inner_viewport_scroll_bounds_delta());
+  property_trees->SetOuterViewportContainerBoundsDelta(scaled_bounds_delta);
+  // outer_viewport_container_bounds_delta and
+  // inner_viewport_scroll_bounds_delta are the same thing.
+  DCHECK_EQ(scaled_bounds_delta,
+            property_trees->inner_viewport_scroll_bounds_delta());
 
-    if (auto* outer_clip_node = OuterViewportClipNode()) {
-      float adjusted_container_height =
-          outer_scroll->container_bounds.height() + scaled_bounds_delta.y();
-      outer_clip_node->clip.set_height(adjusted_container_height);
-    }
-
-    anchor.ResetViewportToAnchoredPosition();
+  if (auto* outer_clip_node = OuterViewportClipNode()) {
+    float adjusted_container_height =
+        OuterViewportScrollNode()->container_bounds.height() +
+        scaled_bounds_delta.y();
+    outer_clip_node->clip.set_height(adjusted_container_height);
   }
 
+  anchor.ResetViewportToAnchoredPosition();
+
   property_trees->clip_tree.set_needs_update(true);
   property_trees->full_tree_damaged = true;
   set_needs_update_draw_properties();
@@ -441,11 +442,12 @@
   gfx::ScrollOffset offset;
   const auto& scroll_tree = property_trees()->scroll_tree;
 
-  if (auto* inner_scroll = InnerViewportScrollNode())
+  if (auto* inner_scroll = InnerViewportScrollNode()) {
     offset += scroll_tree.current_scroll_offset(inner_scroll->element_id);
-
-  if (auto* outer_scroll = OuterViewportScrollNode())
-    offset += scroll_tree.current_scroll_offset(outer_scroll->element_id);
+    DCHECK(OuterViewportScrollNode());
+    offset += scroll_tree.current_scroll_offset(
+        OuterViewportScrollNode()->element_id);
+  }
 
   return offset;
 }
@@ -619,12 +621,12 @@
 }
 
 void LayerTreeImpl::HandleTickmarksVisibilityChange() {
-  if (!host_impl_->ViewportMainScrollNode())
+  if (!host_impl_->OuterViewportScrollNode())
     return;
 
   ScrollbarAnimationController* controller =
       host_impl_->ScrollbarAnimationControllerForElementId(
-          host_impl_->ViewportMainScrollNode()->element_id);
+          host_impl_->OuterViewportScrollNode()->element_id);
 
   if (!controller)
     return;
@@ -1123,7 +1125,7 @@
       host_impl_->FlashAllScrollbars(true);
       return;
     }
-    if (auto* scroll_node = host_impl_->ViewportMainScrollNode()) {
+    if (auto* scroll_node = host_impl_->OuterViewportScrollNode()) {
       if (ScrollbarAnimationController* controller =
               host_impl_->ScrollbarAnimationControllerForElementId(
                   scroll_node->element_id))
@@ -1221,10 +1223,10 @@
 
 gfx::Rect LayerTreeImpl::RootScrollLayerDeviceViewportBounds() const {
   const ScrollNode* root_scroll_node = OuterViewportScrollNode();
-  if (!root_scroll_node)
-    root_scroll_node = InnerViewportScrollNode();
-  if (!root_scroll_node)
+  if (!root_scroll_node) {
+    DCHECK(!InnerViewportScrollNode());
     return gfx::Rect();
+  }
   return MathUtil::MapEnclosingClippedRect(
       property_trees()->transform_tree.ToScreen(root_scroll_node->transform_id),
       gfx::Rect(root_scroll_node->bounds));
@@ -1454,10 +1456,10 @@
 
 gfx::SizeF LayerTreeImpl::ScrollableSize() const {
   auto* scroll_node = OuterViewportScrollNode();
-  if (!scroll_node)
-    scroll_node = InnerViewportScrollNode();
-  if (!scroll_node)
+  if (!scroll_node) {
+    DCHECK(!InnerViewportScrollNode());
     return gfx::SizeF();
+  }
   const auto& scroll_tree = property_trees()->scroll_tree;
   auto size = scroll_tree.scroll_bounds(scroll_node->id);
   size.SetToMax(gfx::SizeF(scroll_tree.container_bounds(scroll_node->id)));
diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h
index efe9c9c..2b5f0f6 100644
--- a/cc/trees/layer_tree_impl.h
+++ b/cc/trees/layer_tree_impl.h
@@ -281,6 +281,9 @@
     return !presentation_callbacks_.empty();
   }
 
+  // The following viewport related property nodes will only ever be set on the
+  // main-frame's renderer (i.e. OOPIF and UI compositors will not have these
+  // set.
   using ViewportPropertyIds = LayerTreeHost::ViewportPropertyIds;
   void SetViewportPropertyIds(const ViewportPropertyIds& ids);
 
diff --git a/chrome/android/features/autofill_assistant/BUILD.gn b/chrome/android/features/autofill_assistant/BUILD.gn
index 53b7688..2aea5764 100644
--- a/chrome/android/features/autofill_assistant/BUILD.gn
+++ b/chrome/android/features/autofill_assistant/BUILD.gn
@@ -210,6 +210,7 @@
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantBottomsheetTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDetailsUiTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantDirectActionHandlerTest.java",
+    "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFormActionTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantHeaderUiTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantInfoBoxUiTest.java",
     "javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantOverlayUiTest.java",
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_checkbox.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_checkbox.xml
new file mode 100644
index 0000000..3649045
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_checkbox.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<LinearLayout android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Identifiers in this file match those in autofill_assistant_form_checkbox.xml -->
+    <LinearLayout
+        android:id="@+id/descriptions"
+        android:layout_width="0dp"
+        android:layout_weight="1.0"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:gravity="fill_horizontal">
+        <TextView
+            android:id="@+id/label"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/TextAppearance.BlackTitle2"/>
+        <TextView
+            android:id="@+id/description_line_1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/TextAppearance.BlackCaption"/>
+        <TextView
+            android:id="@+id/description_line_2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/TextAppearance.BlackCaption"/>
+    </LinearLayout>
+    <!-- TODO(806868) Remove the hard-coded padding and align properly. -->
+    <CheckBox
+        android:id="@+id/checkbox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:paddingEnd="26.5dp"
+        android:layout_gravity="center_vertical"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter.xml
index 7ab8ab9..3101e1b 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter.xml
@@ -22,13 +22,17 @@
             android:layout_height="wrap_content"
             android:textAppearance="@style/TextAppearance.BlackTitle2"/>
         <TextView
-            android:id="@+id/subtext"
+            android:id="@+id/description_line_1"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:textAppearance="@style/TextAppearance.BlackCaption"
-            android:visibility="gone"/>
+            android:textAppearance="@style/TextAppearance.BlackCaption"/>
+        <TextView
+            android:id="@+id/description_line_2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/TextAppearance.BlackCaption"/>
     </LinearLayout>
-    <ImageView
+    <org.chromium.ui.widget.ChromeImageView
         android:id="@+id/decrease_button"
         android:layout_width="32dp"
         android:layout_height="32dp"
@@ -43,7 +47,7 @@
         android:minWidth="20dp"
         android:gravity="center"
         android:textAppearance="@style/TextAppearance.BlackTitle2"/>
-    <ImageView
+    <org.chromium.ui.widget.ChromeImageView
         android:id="@+id/increase_button"
         android:layout_width="32dp"
         android:layout_height="32dp"
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter_input.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter_input.xml
index 4e4ebe4..c15d3eff 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter_input.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_counter_input.xml
@@ -36,11 +36,11 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textAppearance="@style/TextAppearance.BlackCaption"/>
-        <ImageView
+        <org.chromium.ui.widget.ChromeImageView
             android:id="@+id/chevron"
             android:layout_width="16dp"
             android:layout_height="16dp"
-            android:tint="@color/default_text_color_secondary_list"
+            android:tint="@color/control_normal_color"
             app:srcCompat="@drawable/ic_expand_more_black_24dp"
             tools:ignore="ContentDescription"/>
     </LinearLayout>
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_radiobutton.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_radiobutton.xml
new file mode 100644
index 0000000..8730de70
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_radiobutton.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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. -->
+
+<!-- Identifiers in this file match those in autofill_assistant_form_radiobutton.xml -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/descriptions"
+    android:layout_width="0dp"
+    android:layout_weight="1.0"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:gravity="fill_horizontal">
+
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.BlackTitle2"/>
+    <TextView
+        android:id="@+id/description_line_1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.BlackCaption"/>
+    <TextView
+        android:id="@+id/description_line_2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.BlackCaption"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_selection_input.xml b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_selection_input.xml
index cc2c0180..53147cd0c9 100644
--- a/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_selection_input.xml
+++ b/chrome/android/features/autofill_assistant/java/res/layout/autofill_assistant_form_selection_input.xml
@@ -3,11 +3,10 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:orientation="vertical">
     <TextView
         android:id="@+id/label"
@@ -16,13 +15,23 @@
         android:layout_marginBottom="4dp"
         android:textAppearance="@style/TextAppearance.BlackButtonText"/>
 
+    <LinearLayout
+        android:id="@+id/checkbox_list"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:visibility="gone">
+        <!-- Choices are created in code. -->
+    </LinearLayout>
+
     <org.chromium.chrome.browser.autofill_assistant.user_data.AssistantChoiceList
-        android:id="@+id/choice_list"
+        android:id="@+id/radiobutton_list"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         app:can_add_items="false"
         app:row_spacing="0dp"
-        app:column_spacing="8dp">
+        app:column_spacing="8dp"
+        android:visibility="gone">
         <!-- Choices are created in code. -->
     </org.chromium.chrome.browser.autofill_assistant.user_data.AssistantChoiceList>
 </LinearLayout>
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCoordinator.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCoordinator.java
index d9f8d5e..96b0d91 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCoordinator.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCoordinator.java
@@ -36,6 +36,7 @@
         mModel.getInputsModel().addObserver(new AbstractListObserver<Void>() {
             @Override
             public void onDataSetChanged() {
+                // TODO(b/144690738) This creates a new instance of the UI on every notification...
                 for (int i = 0; i < mView.getChildCount(); i++) {
                     mView.getChildAt(i).setVisibility(View.GONE);
                 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounter.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounter.java
index 37b70003..dd09b27 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounter.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounter.java
@@ -6,19 +6,25 @@
 
 abstract class AssistantFormCounter {
     private final String mLabel;
-    private final String mSubtext;
+    private final String mDescriptionLine1;
+    private final String mDescriptionLine2;
 
-    private AssistantFormCounter(String label, String subtext) {
+    private AssistantFormCounter(String label, String descriptionLine1, String descriptionLine2) {
         mLabel = label;
-        mSubtext = subtext;
+        mDescriptionLine1 = descriptionLine1;
+        mDescriptionLine2 = descriptionLine2;
     }
 
     String getLabel() {
         return mLabel;
     }
 
-    String getSubtext() {
-        return mSubtext;
+    String getDescriptionLine1() {
+        return mDescriptionLine1;
+    }
+
+    String getDescriptionLine2() {
+        return mDescriptionLine2;
     }
 
     abstract int getValue();
@@ -31,13 +37,16 @@
 
     abstract void increaseValue();
 
-    static AssistantFormCounter create(String label, String subtext, int initialValue, int minValue,
-            int maxValue, int[] allowedValues) {
+    static AssistantFormCounter create(String label, String descriptionLine1,
+            String descriptionLine2, int initialValue, int minValue, int maxValue,
+            int[] allowedValues) {
         if (allowedValues.length > 0) {
-            return new FiniteCounter(label, subtext, initialValue, allowedValues);
+            return new FiniteCounter(
+                    label, descriptionLine1, descriptionLine2, initialValue, allowedValues);
         }
 
-        return new BoundedCounter(label, subtext, initialValue, minValue, maxValue);
+        return new BoundedCounter(
+                label, descriptionLine1, descriptionLine2, initialValue, minValue, maxValue);
     }
 
     /** A counter whose value is limited by a min and max value. */
@@ -46,9 +55,9 @@
         private final int mMaxValue;
         private int mValue;
 
-        private BoundedCounter(
-                String label, String subtext, int initialValue, int minValue, int maxValue) {
-            super(label, subtext);
+        private BoundedCounter(String label, String descriptionLine1, String descriptionLine2,
+                int initialValue, int minValue, int maxValue) {
+            super(label, descriptionLine1, descriptionLine2);
             mMinValue = minValue;
             mMaxValue = maxValue;
             mValue = initialValue;
@@ -85,8 +94,9 @@
         private final int[] mAllowedValues;
         private int mValueIndex;
 
-        private FiniteCounter(String label, String subtext, int initialValue, int[] allowedValues) {
-            super(label, subtext);
+        private FiniteCounter(String label, String descriptionLine1, String descriptionLine2,
+                int initialValue, int[] allowedValues) {
+            super(label, descriptionLine1, descriptionLine2);
             mAllowedValues = allowedValues;
 
             for (int i = 0; i < mAllowedValues.length; i++) {
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java
index 684acbd..9044b7550 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormCounterInput.java
@@ -16,6 +16,7 @@
 import android.widget.TextView;
 
 import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
 
 import java.util.ArrayList;
@@ -39,7 +40,8 @@
     private static class CounterViewHolder {
         private final View mView;
         private final TextView mLabelView;
-        private final TextView mSubtextView;
+        private final TextView mDescriptionLine1View;
+        private final TextView mDescriptionLine2View;
         private final TextView mValueView;
         private final View mDecreaseButtonView;
         private final View mIncreaseButtonView;
@@ -48,7 +50,8 @@
             mView = LayoutInflater.from(context).inflate(
                     R.layout.autofill_assistant_form_counter, /*root= */ null);
             mLabelView = mView.findViewById(R.id.label);
-            mSubtextView = mView.findViewById(R.id.subtext);
+            mDescriptionLine1View = mView.findViewById(R.id.description_line_1);
+            mDescriptionLine2View = mView.findViewById(R.id.description_line_2);
             mValueView = mView.findViewById(R.id.value);
             mDecreaseButtonView = mView.findViewById(R.id.decrease_button);
             mIncreaseButtonView = mView.findViewById(R.id.increase_button);
@@ -191,10 +194,13 @@
             AssistantFormCounter counter = counters.get(i);
             CounterViewHolder view = views.get(i);
 
-            if (!counter.getSubtext().isEmpty()) {
-                view.mSubtextView.setVisibility(View.VISIBLE);
-                view.mSubtextView.setText(counter.getSubtext());
-            }
+            // TODO(b/144402029) Add support for text links.
+            AssistantTextUtils.applyVisualAppearanceTags(
+                    view.mDescriptionLine1View, counter.getDescriptionLine1(), null);
+            AssistantTextUtils.applyVisualAppearanceTags(
+                    view.mDescriptionLine2View, counter.getDescriptionLine2(), null);
+            hideIfEmpty(view.mDescriptionLine1View);
+            hideIfEmpty(view.mDescriptionLine2View);
 
             updateLabelAndValue(counter, view);
 
@@ -254,4 +260,8 @@
 
         mDelegate.onCounterChanged(counterIndex, counter.getValue());
     }
+
+    private void hideIfEmpty(TextView view) {
+        view.setVisibility(view.length() == 0 ? View.GONE : View.VISIBLE);
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormInput.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormInput.java
index f64eb0db..2245876d 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormInput.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormInput.java
@@ -45,16 +45,18 @@
     }
 
     @CalledByNative
-    private static AssistantFormCounter createCounter(String label, String subtext,
-            int initialValue, int minValue, int maxValue, int[] allowedValues) {
-        return AssistantFormCounter.create(
-                label, subtext, initialValue, minValue, maxValue, allowedValues);
+    private static AssistantFormCounter createCounter(String label, String descriptionLine1,
+            String descriptionLine2, int initialValue, int minValue, int maxValue,
+            int[] allowedValues) {
+        return AssistantFormCounter.create(label, descriptionLine1, descriptionLine2, initialValue,
+                minValue, maxValue, allowedValues);
     }
 
     @CalledByNative
-    private static AssistantFormSelectionChoice createChoice(
-            String label, boolean initiallySelected) {
-        return new AssistantFormSelectionChoice(label, initiallySelected);
+    private static AssistantFormSelectionChoice createChoice(String label, String descriptionLine1,
+            String descriptionLine2, boolean initiallySelected) {
+        return new AssistantFormSelectionChoice(
+                label, descriptionLine1, descriptionLine2, initiallySelected);
     }
 
     @CalledByNative
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionChoice.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionChoice.java
index f3c7fa0..3a3907cc 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionChoice.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionChoice.java
@@ -6,10 +6,15 @@
 
 class AssistantFormSelectionChoice {
     private final String mLabel;
+    private final String mDescriptionLine1;
+    private final String mDescriptionLine2;
     private final boolean mIsInitiallySelected;
 
-    public AssistantFormSelectionChoice(String label, boolean isInitiallySelected) {
+    public AssistantFormSelectionChoice(String label, String descriptionLine1,
+            String descriptionLine2, boolean isInitiallySelected) {
         mLabel = label;
+        mDescriptionLine1 = descriptionLine1;
+        mDescriptionLine2 = descriptionLine2;
         mIsInitiallySelected = isInitiallySelected;
     }
 
@@ -17,6 +22,14 @@
         return mLabel;
     }
 
+    public String getDescriptionLine1() {
+        return mDescriptionLine1;
+    }
+
+    public String getDescriptionLine2() {
+        return mDescriptionLine2;
+    }
+
     public boolean isInitiallySelected() {
         return mIsInitiallySelected;
     }
diff --git a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionInput.java b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionInput.java
index 11d8416..47d8dce4 100644
--- a/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionInput.java
+++ b/chrome/android/features/autofill_assistant/java/src/org/chromium/chrome/browser/autofill_assistant/form/AssistantFormSelectionInput.java
@@ -5,14 +5,14 @@
 package org.chromium.chrome.browser.autofill_assistant.form;
 
 import android.content.Context;
-import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.CheckBox;
 import android.widget.TextView;
 
-import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.chrome.browser.autofill_assistant.AssistantTextUtils;
 import org.chromium.chrome.browser.autofill_assistant.user_data.AssistantChoiceList;
 
 import java.util.List;
@@ -38,7 +38,8 @@
 
     @Override
     public View createView(Context context, ViewGroup parent) {
-        ViewGroup root = (ViewGroup) LayoutInflater.from(context).inflate(
+        LayoutInflater inflater = LayoutInflater.from(context);
+        ViewGroup root = (ViewGroup) inflater.inflate(
                 R.layout.autofill_assistant_form_selection_input, parent,
                 /* attachToRoot= */ false);
         TextView label = root.findViewById(org.chromium.chrome.autofill_assistant.R.id.label);
@@ -48,25 +49,61 @@
             label.setText(mLabel);
         }
 
-        AssistantChoiceList choiceList = root.findViewById(R.id.choice_list);
-        choiceList.setAllowMultipleChoices(mAllowMultipleChoices);
+        if (mChoices.isEmpty()) {
+            return root;
+        }
+
+        ViewGroup checkboxList = root.findViewById(R.id.checkbox_list);
+        AssistantChoiceList radiobuttonList = root.findViewById(R.id.radiobutton_list);
         for (int i = 0; i < mChoices.size(); i++) {
             AssistantFormSelectionChoice choice = mChoices.get(i);
 
-            TextView choiceView = new TextView(context);
-            ApiCompatibilityUtils.setTextAppearance(
-                    choiceView, R.style.TextAppearance_BlackCaptionDefault);
-            choiceView.setGravity(Gravity.CENTER_VERTICAL);
-            choiceView.setText(choice.getLabel());
 
             int index = i; // needed for the lambda.
-            choiceList.addItem(choiceView, /* hasEditButton= */ false,
-                    (isChecked)
-                            -> mDelegate.onChoiceSelectionChanged(index, isChecked),
-                    /* itemEditedListener= */ null);
+            View choiceView;
+            if (mAllowMultipleChoices) {
+                choiceView =
+                        inflater.inflate(R.layout.autofill_assistant_form_checkbox, checkboxList);
 
-            choiceList.setChecked(choiceView, choice.isInitiallySelected());
+                CheckBox checkBox = choiceView.findViewById(R.id.checkbox);
+                checkBox.setOnCheckedChangeListener(
+                        (compoundButton,
+                                isChecked) -> mDelegate.onChoiceSelectionChanged(index, isChecked));
+                choiceView.findViewById(R.id.descriptions)
+                        .setOnClickListener(
+                                unusedView -> checkBox.setChecked(!checkBox.isChecked()));
+                checkBox.setChecked(choice.isInitiallySelected());
+            } else {
+                choiceView = inflater.inflate(R.layout.autofill_assistant_form_radiobutton, null);
+
+                radiobuttonList.addItem(choiceView, /* hasEditButton= */ false,
+                        (isChecked)
+                                -> mDelegate.onChoiceSelectionChanged(index, isChecked),
+                        /* itemEditedListener= */ null);
+                radiobuttonList.setChecked(choiceView, choice.isInitiallySelected());
+            }
+
+            TextView choiceLabel = choiceView.findViewById(R.id.label);
+            TextView descriptionLine1 = choiceView.findViewById(R.id.description_line_1);
+            TextView descriptionLine2 = choiceView.findViewById(R.id.description_line_2);
+            AssistantTextUtils.applyVisualAppearanceTags(choiceLabel, choice.getLabel(), null);
+            AssistantTextUtils.applyVisualAppearanceTags(
+                    descriptionLine1, choice.getDescriptionLine1(), null);
+            AssistantTextUtils.applyVisualAppearanceTags(
+                    descriptionLine2, choice.getDescriptionLine2(), null);
+            hideIfEmpty(choiceLabel);
+            hideIfEmpty(descriptionLine1);
+            hideIfEmpty(descriptionLine2);
+        }
+        if (mAllowMultipleChoices) {
+            checkboxList.setVisibility(View.VISIBLE);
+        } else {
+            radiobuttonList.setVisibility(View.VISIBLE);
         }
         return root;
     }
+
+    private void hideIfEmpty(TextView view) {
+        view.setVisibility(view.length() == 0 ? View.GONE : View.VISIBLE);
+    }
 }
diff --git a/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFormActionTest.java b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFormActionTest.java
new file mode 100644
index 0000000..8c418d9
--- /dev/null
+++ b/chrome/android/features/autofill_assistant/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantFormActionTest.java
@@ -0,0 +1,173 @@
+// 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.
+
+package org.chromium.chrome.browser.autofill_assistant;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.hasSibling;
+import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.hamcrest.Matchers.allOf;
+
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.startAutofillAssistant;
+import static org.chromium.chrome.browser.autofill_assistant.AutofillAssistantUiTestUtil.waitUntilViewMatchesCondition;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.DisableIf;
+import org.chromium.chrome.autofill_assistant.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.autofill_assistant.proto.ActionProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ChipProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ChipType;
+import org.chromium.chrome.browser.autofill_assistant.proto.CounterInputProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.FormInputProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.FormProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.PromptProto.Choice;
+import org.chromium.chrome.browser.autofill_assistant.proto.SelectionInputProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.ShowFormProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto;
+import org.chromium.chrome.browser.autofill_assistant.proto.SupportedScriptProto.PresentationProto;
+import org.chromium.chrome.browser.customtabs.CustomTabActivityTestRule;
+import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Tests autofill assistant bottomsheet.
+ */
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@RunWith(ChromeJUnit4ClassRunner.class)
+public class AutofillAssistantFormActionTest {
+    @Rule
+    public CustomTabActivityTestRule mTestRule = new CustomTabActivityTestRule();
+
+    private static final String TEST_PAGE = "/components/test/data/autofill_assistant/html/"
+            + "autofill_assistant_target_website.html";
+
+    @Before
+    public void setUp() {
+        AutofillAssistantPreferencesUtil.setInitialPreferences(true);
+        mTestRule.startCustomTabActivityWithIntent(CustomTabsTestUtils.createMinimalCustomTabIntent(
+                InstrumentationRegistry.getTargetContext(),
+                mTestRule.getTestServer().getURL(TEST_PAGE)));
+        mTestRule.getActivity().getScrim().disableAnimationForTesting(true);
+    }
+
+    @Test
+    @MediumTest
+    @DisableIf.Build(sdk_is_less_than = 21)
+    public void testFormAction() {
+        ArrayList<ActionProto> list = new ArrayList<>();
+        // FromProto.Builder, extracted to avoid excessive line widths.
+        FormProto.Builder formProto =
+                FormProto.newBuilder()
+                        .addInputs(FormInputProto.newBuilder().setCounter(
+                                CounterInputProto.newBuilder()
+                                        .addCounters(CounterInputProto.Counter.newBuilder()
+                                                             .setMinValue(1)
+                                                             .setMaxValue(9)
+                                                             .setLabel("Counter 1")
+                                                             .setDescriptionLine1("$34.00 per tick")
+                                                             .setDescriptionLine2(
+                                                                     "<link1>Details</link1>"))
+                                        .addCounters(
+                                                CounterInputProto.Counter.newBuilder()
+                                                        .setMinValue(1)
+                                                        .setMaxValue(9)
+                                                        .setLabel("Counter 2")
+                                                        .setDescriptionLine1("$387.00 per tick"))
+                                        .setMinimizedCount(1)
+                                        .setMinCountersSum(2)
+                                        .setMinimizeText("Minimize")
+                                        .setExpandText("Expand")))
+                        .addInputs(FormInputProto.newBuilder().setSelection(
+                                SelectionInputProto.newBuilder()
+                                        .addChoices(
+                                                SelectionInputProto.Choice.newBuilder()
+                                                        .setLabel("Choice 1")
+                                                        .setDescriptionLine1("$10.00 per choice")
+                                                        .setDescriptionLine2(
+                                                                "<link1>Details</link1>"))
+                                        .setAllowMultiple(true)))
+                        .addInputs(FormInputProto.newBuilder().setCounter(
+                                CounterInputProto.newBuilder().addCounters(
+                                        CounterInputProto.Counter.newBuilder()
+                                                .setMinValue(1)
+                                                .setMaxValue(9)
+                                                .setLabel("Counter 3")
+                                                .setDescriptionLine1("$20.00 per tick")
+                                                .setDescriptionLine2("<link1>Details</link1>"))));
+        // FormAction.
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setShowForm(ShowFormProto.newBuilder()
+                                              .setChip(ChipProto.newBuilder()
+                                                               .setType(ChipType.HIGHLIGHTED_ACTION)
+                                                               .setText("Continue"))
+                                              .setForm(formProto))
+                         .build());
+
+        // Prompt.
+        list.add((ActionProto) ActionProto.newBuilder()
+                         .setPrompt(PromptProto.newBuilder()
+                                            .setMessage("Finished")
+                                            .addChoices(Choice.newBuilder().setChip(
+                                                    ChipProto.newBuilder()
+                                                            .setType(ChipType.DONE_ACTION)
+                                                            .setText("End"))))
+                         .build());
+
+        AutofillAssistantTestScript script = new AutofillAssistantTestScript(
+                (SupportedScriptProto) SupportedScriptProto.newBuilder()
+                        .setPath("autofill_assistant_target_website.html")
+                        .setPresentation(PresentationProto.newBuilder().setAutostart(true).setChip(
+                                ChipProto.newBuilder().setText("Autostart")))
+                        .build(),
+                list);
+
+        AutofillAssistantTestService testService =
+                new AutofillAssistantTestService(Collections.singletonList(script));
+        startAutofillAssistant(mTestRule.getActivity(), testService);
+
+        waitUntilViewMatchesCondition(withText("Continue"), isCompletelyDisplayed());
+        // TODO(b/144690738) Remove the isDisplayed() condition.
+        onView(allOf(isDisplayed(), withId(R.id.increase_button),
+                       hasSibling(hasDescendant(withText("Counter 1")))))
+                .perform(click());
+
+        onView(allOf(isDisplayed(), withId(R.id.expand_label))).perform(click());
+        onView(allOf(isDisplayed(), withId(R.id.increase_button),
+                       hasSibling(hasDescendant(withText("Counter 3")))))
+                .perform(click());
+
+        onView(allOf(isDisplayed(), withId(R.id.checkbox),
+                       hasSibling(hasDescendant(withText("Choice 1")))))
+                .perform(click());
+        onView(allOf(isDisplayed(), withId(R.id.increase_button),
+                       hasSibling(hasDescendant(withText("Counter 2")))))
+                .perform(click());
+
+        waitUntilViewMatchesCondition(withText("Continue"), isEnabled());
+        onView(withText("Continue")).perform(click());
+
+        waitUntilViewMatchesCondition(withText("End"), isCompletelyDisplayed());
+        // TODO(806868): check that the values were correctly received by the native delegate.
+    }
+}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestrator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestrator.java
index 9b57bf55..e4c26cb 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestrator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestrator.java
@@ -4,14 +4,19 @@
 
 package org.chromium.chrome.browser.tasks.tab_management.suggestions;
 
+import org.chromium.base.Log;
 import org.chromium.base.ObserverList;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.Destroyable;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Represents the entry point for the TabSuggestions component. Responsible for
@@ -19,7 +24,7 @@
  */
 public class TabSuggestionsOrchestrator implements TabSuggestions, Destroyable {
     public static final String TAB_SUGGESTIONS_UMA_PREFIX = "TabSuggestionsOrchestrator";
-    private static final String TAG = "TabSuggestionsDetailed";
+    private static final String TAG = "TabSuggestDetailed";
     private static final int MIN_CLOSE_SUGGESTIONS_THRESHOLD = 3;
 
     protected TabContextObserver mTabContextObserver;
@@ -133,8 +138,43 @@
     }
 
     public void onTabSuggestionFeedback(TabSuggestionFeedback tabSuggestionFeedback) {
+        if (tabSuggestionFeedback == null) {
+            Log.e(TAG, "TabSuggestionFeedback is null");
+            return;
+        }
         // Record TabSuggestionFeedback for testing purposes
         mTabSuggestionFeedback = tabSuggestionFeedback;
-        // TODO(crbug.com/1026068) log tab suggestion feedback
+
+        if (tabSuggestionFeedback.tabSuggestionResponse
+                == TabSuggestionFeedback.TabSuggestionResponse.NOT_CONSIDERED) {
+            RecordUserAction.record("TabsSuggestions.Close.SuggestionsReview.Dismissed");
+        } else {
+            RecordUserAction.record("TabsSuggestions.Close.SuggestionsReview.Accepted");
+            if (tabSuggestionFeedback.tabSuggestionResponse
+                    == TabSuggestionFeedback.TabSuggestionResponse.ACCEPTED) {
+                RecordUserAction.record("TabsSuggestions.Close.Accepted");
+            } else {
+                RecordUserAction.record("TabsSuggestions.Close.Dismissed");
+            }
+        }
+
+        Set<Integer> suggestedTabIds = new HashSet<>();
+        for (TabContext.TabInfo tabInfo : tabSuggestionFeedback.tabSuggestion.getTabsInfo()) {
+            suggestedTabIds.add(tabInfo.id);
+        }
+
+        int numSelectFromSuggestion = 0;
+        int numSelectOutsideSuggestion = 0;
+        for (int selectedTabId : tabSuggestionFeedback.selectedTabIds) {
+            if (suggestedTabIds.contains(selectedTabId)) {
+                numSelectFromSuggestion++;
+            } else {
+                numSelectOutsideSuggestion++;
+            }
+        }
+        int numChanged = tabSuggestionFeedback.tabSuggestion.getTabsInfo().size()
+                - numSelectFromSuggestion + numSelectOutsideSuggestion;
+        RecordHistogram.recordCount100Histogram(
+                "TabsSuggestions.Close.NumSuggestionsChanged", numChanged);
     }
 }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java
index 990aa863..0d1804f 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/suggestions/TabSuggestionsOrchestratorTest.java
@@ -13,6 +13,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
@@ -24,6 +25,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.Callback;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelFilter;
@@ -80,6 +83,14 @@
                 .addTabModelFilterObserver(any(TabModelObserver.class));
         doReturn(mTabModelFilter).when(mTabModelFilterProvider).getCurrentTabModelFilter();
         doReturn(null).when(mTabModelFilter).getRelatedTabList(anyInt());
+        RecordUserAction.setDisabledForTests(true);
+        RecordHistogram.setDisabledForTests(true);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        RecordUserAction.setDisabledForTests(false);
+        RecordHistogram.setDisabledForTests(false);
     }
 
     @Test
@@ -150,7 +161,9 @@
             @Override
             public void onNewSuggestion(List<TabSuggestion> tabSuggestions,
                     Callback<TabSuggestionFeedback> tabSuggestionFeedback) {
-                tabSuggestionFeedback.onResult(new TabSuggestionFeedback(null,
+                TabSuggestion tabSuggestion = new TabSuggestion(
+                        Arrays.asList(TabContext.TabInfo.createFromTab(sTabs[0])), 0, "");
+                tabSuggestionFeedback.onResult(new TabSuggestionFeedback(tabSuggestion,
                         TabSuggestionFeedback.TabSuggestionResponse.ACCEPTED, Arrays.asList(1), 1));
             }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
index 1a4494c..c2a9bde 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterAccessibilityTest.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.history.HistoryTestUtils.checkAdapterContents;
 
+import android.os.Build;
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -31,7 +32,8 @@
  * turned on (HistoryManager::isScrollToLoadDisabled() == true).
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/1023426")
+@DisableIf.Build(supported_abis_includes = "arm64-v8a",
+        sdk_is_greater_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/1023426")
 public class HistoryAdapterAccessibilityTest {
     public static final int PAGING = 2;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
index ff596ab..3247b9de 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/history/HistoryAdapterTest.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.history.HistoryTestUtils.checkAdapterContents;
 
+import android.os.Build;
 import android.support.test.filters.SmallTest;
 
 import org.junit.Assert;
@@ -30,7 +31,8 @@
  * Tests for the {@link HistoryAdapter}.
  */
 @RunWith(ChromeJUnit4ClassRunner.class)
-@DisableIf.Build(supported_abis_includes = "arm64-v8a", message = "crbug.com/1023426")
+@DisableIf.Build(supported_abis_includes = "arm64-v8a",
+        sdk_is_greater_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/1023426")
 public class HistoryAdapterTest {
     private StubbedHistoryProvider mHistoryProvider;
     private HistoryAdapter mAdapter;
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 7498f243e..e02c26dd 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -422,6 +422,7 @@
     "//chrome/common:mojo_bindings",
     "//chrome/common:offline_page_auto_fetcher_mojom",
     "//chrome/common/media_router/mojom:media_router",
+    "//chrome/test/data:web_ui_test_bindings",
     "//components/autofill/content/common/mojom",
     "//components/contextual_search/content/common/mojom",
     "//components/data_reduction_proxy/core/common:interfaces",
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index 96c3c51..0b8bb0b 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -1051,8 +1051,10 @@
               Java_AssistantFormInput_createCounter(
                   env,
                   base::android::ConvertUTF8ToJavaString(env, counter.label()),
-                  base::android::ConvertUTF8ToJavaString(env,
-                                                         counter.subtext()),
+                  base::android::ConvertUTF8ToJavaString(
+                      env, counter.description_line_1()),
+                  base::android::ConvertUTF8ToJavaString(
+                      env, counter.description_line_2()),
                   counter.initial_value(), counter.min_value(),
                   counter.max_value(),
                   base::android::ToJavaIntArray(env, allowed_values)));
@@ -1085,6 +1087,10 @@
               Java_AssistantFormInput_createChoice(
                   env,
                   base::android::ConvertUTF8ToJavaString(env, choice.label()),
+                  base::android::ConvertUTF8ToJavaString(
+                      env, choice.description_line_1()),
+                  base::android::ConvertUTF8ToJavaString(
+                      env, choice.description_line_2()),
                   choice.selected()));
         }
 
diff --git a/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc b/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc
index 81811a94..34ccaa77 100644
--- a/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc
+++ b/chrome/browser/android/explore_sites/explore_sites_fetcher_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/test/bind_test_util.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/mock_callback.h"
-#include "base/test/mock_entropy_provider.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_mock_time_task_runner.h"
@@ -107,7 +106,6 @@
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::SingleThreadTaskEnvironment::MainThreadType::IO,
       base::test::SingleThreadTaskEnvironment::TimeSource::MOCK_TIME};
-  std::unique_ptr<base::FieldTrialList> field_trial_list_;
   base::test::ScopedFeatureList scoped_feature_list_;
   std::unique_ptr<base::HistogramTester> histogram_tester_;
 };
@@ -131,8 +129,6 @@
         EXPECT_TRUE(request.url.is_valid() && !request.url.is_empty());
         last_resource_request = request;
       }));
-  field_trial_list_ = std::make_unique<base::FieldTrialList>(
-      std::make_unique<base::MockEntropyProvider>());
 }
 
 ExploreSitesRequestStatus ExploreSitesFetcherTest::RunFetcherWithNetError(
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 2ff7eae4..1090614 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -20,6 +20,7 @@
 #include "base/i18n/character_encoding.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/path_service.h"
@@ -3307,6 +3308,9 @@
   // Apply caption style from preferences if system caption style is undefined.
   if (!style && base::FeatureList::IsEnabled(features::kCaptionSettings)) {
     style = pref_names_util::GetCaptionStyleFromPrefs(prefs);
+
+    base::UmaHistogramBoolean("Accessibility.CaptionSettingsLoadedFromPrefs",
+                              style.has_value());
   }
 
   if (style) {
diff --git a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
index 7df556b..80e4dea 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -147,7 +147,6 @@
 #include "ui/message_center/public/cpp/notification.h"
 #include "ui/message_center/public/cpp/notification_types.h"
 #include "ui/views/widget/widget.h"
-#include "ui/views/window/dialog_client_view.h"
 #include "ui/wm/core/coordinate_conversion.h"
 #include "ui/wm/core/cursor_manager.h"
 #include "ui/wm/core/window_util.h"
@@ -367,6 +366,14 @@
 std::string SetWhitelistedPref(Profile* profile,
                                const std::string& pref_name,
                                const base::Value& value) {
+  // Special case for the preference that is stored in the "Local State"
+  // profile.
+  if (pref_name == prefs::kEnableAdbSideloadingRequested) {
+    DCHECK(value.is_bool());
+    g_browser_process->local_state()->Set(pref_name, value);
+    return std::string();
+  }
+
   if (pref_name == chromeos::assistant::prefs::kAssistantEnabled ||
       pref_name == chromeos::assistant::prefs::kAssistantHotwordEnabled) {
     DCHECK(value.is_bool());
@@ -1761,9 +1768,7 @@
   }
 
   // Dismiss the dialog and start launching the VM.
-  PluginVmLauncherView::GetActiveViewForTesting()
-      ->GetDialogClientView()
-      ->AcceptWindow();
+  PluginVmLauncherView::GetActiveViewForTesting()->AcceptDialog();
 
   Respond(NoArguments());
 }
diff --git a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
index deac9569..f14b4672 100644
--- a/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
+++ b/chrome/browser/extensions/api/tab_capture/tab_capture_performancetest.cc
@@ -333,7 +333,7 @@
 // On ChromeOS, software compositing is not an option, and using MSAN on
 // ChromeOS causes problems due to its hardware OpenGL library.
 #if !defined(MEMORY_SANITIZER)
-INSTANTIATE_TEST_SUITE_P(,
+INSTANTIATE_TEST_SUITE_P(All,
                          TabCapturePerformanceTest,
                          testing::Values(kUseGpu,
                                          kTestThroughWebRTC | kUseGpu));
@@ -342,7 +342,7 @@
 #else
 
 // Run everything on non-ChromeOS platforms.
-INSTANTIATE_TEST_SUITE_P(,
+INSTANTIATE_TEST_SUITE_P(All,
                          TabCapturePerformanceTest,
                          testing::Values(0,
                                          kUseGpu,
diff --git a/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc b/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
index f7f0ce9..47135527 100644
--- a/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
+++ b/chrome/browser/extensions/api/vpn_provider/vpn_provider_apitest.cc
@@ -74,37 +74,37 @@
 class TestShillThirdPartyVpnDriverClient
     : public FakeShillThirdPartyVpnDriverClient {
  public:
-  void SetParameters(
-      const std::string& object_path_value,
-      const base::DictionaryValue& parameters,
-      const ShillClientHelper::StringCallback& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) override {
+  void SetParameters(const std::string& object_path_value,
+                     const base::DictionaryValue& parameters,
+                     StringCallback callback,
+                     ErrorCallback error_callback) override {
     set_parameters_counter_++;
     parameters_ = parameters.DeepCopy();
     FakeShillThirdPartyVpnDriverClient::SetParameters(
-        object_path_value, parameters, callback, error_callback);
+        object_path_value, parameters, std::move(callback),
+        std::move(error_callback));
   }
 
-  void UpdateConnectionState(
-      const std::string& object_path_value,
-      const uint32_t connection_state,
-      const base::Closure& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) override {
+  void UpdateConnectionState(const std::string& object_path_value,
+                             const uint32_t connection_state,
+                             base::OnceClosure callback,
+                             ErrorCallback error_callback) override {
     update_connection_state_counter_++;
     connection_state_ = connection_state;
     FakeShillThirdPartyVpnDriverClient::UpdateConnectionState(
-        object_path_value, connection_state, callback, error_callback);
+        object_path_value, connection_state, std::move(callback),
+        std::move(error_callback));
   }
 
-  void SendPacket(
-      const std::string& object_path_value,
-      const std::vector<char>& ip_packet,
-      const base::Closure& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) override {
+  void SendPacket(const std::string& object_path_value,
+                  const std::vector<char>& ip_packet,
+                  base::OnceClosure callback,
+                  ErrorCallback error_callback) override {
     send_packet_counter_++;
     ip_packet_ = ip_packet;
     FakeShillThirdPartyVpnDriverClient::SendPacket(object_path_value, ip_packet,
-                                                   callback, error_callback);
+                                                   std::move(callback),
+                                                   std::move(error_callback));
   }
 
   int set_parameters_counter_ = 0;
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
index fd938d3..149b775 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -63,6 +63,7 @@
 #endif  // defined(USE_OZONE) || defined(USE_X11)
 
 #if defined(OS_WIN)
+#include "base/win/scoped_handle.h"
 #include "base/win/windows_version.h"
 #include "chrome/browser/shell_integration_win.h"
 #include "printing/backend/win_helper.h"
@@ -205,6 +206,10 @@
                            base::SysInfo::NumberOfProcessors());
 }
 
+#if defined(OS_WIN)
+bool IsApplockerRunning();
+#endif  // defined(OS_WIN)
+
 // Called on a background thread, with low priority to avoid slowing down
 // startup with metrics that aren't trivial to compute.
 void RecordStartupMetrics() {
@@ -235,6 +240,10 @@
   // Metric of interest specifically for Windows 7 printing.
   UMA_HISTOGRAM_BOOLEAN("Windows.HasOpenXpsSupport",
                         printing::XPSModule::IsOpenXpsCapable());
+
+  // Determine if Applocker is enabled and running. This does not check if
+  // Applocker rules are being enforced.
+  UMA_HISTOGRAM_BOOLEAN("Windows.ApplockerRunning", IsApplockerRunning());
 #endif  // defined(OS_WIN)
 
   bluetooth_utility::ReportBluetoothAvailability();
@@ -532,6 +541,49 @@
 void RecordVrStartupHistograms() {
   vr::XRRuntimeManager::RecordVrStartupHistograms();
 }
+
+class ScHandleTraits {
+ public:
+  typedef SC_HANDLE Handle;
+
+  ScHandleTraits() = delete;
+  ScHandleTraits(const ScHandleTraits&) = delete;
+  ScHandleTraits& operator=(const ScHandleTraits&) = delete;
+
+  // Closes the handle.
+  static bool CloseHandle(SC_HANDLE handle) {
+    return ::CloseServiceHandle(handle) != FALSE;
+  }
+
+  // Returns true if the handle value is valid.
+  static bool IsHandleValid(SC_HANDLE handle) { return handle != nullptr; }
+
+  // Returns null handle value.
+  static SC_HANDLE NullHandle() { return nullptr; }
+};
+
+typedef base::win::GenericScopedHandle<ScHandleTraits,
+                                       base::win::DummyVerifierTraits>
+    ScopedScHandle;
+
+bool IsApplockerRunning() {
+  ScopedScHandle scm_handle(
+      ::OpenSCManagerW(nullptr, nullptr, SC_MANAGER_CONNECT));
+  if (!scm_handle.IsValid())
+    return false;
+
+  ScopedScHandle service_handle(
+      ::OpenServiceW(scm_handle.Get(), L"appid", SERVICE_QUERY_STATUS));
+  if (!service_handle.IsValid())
+    return false;
+
+  SERVICE_STATUS status;
+  if (!::QueryServiceStatus(service_handle.Get(), &status))
+    return false;
+
+  return status.dwCurrentState == SERVICE_RUNNING;
+}
+
 #endif  // defined(OS_WIN)
 
 }  // namespace
diff --git a/chrome/browser/metrics/ukm_browsertest.cc b/chrome/browser/metrics/ukm_browsertest.cc
index 9c939afb..37ae356 100644
--- a/chrome/browser/metrics/ukm_browsertest.cc
+++ b/chrome/browser/metrics/ukm_browsertest.cc
@@ -55,6 +55,7 @@
 #include "services/network/test/test_network_quality_tracker.h"
 #include "third_party/metrics_proto/ukm/report.pb.h"
 #include "third_party/zlib/google/compression_utils.h"
+#include "url/url_constants.h"
 
 #if BUILDFLAG(ENABLE_DICE_SUPPORT)
 #include "chrome/browser/signin/scoped_account_consistency.h"
@@ -1021,21 +1022,38 @@
 // Verify that sources kept alive in-memory will be discarded by UKM service in
 // one reporting cycle after the web contents are destroyed when the tab is
 // closed or when the user navigated away in the same tab.
-// Disabled as per crbug.com/1004296.
-IN_PROC_BROWSER_TEST_F(UkmBrowserTest, DISABLED_EvictObsoleteSources) {
+IN_PROC_BROWSER_TEST_F(UkmBrowserTest, EvictObsoleteSources) {
   MetricsConsentOverride metrics_consent(true);
   Profile* profile = ProfileManager::GetActiveUserProfile();
   std::unique_ptr<ProfileSyncServiceHarness> harness =
       EnableSyncForProfile(profile);
   Browser* sync_browser = CreateBrowser(profile);
+  ASSERT_TRUE(embedded_test_server()->Start());
 
-  // Navigate to a URL in a new tab.
-  AddTabAtIndexToBrowser(sync_browser, 1, GURL("https://www.chromium.org"),
-                         ui::PAGE_TRANSITION_TYPED, true);
-  ukm::SourceId source_id1 = ukm::GetSourceIdForWebContentsDocument(
-      sync_browser->tab_strip_model()->GetWebContentsAt(1));
+  ukm::SourceId source_id1 = ukm::kInvalidSourceId;
   ukm::SourceId source_id2 = ukm::kInvalidSourceId;
 
+  const std::vector<GURL> test_urls = {
+      embedded_test_server()->GetURL("/title1.html"),
+      embedded_test_server()->GetURL("/title2.html"),
+      embedded_test_server()->GetURL("/title3.html")};
+
+  // Open a blank new tab.
+  AddTabAtIndexToBrowser(sync_browser, 1, GURL(url::kAboutBlankURL),
+                         ui::PAGE_TRANSITION_TYPED, true);
+  // Gather source id from the NavigationHandle assigned to navigations that
+  // start with the expected URL.
+  content::NavigationHandleObserver tab_1_observer(
+      sync_browser->tab_strip_model()->GetActiveWebContents(), test_urls[0]);
+  // Navigate to a test URL in this new tab.
+  ui_test_utils::NavigateToURL(sync_browser, test_urls[0]);
+  // Get the source id associated to the last committed navigation, which could
+  // differ from the id from WebContents for example if the site executes a
+  // same-document navigation (e.g. history.pushState/replaceState). This
+  // navigation source id is the one marked as obsolete by UKM recorder.
+  source_id1 = ukm::ConvertToSourceId(tab_1_observer.navigation_id(),
+                                      ukm::SourceIdType::NAVIGATION_ID);
+
   // The UKM report contains this newly-created source.
   BuildAndStoreUkmLog();
   ukm::Report report = GetUkmReport();
@@ -1048,11 +1066,14 @@
   EXPECT_TRUE(has_source_id1);
   EXPECT_FALSE(has_source_id2);
 
-  // Navigate to a URL in another new tab.
-  AddTabAtIndexToBrowser(sync_browser, 2, GURL("https://www.google.com"),
+  // Navigate to another URL in a new tab.
+  AddTabAtIndexToBrowser(sync_browser, 2, GURL(url::kAboutBlankURL),
                          ui::PAGE_TRANSITION_TYPED, true);
-  source_id2 = ukm::GetSourceIdForWebContentsDocument(
-      sync_browser->tab_strip_model()->GetWebContentsAt(2));
+  content::NavigationHandleObserver tab_2_observer(
+      sync_browser->tab_strip_model()->GetActiveWebContents(), test_urls[1]);
+  ui_test_utils::NavigateToURL(sync_browser, test_urls[1]);
+  source_id2 = ukm::ConvertToSourceId(tab_2_observer.navigation_id(),
+                                      ukm::SourceIdType::NAVIGATION_ID);
 
   // The next report should again contain source 1 because the tab is still
   // alive, and also source 2 associated to the new tab that has just been
@@ -1087,7 +1108,7 @@
 
   // Navigate to a new URL in the current tab, this will mark source 2 that was
   // in the current tab as obsolete.
-  ui_test_utils::NavigateToURL(sync_browser, GURL("https://www.wikipedia.org"));
+  ui_test_utils::NavigateToURL(sync_browser, test_urls[2]);
 
   // The previous report was the last one that could potentially contain entries
   // for source 1. Source 1 is thus no longer included in future reports. This
diff --git a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
index 957844c..adc6687b 100644
--- a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
@@ -435,14 +435,10 @@
 
 #if defined(OS_ANDROID)
   // OfflinePageTabHelper instantiates PrefetchService which in turn requests a
-  // fresh GCM token automatically. These two lines mock out InstanceID (the
-  // component which actually requests the token from play services). Without
-  // this, each test takes an extra 30s waiting on the token (because
-  // content::BrowserTaskEnvironment tries to finish all pending tasks before
-  // ending the test).
+  // fresh GCM token automatically. This causes the request to be done
+  // synchronously instead of with a posted task.
   instance_id::InstanceIDAndroid::ScopedBlockOnAsyncTasksForTesting
       block_async_;
-  instance_id::ScopedUseFakeInstanceIDAndroid use_fake_;
 #endif  // OS_ANDROID
 
   // These are not thread-safe. But they can be used in the pattern that
diff --git a/chrome/browser/offline_pages/offline_page_utils_unittest.cc b/chrome/browser/offline_pages/offline_page_utils_unittest.cc
index 7daf029..d34b328 100644
--- a/chrome/browser/offline_pages/offline_page_utils_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_utils_unittest.cc
@@ -182,14 +182,10 @@
 #if defined(OS_ANDROID)
   chrome::android::MockDownloadController download_controller_;
   // OfflinePageTabHelper instantiates PrefetchService which in turn requests a
-  // fresh GCM token automatically. These two lines mock out InstanceID (the
-  // component which actually requests the token from play services). Without
-  // this, each test takes an extra 30s waiting on the token (because
-  // content::BrowserTaskEnvironment tries to finish all pending tasks before
-  // ending the test).
+  // fresh GCM token automatically. This causes the request to be done
+  // synchronously instead of with a posted task.
   instance_id::InstanceIDAndroid::ScopedBlockOnAsyncTasksForTesting
       block_async_;
-  instance_id::ScopedUseFakeInstanceIDAndroid use_fake_;
 #endif
 };
 
diff --git a/chrome/browser/offline_pages/prefetch/gcm_token_unittest.cc b/chrome/browser/offline_pages/prefetch/gcm_token_unittest.cc
index 71e8d76c..ed5ee2469 100644
--- a/chrome/browser/offline_pages/prefetch/gcm_token_unittest.cc
+++ b/chrome/browser/offline_pages/prefetch/gcm_token_unittest.cc
@@ -54,7 +54,6 @@
 #if defined(OS_ANDROID)
   instance_id::InstanceIDAndroid::ScopedBlockOnAsyncTasksForTesting
       block_async_;
-  instance_id::ScopedUseFakeInstanceIDAndroid use_fake_;
 #endif  // OS_ANDROID
 
   std::string token_;
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index a7eccca..54525c9 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -118,33 +118,6 @@
   }
 }
 
-// Returns the OptimizationGuideDecision based on |decisions|
-// 1) If any decision is false, return false.
-// 2) If all decisions are true, return true.
-// 3) Otherwise, return unknown.
-optimization_guide::OptimizationGuideDecision ResolveOptimizationGuideDecisions(
-    const std::vector<optimization_guide::OptimizationGuideDecision>&
-        decisions) {
-  bool has_all_true_decisions = true;
-  for (const auto decision : decisions) {
-    if (decision == optimization_guide::OptimizationGuideDecision::kFalse)
-      return optimization_guide::OptimizationGuideDecision::kFalse;
-
-    if (decision != optimization_guide::OptimizationGuideDecision::kTrue) {
-      has_all_true_decisions = false;
-      break;
-    }
-  }
-
-  return has_all_true_decisions
-             ? optimization_guide::OptimizationGuideDecision::kTrue
-             : optimization_guide::OptimizationGuideDecision::kUnknown;
-  static_assert(optimization_guide::OptimizationGuideDecision::kMaxValue ==
-                    optimization_guide::OptimizationGuideDecision::kFalse,
-                "This function should be updated when a new "
-                "OptimizationGuideDecision is added");
-}
-
 }  // namespace
 
 OptimizationGuideKeyedService::OptimizationGuideKeyedService(
@@ -272,27 +245,6 @@
       optimization_type_decision);
 }
 
-optimization_guide::OptimizationGuideDecision
-OptimizationGuideKeyedService::ShouldTargetNavigationAndCanApplyOptimization(
-    content::NavigationHandle* navigation_handle,
-    optimization_guide::proto::OptimizationTarget optimization_target,
-    optimization_guide::proto::OptimizationType optimization_type,
-    optimization_guide::OptimizationMetadata* optimization_metadata) {
-  optimization_guide::OptimizationGuideDecision
-      optimization_target_guide_decision =
-          ShouldTargetNavigation(navigation_handle, optimization_target);
-
-  // Get both decisions regardless of what the first decision is for logging
-  // purposes.
-
-  optimization_guide::OptimizationGuideDecision
-      optimization_type_guide_decision = CanApplyOptimization(
-          navigation_handle, optimization_type, optimization_metadata);
-
-  return ResolveOptimizationGuideDecisions(
-      {optimization_target_guide_decision, optimization_type_guide_decision});
-}
-
 void OptimizationGuideKeyedService::ClearData() {
   if (hints_manager_)
     hints_manager_->ClearFetchedHints();
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
index 6781027..022e40c 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -86,12 +86,6 @@
       content::NavigationHandle* navigation_handle,
       optimization_guide::proto::OptimizationType optimization_type,
       optimization_guide::OptimizationMetadata* optimization_metadata) override;
-  optimization_guide::OptimizationGuideDecision
-  ShouldTargetNavigationAndCanApplyOptimization(
-      content::NavigationHandle* navigation_handle,
-      optimization_guide::proto::OptimizationTarget optimization_target,
-      optimization_guide::proto::OptimizationType optimization_type,
-      optimization_guide::OptimizationMetadata* optimization_metadata) override;
 
   // KeyedService implementation:
   void Shutdown() override;
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
index 99f0abd..ee0b6d11 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
@@ -89,20 +89,6 @@
     last_can_apply_optimization_decision_ = service->CanApplyOptimization(
         navigation_handle, optimization_guide::proto::NOSCRIPT,
         /*optimization_metadata=*/nullptr);
-    last_optimization_guide_decision_ =
-        service->ShouldTargetNavigationAndCanApplyOptimization(
-            navigation_handle,
-            optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-            optimization_guide::proto::NOSCRIPT,
-            /*optimization_metadata=*/nullptr);
-  }
-
-  // Returns the last optimization guide decision that was returned by the
-  // OptimizationGuideKeyedService's
-  // ShouldTargetNavigationAndCanApplyOptimization() method.
-  optimization_guide::OptimizationGuideDecision
-  last_optimization_guide_decision() const {
-    return last_optimization_guide_decision_;
   }
 
   // Returns the last optimization guide decision that was returned by the
@@ -121,9 +107,6 @@
 
  private:
   optimization_guide::OptimizationGuideDecision
-      last_optimization_guide_decision_ =
-          optimization_guide::OptimizationGuideDecision::kUnknown;
-  optimization_guide::OptimizationGuideDecision
       last_should_target_navigation_decision_ =
           optimization_guide::OptimizationGuideDecision::kUnknown;
   optimization_guide::OptimizationGuideDecision
@@ -238,12 +221,6 @@
     return consumer_->last_can_apply_optimization_decision();
   }
 
-  // Returns the last decision from the ShouldTargetAndCanApplyOptimization()
-  // method seen by the consumer of the OptimizationGuideKeyedService.
-  optimization_guide::OptimizationGuideDecision last_consumer_decision() {
-    return consumer_->last_optimization_guide_decision();
-  }
-
   GURL url_with_hints() { return url_with_hints_; }
 
   GURL url_that_redirects() { return url_that_redirects_; }
@@ -300,8 +277,6 @@
             last_should_target_navigation_decision());
   EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kUnknown,
             last_can_apply_optimization_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kUnknown,
-            last_consumer_decision());
 }
 
 IN_PROC_BROWSER_TEST_F(OptimizationGuideKeyedServiceBrowserTest,
@@ -340,8 +315,6 @@
             last_should_target_navigation_decision());
   EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue,
             last_can_apply_optimization_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue,
-            last_consumer_decision());
   // Expect that the optimization guide UKM was recorded.
   auto entries = ukm_recorder.GetEntriesByName(
       ukm::builders::OptimizationGuide::kEntryName);
@@ -382,8 +355,6 @@
             last_should_target_navigation_decision());
   EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue,
             last_can_apply_optimization_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
-            last_consumer_decision());
   histogram_tester.ExpectUniqueSample(
       "OptimizationGuide.TargetDecision.PainfulPageLoad",
       static_cast<int>(optimization_guide::OptimizationTargetDecision::
@@ -433,8 +404,6 @@
             last_should_target_navigation_decision());
   EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue,
             last_can_apply_optimization_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue,
-            last_consumer_decision());
   // Make sure hint cache match UMA was logged.
   histogram_tester.ExpectUniqueSample(
       "OptimizationGuide.HintCache.HasHint.BeforeCommit", true, 1);
@@ -483,8 +452,6 @@
             last_should_target_navigation_decision());
   EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
             last_can_apply_optimization_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
-            last_consumer_decision());
   histogram_tester.ExpectUniqueSample(
       "OptimizationGuide.ApplyDecision.NoScript",
       static_cast<int>(
@@ -535,8 +502,6 @@
             last_should_target_navigation_decision());
   EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
             last_can_apply_optimization_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
-            last_consumer_decision());
   histogram_tester.ExpectUniqueSample(
       "OptimizationGuide.ApplyDecision.NoScript",
       static_cast<int>(
@@ -586,8 +551,6 @@
             last_should_target_navigation_decision());
   EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
             last_can_apply_optimization_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse,
-            last_consumer_decision());
   histogram_tester.ExpectUniqueSample(
       "OptimizationGuide.ApplyDecision.NoScript",
       static_cast<int>(
@@ -796,8 +759,6 @@
             last_should_target_navigation_decision());
   EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kTrue,
             last_can_apply_optimization_decision());
-  EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kUnknown,
-            last_consumer_decision());
   histogram_tester.ExpectUniqueSample(
       "OptimizationGuide.TargetDecision.PainfulPageLoad",
       static_cast<int>(optimization_guide::OptimizationTargetDecision::
diff --git a/chrome/browser/policy/arc_policy_browsertest.cc b/chrome/browser/policy/arc_policy_browsertest.cc
new file mode 100644
index 0000000..4d9a5c0a
--- /dev/null
+++ b/chrome/browser/policy/arc_policy_browsertest.cc
@@ -0,0 +1,216 @@
+// 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 "chrome/browser/chromeos/arc/session/arc_session_manager.h"
+#include "chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.h"
+#include "chrome/browser/policy/policy_test_utils.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "components/arc/arc_prefs.h"
+#include "components/arc/arc_util.h"
+#include "components/arc/session/arc_session_runner.h"
+#include "components/arc/test/fake_arc_session.h"
+#include "components/policy/policy_constants.h"
+#include "components/prefs/pref_service.h"
+
+namespace policy {
+
+class ArcPolicyTest : public PolicyTest {
+ public:
+  ArcPolicyTest() {}
+  ~ArcPolicyTest() override {}
+
+ protected:
+  void SetUpOnMainThread() override {
+    PolicyTest::SetUpOnMainThread();
+    arc::ArcSessionManager::SetUiEnabledForTesting(false);
+    arc::ArcSessionManager::Get()->SetArcSessionRunnerForTesting(
+        std::make_unique<arc::ArcSessionRunner>(
+            base::BindRepeating(arc::FakeArcSession::Create)));
+
+    browser()->profile()->GetPrefs()->SetBoolean(arc::prefs::kArcSignedIn,
+                                                 true);
+    browser()->profile()->GetPrefs()->SetBoolean(arc::prefs::kArcTermsAccepted,
+                                                 true);
+  }
+
+  void TearDownOnMainThread() override {
+    arc::ArcSessionManager::Get()->Shutdown();
+    PolicyTest::TearDownOnMainThread();
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    PolicyTest::SetUpCommandLine(command_line);
+    arc::SetArcAvailableCommandLineForTesting(command_line);
+  }
+
+  void SetArcEnabledByPolicy(bool enabled) {
+    PolicyMap policies;
+    policies.Set(key::kArcEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_CLOUD,
+                 base::WrapUnique(new base::Value(enabled)), nullptr);
+    UpdateProviderPolicy(policies);
+    if (browser()) {
+      const PrefService* const prefs = browser()->profile()->GetPrefs();
+      EXPECT_EQ(prefs->GetBoolean(arc::prefs::kArcEnabled), enabled);
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ArcPolicyTest);
+};
+
+// Test ArcEnabled policy.
+IN_PROC_BROWSER_TEST_F(ArcPolicyTest, ArcEnabled) {
+  const PrefService* const pref = browser()->profile()->GetPrefs();
+  const auto* const arc_session_manager = arc::ArcSessionManager::Get();
+
+  // ARC is switched off by default.
+  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcEnabled));
+  EXPECT_FALSE(arc_session_manager->enable_requested());
+
+  // Enable ARC.
+  SetArcEnabledByPolicy(true);
+  EXPECT_TRUE(arc_session_manager->enable_requested());
+
+  // Disable ARC.
+  SetArcEnabledByPolicy(false);
+  EXPECT_FALSE(arc_session_manager->enable_requested());
+}
+
+// Test ArcBackupRestoreServiceEnabled policy.
+IN_PROC_BROWSER_TEST_F(ArcPolicyTest, ArcBackupRestoreServiceEnabled) {
+  PrefService* const pref = browser()->profile()->GetPrefs();
+
+  // Enable ARC backup and restore in user prefs.
+  pref->SetBoolean(arc::prefs::kArcBackupRestoreEnabled, true);
+
+  // ARC backup and restore is disabled by policy by default.
+  UpdateProviderPolicy(PolicyMap());
+  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
+  EXPECT_TRUE(pref->IsManagedPreference(arc::prefs::kArcBackupRestoreEnabled));
+
+  // Set ARC backup and restore to user control via policy.
+  PolicyMap policies;
+  policies.Set(key::kArcBackupRestoreServiceEnabled, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               std::make_unique<base::Value>(
+                   static_cast<int>(ArcServicePolicyValue::kUnderUserControl)),
+               nullptr);
+  UpdateProviderPolicy(policies);
+
+  // User choice should be honored now.
+  EXPECT_TRUE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
+  EXPECT_FALSE(pref->IsManagedPreference(arc::prefs::kArcBackupRestoreEnabled));
+  pref->SetBoolean(arc::prefs::kArcBackupRestoreEnabled, false);
+  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
+  pref->SetBoolean(arc::prefs::kArcBackupRestoreEnabled, true);
+  EXPECT_TRUE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
+
+  // Set ARC backup and restore to disabled via policy.
+  policies.Set(key::kArcBackupRestoreServiceEnabled, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               std::make_unique<base::Value>(
+                   static_cast<int>(ArcServicePolicyValue::kDisabled)),
+               nullptr);
+  UpdateProviderPolicy(policies);
+  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
+  EXPECT_TRUE(pref->IsManagedPreference(arc::prefs::kArcBackupRestoreEnabled));
+
+  // Disable ARC backup and restore in user prefs.
+  pref->SetBoolean(arc::prefs::kArcBackupRestoreEnabled, true);
+
+  // Set ARC backup and restore to enabled via policy.
+  policies.Set(key::kArcBackupRestoreServiceEnabled, POLICY_LEVEL_MANDATORY,
+               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+               std::make_unique<base::Value>(
+                   static_cast<int>(ArcServicePolicyValue::kEnabled)),
+               nullptr);
+  UpdateProviderPolicy(policies);
+  EXPECT_TRUE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
+  EXPECT_TRUE(pref->IsManagedPreference(arc::prefs::kArcBackupRestoreEnabled));
+}
+
+// Test ArcGoogleLocationServicesEnabled policy and its interplay with the
+// DefaultGeolocationSetting policy.
+IN_PROC_BROWSER_TEST_F(ArcPolicyTest, ArcGoogleLocationServicesEnabled) {
+  PrefService* const pref = browser()->profile()->GetPrefs();
+
+  // Values of the ArcGoogleLocationServicesEnabled policy to be tested.
+  std::vector<base::Value> test_policy_values;
+  test_policy_values.emplace_back();  // unset
+  test_policy_values.emplace_back(
+      static_cast<int>(ArcServicePolicyValue::kDisabled));
+  test_policy_values.emplace_back(
+      static_cast<int>(ArcServicePolicyValue::kUnderUserControl));
+  test_policy_values.emplace_back(
+      static_cast<int>(ArcServicePolicyValue::kEnabled));
+
+  // Values of the DefaultGeolocationSetting policy to be tested.
+  std::vector<base::Value> test_default_geo_policy_values;
+  test_default_geo_policy_values.emplace_back();   // unset
+  test_default_geo_policy_values.emplace_back(1);  // 'AllowGeolocation'
+  test_default_geo_policy_values.emplace_back(2);  // 'BlockGeolocation'
+  test_default_geo_policy_values.emplace_back(3);  // 'AskGeolocation'
+
+  // Switch on the pref in user prefs.
+  pref->SetBoolean(arc::prefs::kArcLocationServiceEnabled, true);
+
+  // The pref is overridden to disabled by policy by default.
+  UpdateProviderPolicy(PolicyMap());
+  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcLocationServiceEnabled));
+  EXPECT_TRUE(
+      pref->IsManagedPreference(arc::prefs::kArcLocationServiceEnabled));
+
+  for (const auto& test_policy_value : test_policy_values) {
+    for (const auto& test_default_geo_policy_value :
+         test_default_geo_policy_values) {
+      PolicyMap policies;
+      if (test_policy_value.is_int()) {
+        policies.Set(key::kArcGoogleLocationServicesEnabled,
+                     POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                     POLICY_SOURCE_CLOUD, test_policy_value.CreateDeepCopy(),
+                     nullptr);
+      }
+      if (test_default_geo_policy_value.is_int()) {
+        policies.Set(key::kDefaultGeolocationSetting, POLICY_LEVEL_MANDATORY,
+                     POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
+                     test_default_geo_policy_value.CreateDeepCopy(), nullptr);
+      }
+      UpdateProviderPolicy(policies);
+
+      const bool should_be_disabled_by_policy =
+          test_policy_value.is_none() ||
+          (test_policy_value.GetInt() ==
+           static_cast<int>(ArcServicePolicyValue::kDisabled));
+      const bool should_be_enabled_by_policy =
+          test_policy_value.is_int() &&
+          test_policy_value.GetInt() ==
+              static_cast<int>(ArcServicePolicyValue::kEnabled);
+      const bool should_be_disabled_by_default_geo_policy =
+          test_default_geo_policy_value.is_int() &&
+          test_default_geo_policy_value.GetInt() == 2;
+      const bool expected_pref_value =
+          !(should_be_disabled_by_policy ||
+            should_be_disabled_by_default_geo_policy);
+      EXPECT_EQ(expected_pref_value,
+                pref->GetBoolean(arc::prefs::kArcLocationServiceEnabled))
+          << "ArcGoogleLocationServicesEnabled policy is set to "
+          << test_policy_value << "DefaultGeolocationSetting policy is set to "
+          << test_default_geo_policy_value;
+
+      const bool expected_pref_managed =
+          should_be_disabled_by_policy || should_be_enabled_by_policy ||
+          should_be_disabled_by_default_geo_policy;
+      EXPECT_EQ(
+          expected_pref_managed,
+          pref->IsManagedPreference(arc::prefs::kArcLocationServiceEnabled))
+          << "ArcGoogleLocationServicesEnabled policy is set to "
+          << test_policy_value << "DefaultGeolocationSetting policy is set to "
+          << test_default_geo_policy_value;
+    }
+  }
+}
+
+}  // namespace policy
diff --git a/chrome/browser/policy/browser_dm_token_storage.cc b/chrome/browser/policy/browser_dm_token_storage.cc
index 2b3460c..dd41418 100644
--- a/chrome/browser/policy/browser_dm_token_storage.cc
+++ b/chrome/browser/policy/browser_dm_token_storage.cc
@@ -126,7 +126,6 @@
 
 DMToken BrowserDMTokenStorage::RetrieveDMToken() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!store_callback_);
 
   InitIfNeeded();
   return dm_token_;
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index ef836f51..269b90e9 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -1239,6 +1239,10 @@
     prefs::kClickToCallEnabled,
     base::Value::Type::BOOLEAN },
 #endif  // BUILDFLAG(ENABLE_CLICK_TO_CALL)
+
+  { key::kPrinterTypeDenyList,
+    prefs::kPrinterTypeDenyList,
+    base::Value::Type::LIST },
 };
 // clang-format on
 
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 7be861e..3e12f8a 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -66,7 +66,6 @@
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
-#include "chrome/browser/chromeos/policy/configuration_policy_handler_chromeos.h"
 #include "chrome/browser/component_updater/chrome_component_updater_configurator.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
@@ -237,8 +236,6 @@
 #include "ash/public/cpp/ash_pref_names.h"
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/shell.h"
-#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
-#include "chrome/browser/chromeos/arc/session/arc_session_manager.h"
 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
 #include "chrome/browser/chromeos/note_taking_helper.h"
@@ -254,11 +251,8 @@
 #include "chromeos/constants/chromeos_switches.h"
 #include "chromeos/cryptohome/cryptohome_parameters.h"
 #include "components/account_id/account_id.h"
-#include "components/arc/arc_prefs.h"
-#include "components/arc/arc_util.h"
-#include "components/arc/session/arc_session_runner.h"
-#include "components/arc/test/fake_arc_session.h"
 #include "components/user_manager/user_manager.h"
+#include "ui/base/ime/chromeos/input_method_manager.h"
 #include "ui/snapshot/screenshot_grabber.h"
 #endif
 
@@ -4206,203 +4200,6 @@
   EXPECT_FALSE(display_manager->unified_desktop_enabled());
 }
 
-class ArcPolicyTest : public PolicyTest {
- public:
-  ArcPolicyTest() {}
-  ~ArcPolicyTest() override {}
-
- protected:
-  void SetUpOnMainThread() override {
-    PolicyTest::SetUpOnMainThread();
-    arc::ArcSessionManager::SetUiEnabledForTesting(false);
-    arc::ArcSessionManager::Get()->SetArcSessionRunnerForTesting(
-        std::make_unique<arc::ArcSessionRunner>(
-            base::BindRepeating(arc::FakeArcSession::Create)));
-
-    browser()->profile()->GetPrefs()->SetBoolean(arc::prefs::kArcSignedIn,
-                                                 true);
-    browser()->profile()->GetPrefs()->SetBoolean(arc::prefs::kArcTermsAccepted,
-                                                 true);
-  }
-
-  void TearDownOnMainThread() override {
-    arc::ArcSessionManager::Get()->Shutdown();
-    PolicyTest::TearDownOnMainThread();
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    PolicyTest::SetUpCommandLine(command_line);
-    arc::SetArcAvailableCommandLineForTesting(command_line);
-  }
-
-  void SetArcEnabledByPolicy(bool enabled) {
-    PolicyMap policies;
-    policies.Set(key::kArcEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-                 POLICY_SOURCE_CLOUD,
-                 base::WrapUnique(new base::Value(enabled)), nullptr);
-    UpdateProviderPolicy(policies);
-    if (browser()) {
-      const PrefService* const prefs = browser()->profile()->GetPrefs();
-      EXPECT_EQ(prefs->GetBoolean(arc::prefs::kArcEnabled), enabled);
-    }
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ArcPolicyTest);
-};
-
-// Test ArcEnabled policy.
-IN_PROC_BROWSER_TEST_F(ArcPolicyTest, ArcEnabled) {
-  const PrefService* const pref = browser()->profile()->GetPrefs();
-  const auto* const arc_session_manager = arc::ArcSessionManager::Get();
-
-  // ARC is switched off by default.
-  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcEnabled));
-  EXPECT_FALSE(arc_session_manager->enable_requested());
-
-  // Enable ARC.
-  SetArcEnabledByPolicy(true);
-  EXPECT_TRUE(arc_session_manager->enable_requested());
-
-  // Disable ARC.
-  SetArcEnabledByPolicy(false);
-  EXPECT_FALSE(arc_session_manager->enable_requested());
-}
-
-// Test ArcBackupRestoreServiceEnabled policy.
-IN_PROC_BROWSER_TEST_F(ArcPolicyTest, ArcBackupRestoreServiceEnabled) {
-  PrefService* const pref = browser()->profile()->GetPrefs();
-
-  // Enable ARC backup and restore in user prefs.
-  pref->SetBoolean(arc::prefs::kArcBackupRestoreEnabled, true);
-
-  // ARC backup and restore is disabled by policy by default.
-  UpdateProviderPolicy(PolicyMap());
-  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
-  EXPECT_TRUE(pref->IsManagedPreference(arc::prefs::kArcBackupRestoreEnabled));
-
-  // Set ARC backup and restore to user control via policy.
-  PolicyMap policies;
-  policies.Set(key::kArcBackupRestoreServiceEnabled, POLICY_LEVEL_MANDATORY,
-               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
-               std::make_unique<base::Value>(
-                   static_cast<int>(ArcServicePolicyValue::kUnderUserControl)),
-               nullptr);
-  UpdateProviderPolicy(policies);
-
-  // User choice should be honored now.
-  EXPECT_TRUE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
-  EXPECT_FALSE(pref->IsManagedPreference(arc::prefs::kArcBackupRestoreEnabled));
-  pref->SetBoolean(arc::prefs::kArcBackupRestoreEnabled, false);
-  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
-  pref->SetBoolean(arc::prefs::kArcBackupRestoreEnabled, true);
-  EXPECT_TRUE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
-
-  // Set ARC backup and restore to disabled via policy.
-  policies.Set(key::kArcBackupRestoreServiceEnabled, POLICY_LEVEL_MANDATORY,
-               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
-               std::make_unique<base::Value>(
-                   static_cast<int>(ArcServicePolicyValue::kDisabled)),
-               nullptr);
-  UpdateProviderPolicy(policies);
-  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
-  EXPECT_TRUE(pref->IsManagedPreference(arc::prefs::kArcBackupRestoreEnabled));
-
-  // Disable ARC backup and restore in user prefs.
-  pref->SetBoolean(arc::prefs::kArcBackupRestoreEnabled, true);
-
-  // Set ARC backup and restore to enabled via policy.
-  policies.Set(key::kArcBackupRestoreServiceEnabled, POLICY_LEVEL_MANDATORY,
-               POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
-               std::make_unique<base::Value>(
-                   static_cast<int>(ArcServicePolicyValue::kEnabled)),
-               nullptr);
-  UpdateProviderPolicy(policies);
-  EXPECT_TRUE(pref->GetBoolean(arc::prefs::kArcBackupRestoreEnabled));
-  EXPECT_TRUE(pref->IsManagedPreference(arc::prefs::kArcBackupRestoreEnabled));
-}
-
-// Test ArcGoogleLocationServicesEnabled policy and its interplay with the
-// DefaultGeolocationSetting policy.
-IN_PROC_BROWSER_TEST_F(ArcPolicyTest, ArcGoogleLocationServicesEnabled) {
-  PrefService* const pref = browser()->profile()->GetPrefs();
-
-  // Values of the ArcGoogleLocationServicesEnabled policy to be tested.
-  std::vector<base::Value> test_policy_values;
-  test_policy_values.emplace_back();  // unset
-  test_policy_values.emplace_back(
-      static_cast<int>(ArcServicePolicyValue::kDisabled));
-  test_policy_values.emplace_back(
-      static_cast<int>(ArcServicePolicyValue::kUnderUserControl));
-  test_policy_values.emplace_back(
-      static_cast<int>(ArcServicePolicyValue::kEnabled));
-
-  // Values of the DefaultGeolocationSetting policy to be tested.
-  std::vector<base::Value> test_default_geo_policy_values;
-  test_default_geo_policy_values.emplace_back();   // unset
-  test_default_geo_policy_values.emplace_back(1);  // 'AllowGeolocation'
-  test_default_geo_policy_values.emplace_back(2);  // 'BlockGeolocation'
-  test_default_geo_policy_values.emplace_back(3);  // 'AskGeolocation'
-
-  // Switch on the pref in user prefs.
-  pref->SetBoolean(arc::prefs::kArcLocationServiceEnabled, true);
-
-  // The pref is overridden to disabled by policy by default.
-  UpdateProviderPolicy(PolicyMap());
-  EXPECT_FALSE(pref->GetBoolean(arc::prefs::kArcLocationServiceEnabled));
-  EXPECT_TRUE(
-      pref->IsManagedPreference(arc::prefs::kArcLocationServiceEnabled));
-
-  for (const auto& test_policy_value : test_policy_values) {
-    for (const auto& test_default_geo_policy_value :
-         test_default_geo_policy_values) {
-      PolicyMap policies;
-      if (test_policy_value.is_int()) {
-        policies.Set(key::kArcGoogleLocationServicesEnabled,
-                     POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-                     POLICY_SOURCE_CLOUD, test_policy_value.CreateDeepCopy(),
-                     nullptr);
-      }
-      if (test_default_geo_policy_value.is_int()) {
-        policies.Set(key::kDefaultGeolocationSetting, POLICY_LEVEL_MANDATORY,
-                     POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD,
-                     test_default_geo_policy_value.CreateDeepCopy(), nullptr);
-      }
-      UpdateProviderPolicy(policies);
-
-      const bool should_be_disabled_by_policy =
-          test_policy_value.is_none() ||
-          (test_policy_value.GetInt() ==
-           static_cast<int>(ArcServicePolicyValue::kDisabled));
-      const bool should_be_enabled_by_policy =
-          test_policy_value.is_int() &&
-          test_policy_value.GetInt() ==
-              static_cast<int>(ArcServicePolicyValue::kEnabled);
-      const bool should_be_disabled_by_default_geo_policy =
-          test_default_geo_policy_value.is_int() &&
-          test_default_geo_policy_value.GetInt() == 2;
-      const bool expected_pref_value =
-          !(should_be_disabled_by_policy ||
-            should_be_disabled_by_default_geo_policy);
-      EXPECT_EQ(expected_pref_value,
-                pref->GetBoolean(arc::prefs::kArcLocationServiceEnabled))
-          << "ArcGoogleLocationServicesEnabled policy is set to "
-          << test_policy_value << "DefaultGeolocationSetting policy is set to "
-          << test_default_geo_policy_value;
-
-      const bool expected_pref_managed =
-          should_be_disabled_by_policy || should_be_enabled_by_policy ||
-          should_be_disabled_by_default_geo_policy;
-      EXPECT_EQ(
-          expected_pref_managed,
-          pref->IsManagedPreference(arc::prefs::kArcLocationServiceEnabled))
-          << "ArcGoogleLocationServicesEnabled policy is set to "
-          << test_policy_value << "DefaultGeolocationSetting policy is set to "
-          << test_default_geo_policy_value;
-    }
-  }
-}
-
 #endif  // defined(OS_CHROMEOS)
 
 class NetworkTimePolicyTest : public PolicyTest {
diff --git a/chrome/browser/policy/policy_prefs_browsertest.cc b/chrome/browser/policy/policy_prefs_browsertest.cc
index 83bffc4..b72801def 100644
--- a/chrome/browser/policy/policy_prefs_browsertest.cc
+++ b/chrome/browser/policy/policy_prefs_browsertest.cc
@@ -76,49 +76,74 @@
 // Contains the details of a single test case verifying that the controlled
 // setting indicators for a pref affected by a policy work correctly. This is
 // part of the data loaded from chrome/test/data/policy/policy_test_cases.json.
-class IndicatorTestCase {
+class PrefIndicatorTest {
  public:
-  IndicatorTestCase(const base::DictionaryValue& policy,
-                    const std::string& value,
-                    bool readonly)
-      : policy_(policy.DeepCopy()), value_(value), readonly_(readonly) {}
-  ~IndicatorTestCase() {}
+  explicit PrefIndicatorTest(const base::Value& indicator_test) {
+    base::Optional<bool> readonly = indicator_test.FindBoolKey("readonly");
+    const std::string* value = indicator_test.FindStringKey("value");
+    const std::string* selector = indicator_test.FindStringKey("selector");
+    const std::string* test_url = indicator_test.FindStringKey("test_url");
+    const std::string* pref = indicator_test.FindStringKey("pref");
+    const std::string* test_setup_js =
+        indicator_test.FindStringKey("test_setup_js");
 
-  const base::DictionaryValue& policy() const { return *policy_; }
+    readonly_ = readonly.value_or(false);
+    value_ = value ? *value : std::string();
+    test_url_ = test_url ? *test_url : std::string();
+    test_setup_js_ = test_setup_js ? *test_setup_js : std::string();
+    selector_ = selector ? *selector : std::string();
+    pref_ = pref ? *pref : std::string();
+  }
+
+  ~PrefIndicatorTest() {}
 
   const std::string& value() const { return value_; }
+  const std::string& test_url() const { return test_url_; }
+  const std::string& test_setup_js() const { return test_setup_js_; }
+  const std::string& selector() const { return selector_; }
+  const std::string& pref() const { return pref_; }
 
   bool readonly() const { return readonly_; }
 
  private:
-  std::unique_ptr<base::DictionaryValue> policy_;
-  std::string value_;
   bool readonly_;
+  std::string value_;
+  std::string test_url_;
+  std::string test_setup_js_;
+  std::string selector_;
+  std::string pref_;
 
-  DISALLOW_COPY_AND_ASSIGN(IndicatorTestCase);
+  DISALLOW_COPY_AND_ASSIGN(PrefIndicatorTest);
 };
 
-// Contains the testing details for a single pref affected by a policy. This is
-// part of the data loaded from chrome/test/data/policy/policy_test_cases.json.
-class PrefMapping {
+// Contains the testing details for a single pref affected by one or multiple
+// policies. This is part of the data loaded from
+// chrome/test/data/policy/policy_test_cases.json.
+class PrefTestCase {
  public:
-  PrefMapping(const std::string& pref,
-              bool is_local_state,
-              bool check_for_mandatory,
-              bool check_for_recommended,
-              const std::string& indicator_test_url,
-              const std::string& indicator_test_setup_js,
-              const std::string& indicator_selector)
-      : pref_(pref),
-        is_local_state_(is_local_state),
-        check_for_mandatory_(check_for_mandatory),
-        check_for_recommended_(check_for_recommended),
-        indicator_test_url_(indicator_test_url),
-        indicator_test_setup_js_(indicator_test_setup_js),
-        indicator_selector_(indicator_selector) {}
-  ~PrefMapping() {}
+  explicit PrefTestCase(const std::string& name, const base::Value& settings) {
+    const base::Value* value = settings.FindKey("value");
+    const base::Value* indicator_test = settings.FindDictKey("indicator_test");
+    is_local_state_ = settings.FindBoolKey("local_state").value_or(false);
+    check_for_mandatory_ =
+        settings.FindBoolKey("check_for_mandatory").value_or(true);
+    check_for_recommended_ =
+        settings.FindBoolKey("check_for_recommended").value_or(true);
+    expect_default_ = settings.FindBoolKey("expect_default").value_or(false);
+
+    pref_ = name;
+    if (value)
+      value_ = value->CreateDeepCopy();
+    if (indicator_test) {
+      pref_indicator_test_ =
+          std::make_unique<PrefIndicatorTest>(*indicator_test);
+    }
+  }
+
+  ~PrefTestCase() {}
 
   const std::string& pref() const { return pref_; }
+  const base::Value* value() const { return value_.get(); }
 
   bool is_local_state() const { return is_local_state_; }
 
@@ -126,47 +151,88 @@
 
   bool check_for_recommended() const { return check_for_recommended_; }
 
-  const std::string& indicator_test_url() const { return indicator_test_url_; }
+  bool expect_default() const { return expect_default_; }
 
-  const std::string& indicator_test_setup_js() const {
-    return indicator_test_setup_js_;
+  const PrefIndicatorTest* indicator_test_case() const {
+    return pref_indicator_test_.get();
   }
 
-  const std::string& indicator_selector() const { return indicator_selector_; }
+ private:
+  std::string pref_;
+  std::unique_ptr<base::Value> value_;
+  bool is_local_state_;
+  bool check_for_mandatory_;
+  bool check_for_recommended_;
+  bool expect_default_;
+  std::unique_ptr<PrefIndicatorTest> pref_indicator_test_;
+  DISALLOW_COPY_AND_ASSIGN(PrefTestCase);
+};
 
-  const std::vector<std::unique_ptr<IndicatorTestCase>>& indicator_test_cases()
-      const {
-    return indicator_test_cases_;
+// Contains the testing details for a single pref affected by a policy. This is
+// part of the data loaded from chrome/test/data/policy/policy_test_cases.json.
+class PolicyPrefMappingTest {
+ public:
+  explicit PolicyPrefMappingTest(const base::Value& mapping) {
+    const base::Value* policies = mapping.FindDictKey("policies");
+    const base::Value* prefs = mapping.FindDictKey("prefs");
+    if (policies)
+      policies_ = policies->Clone();
+    if (prefs) {
+      for (const auto& pref_setting : prefs->DictItems())
+        prefs_.push_back(std::make_unique<PrefTestCase>(pref_setting.first,
+                                                        pref_setting.second));
+    }
   }
-  void AddIndicatorTestCase(std::unique_ptr<IndicatorTestCase> test_case) {
-    indicator_test_cases_.push_back(std::move(test_case));
+  ~PolicyPrefMappingTest() {}
+
+  const base::Value& policies() const { return policies_; }
+
+  const std::vector<std::unique_ptr<PrefTestCase>>& prefs() const {
+    return prefs_;
   }
 
  private:
   const std::string pref_;
-  const bool is_local_state_;
-  const bool check_for_mandatory_;
-  const bool check_for_recommended_;
-  const std::string indicator_test_url_;
-  const std::string indicator_test_setup_js_;
-  const std::string indicator_selector_;
-  std::vector<std::unique_ptr<IndicatorTestCase>> indicator_test_cases_;
+  base::Value policies_;
+  std::vector<std::unique_ptr<PrefTestCase>> prefs_;
 
-  DISALLOW_COPY_AND_ASSIGN(PrefMapping);
+  DISALLOW_COPY_AND_ASSIGN(PolicyPrefMappingTest);
 };
 
 // Contains the testing details for a single policy. This is part of the data
 // loaded from chrome/test/data/policy/policy_test_cases.json.
 class PolicyTestCase {
  public:
-  PolicyTestCase(const std::string& name,
-                 bool is_official_only,
-                 bool can_be_recommended,
-                 const std::string& indicator_selector)
-      : name_(name),
-        is_official_only_(is_official_only),
-        can_be_recommended_(can_be_recommended),
-        indicator_selector_(indicator_selector) {}
+  PolicyTestCase(const std::string& name, const base::Value& test_case)
+      : name_(name) {
+    is_official_only_ = test_case.FindBoolKey("official_only").value_or(false);
+    can_be_recommended_ =
+        test_case.FindBoolKey("can_be_recommended").value_or(false);
+
+    const base::Value* os_list = test_case.FindListKey("os");
+    if (os_list) {
+      for (const auto& os : os_list->GetList()) {
+        if (os.is_string())
+          supported_os_.push_back(os.GetString());
+      }
+    }
+
+    const base::Value* test_policy = test_case.FindDictKey("test_policy");
+    if (test_policy)
+      test_policy_ = test_policy->CreateDeepCopy();
+
+    const base::Value* policy_pref_mapping_tests =
+        test_case.FindListKey("policy_pref_mapping_test");
+    if (policy_pref_mapping_tests) {
+      for (const auto& mapping : policy_pref_mapping_tests->GetList()) {
+        if (mapping.is_dict()) {
+          policy_pref_mapping_test_.push_back(
+              std::make_unique<PolicyPrefMappingTest>(mapping));
+        }
+      }
+    }
+  }
+
   ~PolicyTestCase() {}
 
   const std::string& name() const { return name_; }
@@ -199,29 +265,20 @@
     return IsOsSupported();
   }
 
-  const base::DictionaryValue& test_policy() const { return test_policy_; }
-  void SetTestPolicy(const base::DictionaryValue& policy) {
-    test_policy_.Clear();
-    test_policy_.MergeDictionary(&policy);
-  }
+  const base::Value* test_policy() const { return test_policy_.get(); }
 
-  const std::vector<std::unique_ptr<PrefMapping>>& pref_mappings() const {
-    return pref_mappings_;
+  const std::vector<std::unique_ptr<PolicyPrefMappingTest>>&
+  policy_pref_mapping_test() const {
+    return policy_pref_mapping_test_;
   }
-  void AddPrefMapping(std::unique_ptr<PrefMapping> pref_mapping) {
-    pref_mappings_.push_back(std::move(pref_mapping));
-  }
-
-  const std::string& indicator_selector() const { return indicator_selector_; }
 
  private:
   std::string name_;
   bool is_official_only_;
   bool can_be_recommended_;
   std::vector<std::string> supported_os_;
-  base::DictionaryValue test_policy_;
-  std::vector<std::unique_ptr<PrefMapping>> pref_mappings_;
-  std::string indicator_selector_;
+  std::unique_ptr<base::Value> test_policy_;
+  std::vector<std::unique_ptr<PolicyPrefMappingTest>> policy_pref_mapping_test_;
 
   DISALLOW_COPY_AND_ASSIGN(PolicyTestCase);
 };
@@ -229,7 +286,7 @@
 // Parses all policy test cases and makes them available in a map.
 class PolicyTestCases {
  public:
-  typedef std::vector<PolicyTestCase*> PolicyTestCaseVector;
+  typedef std::vector<std::unique_ptr<PolicyTestCase>> PolicyTestCaseVector;
   typedef std::map<std::string, PolicyTestCaseVector> PolicyTestCaseMap;
   typedef PolicyTestCaseMap::const_iterator iterator;
 
@@ -257,18 +314,13 @@
       const std::string policy_name = GetPolicyName(it.first);
       if (policy_name == kTemplateSampleTest)
         continue;
-      PolicyTestCase* policy_test_case = GetPolicyTestCase(dict, it.first);
-      if (policy_test_case)
-        policy_test_cases_[policy_name].push_back(policy_test_case);
+      auto policy_test_case =
+          std::make_unique<PolicyTestCase>(it.first, it.second);
+      policy_test_cases_[policy_name].push_back(std::move(policy_test_case));
     }
   }
 
-  ~PolicyTestCases() {
-    for (const auto& policy : policy_test_cases_) {
-      for (PolicyTestCase* test_case : policy.second)
-        delete test_case;
-    }
-  }
+  ~PolicyTestCases() {}
 
   const PolicyTestCaseVector* Get(const std::string& name) const {
     const iterator it = policy_test_cases_.find(name);
@@ -280,86 +332,6 @@
   iterator end() const { return policy_test_cases_.end(); }
 
  private:
-  PolicyTestCase* GetPolicyTestCase(const base::DictionaryValue* tests,
-                                    const std::string& name) {
-    const base::DictionaryValue* policy_test_dict = nullptr;
-    if (!tests->GetDictionaryWithoutPathExpansion(name, &policy_test_dict))
-      return nullptr;
-    bool is_official_only = false;
-    policy_test_dict->GetBoolean("official_only", &is_official_only);
-    bool can_be_recommended = false;
-    policy_test_dict->GetBoolean("can_be_recommended", &can_be_recommended);
-    std::string indicator_selector;
-    policy_test_dict->GetString("indicator_selector", &indicator_selector);
-
-    PolicyTestCase* policy_test_case = new PolicyTestCase(
-        name, is_official_only, can_be_recommended, indicator_selector);
-    const base::ListValue* os_list = nullptr;
-    if (policy_test_dict->GetList("os", &os_list)) {
-      for (size_t i = 0; i < os_list->GetSize(); ++i) {
-        std::string os;
-        if (os_list->GetString(i, &os))
-          policy_test_case->AddSupportedOs(os);
-      }
-    }
-
-    const base::DictionaryValue* policy = nullptr;
-    if (policy_test_dict->GetDictionary("test_policy", &policy))
-      policy_test_case->SetTestPolicy(*policy);
-    const base::ListValue* pref_mappings = nullptr;
-    if (policy_test_dict->GetList("pref_mappings", &pref_mappings)) {
-      for (size_t i = 0; i < pref_mappings->GetSize(); ++i) {
-        const base::DictionaryValue* pref_mapping_dict = nullptr;
-        std::string pref;
-        if (!pref_mappings->GetDictionary(i, &pref_mapping_dict) ||
-            !pref_mapping_dict->GetString("pref", &pref)) {
-          ADD_FAILURE() << "Malformed pref_mappings entry for " << name
-                        << " in policy_test_cases.json.";
-          continue;
-        }
-        bool is_local_state = false;
-        pref_mapping_dict->GetBoolean("local_state", &is_local_state);
-        bool check_for_mandatory = true;
-        pref_mapping_dict->GetBoolean("check_for_mandatory",
-                                      &check_for_mandatory);
-        bool check_for_recommended = true;
-        pref_mapping_dict->GetBoolean("check_for_recommended",
-                                      &check_for_recommended);
-        std::string indicator_test_url;
-        pref_mapping_dict->GetString("indicator_test_url", &indicator_test_url);
-        std::string indicator_test_setup_js;
-        pref_mapping_dict->GetString("indicator_test_setup_js",
-                                     &indicator_test_setup_js);
-        std::string indicator_selector;
-        pref_mapping_dict->GetString("indicator_selector", &indicator_selector);
-        auto pref_mapping = std::make_unique<PrefMapping>(
-            pref, is_local_state, check_for_mandatory, check_for_recommended,
-            indicator_test_url, indicator_test_setup_js, indicator_selector);
-        const base::ListValue* indicator_tests = nullptr;
-        if (pref_mapping_dict->GetList("indicator_tests", &indicator_tests)) {
-          for (size_t i = 0; i < indicator_tests->GetSize(); ++i) {
-            const base::DictionaryValue* indicator_test_dict = nullptr;
-            const base::DictionaryValue* policy = nullptr;
-            if (!indicator_tests->GetDictionary(i, &indicator_test_dict) ||
-                !indicator_test_dict->GetDictionary("policy", &policy)) {
-              ADD_FAILURE() << "Malformed indicator_tests entry for " << name
-                            << " in policy_test_cases.json.";
-              continue;
-            }
-            std::string value;
-            indicator_test_dict->GetString("value", &value);
-            bool readonly = false;
-            indicator_test_dict->GetBoolean("readonly", &readonly);
-            pref_mapping->AddIndicatorTestCase(
-                std::make_unique<IndicatorTestCase>(*policy, value, readonly));
-          }
-        }
-        policy_test_case->AddPrefMapping(std::move(pref_mapping));
-      }
-    }
-    return policy_test_case;
-  }
-
   PolicyTestCaseMap policy_test_cases_;
 
   DISALLOW_COPY_AND_ASSIGN(PolicyTestCases);
@@ -387,7 +359,7 @@
     }
 
     bool has_test_case_for_this_os = false;
-    for (const PolicyTestCase* test_case : policy->second) {
+    for (const auto& test_case : policy->second) {
       has_test_case_for_this_os |= test_case->IsSupported();
       if (has_test_case_for_this_os)
         break;
@@ -425,8 +397,7 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  void SetProviderPolicy(const base::DictionaryValue& policies,
-                         PolicyLevel level) {
+  void SetProviderPolicy(const base::Value& policies, PolicyLevel level) {
     PolicyMap policy_map;
     for (const auto& it : policies.DictItems()) {
       const PolicyDetails* policy_details = GetChromePolicyDetails(it.first);
@@ -455,8 +426,8 @@
 
   const PolicyTestCases test_cases;
   for (const auto& policy : test_cases) {
-    for (const PolicyTestCase* test_case : policy.second) {
-      const auto& pref_mappings = test_case->pref_mappings();
+    for (const auto& test_case : policy.second) {
+      const auto& pref_mappings = test_case->policy_pref_mapping_test();
       if (!chrome_schema.GetKnownProperty(policy.first).valid()) {
         // If the policy is supported on this platform according to the test it
         // should be known otherwise we signal this as a failure.
@@ -481,39 +452,50 @@
       LOG(INFO) << "Testing policy: " << policy.first;
 
       for (const auto& pref_mapping : pref_mappings) {
-        // Skip Chrome OS preferences that use a different backend and cannot be
-        // retrieved through the prefs mechanism.
-        if (base::StartsWith(pref_mapping->pref(), kCrosSettingsPrefix,
-                             base::CompareCase::SENSITIVE))
-          continue;
+        for (const auto& pref_case : pref_mapping->prefs()) {
+          // Skip Chrome OS preferences that use a different backend and cannot
+          // be retrieved through the prefs mechanism.
+          if (base::StartsWith(pref_case->pref(), kCrosSettingsPrefix,
+                               base::CompareCase::SENSITIVE))
+            continue;
 
-        // Skip preferences that should not be checked when the policy is set to
-        // a mandatory value.
-        if (!pref_mapping->check_for_mandatory())
-          continue;
+          // Skip preferences that should not be checked when the policy is set
+          // to a mandatory value.
+          if (!pref_case->check_for_mandatory())
+            continue;
 
-        PrefService* prefs =
-            pref_mapping->is_local_state() ? local_state : user_prefs;
-        // The preference must have been registered.
-        const PrefService::Preference* pref =
-            prefs->FindPreference(pref_mapping->pref().c_str());
-        ASSERT_TRUE(pref) << "Pref " << pref_mapping->pref().c_str()
-                          << " not registered";
+          PrefService* prefs =
+              pref_case->is_local_state() ? local_state : user_prefs;
+          // The preference must have been registered.
+          const PrefService::Preference* pref =
+              prefs->FindPreference(pref_case->pref().c_str());
+          ASSERT_TRUE(pref)
+              << "Pref " << pref_case->pref().c_str() << " not registered";
 
-        // Verify that setting the policy overrides the pref.
-        ClearProviderPolicy();
-        prefs->ClearPref(pref_mapping->pref().c_str());
-        EXPECT_TRUE(pref->IsDefaultValue());
-        EXPECT_TRUE(pref->IsUserModifiable());
-        EXPECT_FALSE(pref->IsUserControlled());
-        EXPECT_FALSE(pref->IsManaged());
+          // Verify that setting the policy overrides the pref.
+          ClearProviderPolicy();
+          prefs->ClearPref(pref_case->pref().c_str());
+          EXPECT_TRUE(pref->IsDefaultValue());
+          EXPECT_TRUE(pref->IsUserModifiable());
+          EXPECT_FALSE(pref->IsUserControlled());
+          EXPECT_FALSE(pref->IsManaged());
 
-        ASSERT_NO_FATAL_FAILURE(SetProviderPolicy(test_case->test_policy(),
-                                                  POLICY_LEVEL_MANDATORY));
-        EXPECT_FALSE(pref->IsDefaultValue());
-        EXPECT_FALSE(pref->IsUserModifiable());
-        EXPECT_FALSE(pref->IsUserControlled());
-        EXPECT_TRUE(pref->IsManaged());
+          ASSERT_NO_FATAL_FAILURE(SetProviderPolicy(pref_mapping->policies(),
+                                                    POLICY_LEVEL_MANDATORY));
+          if (pref_case->expect_default()) {
+            EXPECT_TRUE(pref->IsDefaultValue());
+            EXPECT_TRUE(pref->IsUserModifiable());
+            EXPECT_FALSE(pref->IsUserControlled());
+            EXPECT_FALSE(pref->IsManaged());
+          } else {
+            EXPECT_FALSE(pref->IsDefaultValue());
+            EXPECT_FALSE(pref->IsUserModifiable());
+            EXPECT_FALSE(pref->IsUserControlled());
+            EXPECT_TRUE(pref->IsManaged());
+          }
+          if (pref_case->value())
+            EXPECT_TRUE(pref->GetValue()->Equals(pref_case->value()));
+        }
       }
     }
   }
diff --git a/chrome/browser/push_messaging/push_messaging_service_unittest.cc b/chrome/browser/push_messaging/push_messaging_service_unittest.cc
index 6161f29..c24cbda 100644
--- a/chrome/browser/push_messaging/push_messaging_service_unittest.cc
+++ b/chrome/browser/push_messaging/push_messaging_service_unittest.cc
@@ -149,7 +149,6 @@
 #if defined(OS_ANDROID)
   instance_id::InstanceIDAndroid::ScopedBlockOnAsyncTasksForTesting
       block_async_;
-  instance_id::ScopedUseFakeInstanceIDAndroid use_fake_;
 #endif  // OS_ANDROID
 };
 
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/tools/jsbundler.py b/chrome/browser/resources/chromeos/accessibility/chromevox/tools/jsbundler.py
index 7b66755..fc5e4ba 100755
--- a/chrome/browser/resources/chromeos/accessibility/chromevox/tools/jsbundler.py
+++ b/chrome/browser/resources/chromeos/accessibility/chromevox/tools/jsbundler.py
@@ -72,6 +72,8 @@
   def GetOutPath(self):
     return self._out_path
 
+  def __str__(self):
+    return self.GetOutPath()
 
 class Bundle():
   '''An ordered list of sources without duplicates.'''
diff --git a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
index 3dd55c30a..14d5096 100644
--- a/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
+++ b/chrome/browser/resources/chromeos/wallpaper_manager/js/wallpaper_manager.js
@@ -372,35 +372,40 @@
  * do not depend on the download should be initialized here.
  */
 WallpaperManager.prototype.preDownloadDomInit_ = function() {
+  // Ensure that wallpaper manager exits preview if the window gets hidden.
+  document.addEventListener('visibilitychange', () => {
+    if (this.isDuringPreview_() && document.hidden)
+      $('cancel-preview-wallpaper').click();
+  });
   this.document_.defaultView.addEventListener(
       'resize', this.onResize_.bind(this));
   this.document_.defaultView.addEventListener(
       'keydown', this.onKeyDown_.bind(this));
-    $('minimize-button').addEventListener('click', function() {
-      chrome.app.window.current().minimize();
-    });
-    $('close-button').addEventListener('click', function() {
-      window.close();
-    });
-    window.addEventListener(Constants.WallpaperChangedBy3rdParty, e => {
-      this.currentWallpaper_ = e.detail.wallpaperFileName;
-      this.decorateCurrentWallpaperInfoBar_();
-      // Clear the check mark (if any). Do not try to locate the new wallpaper
-      // in the picker to avoid changing the selected category abruptly.
-      this.wallpaperGrid_.selectedItem = null;
-      this.disableDailyRefresh_();
-    });
-    var imagePicker = this.document_.body.querySelector('.image-picker');
-    imagePicker.addEventListener('scroll', function() {
-      var scrollTimer;
-      return () => {
-        imagePicker.classList.add('show-scroll-bar');
-        window.clearTimeout(scrollTimer);
-        scrollTimer = window.setTimeout(() => {
-          imagePicker.classList.remove('show-scroll-bar');
-        }, 500);
-      };
-    }());
+  $('minimize-button').addEventListener('click', function() {
+    chrome.app.window.current().minimize();
+  });
+  $('close-button').addEventListener('click', function() {
+    window.close();
+  });
+  window.addEventListener(Constants.WallpaperChangedBy3rdParty, e => {
+    this.currentWallpaper_ = e.detail.wallpaperFileName;
+    this.decorateCurrentWallpaperInfoBar_();
+    // Clear the check mark (if any). Do not try to locate the new wallpaper
+    // in the picker to avoid changing the selected category abruptly.
+    this.wallpaperGrid_.selectedItem = null;
+    this.disableDailyRefresh_();
+  });
+  var imagePicker = this.document_.body.querySelector('.image-picker');
+  imagePicker.addEventListener('scroll', function() {
+    var scrollTimer;
+    return () => {
+      imagePicker.classList.add('show-scroll-bar');
+      window.clearTimeout(scrollTimer);
+      scrollTimer = window.setTimeout(() => {
+        imagePicker.classList.remove('show-scroll-bar');
+      }, 500);
+    };
+  }());
 };
 
 /**
@@ -1084,7 +1089,8 @@
       this.document_.body.classList.remove('preview-animation');
       this.updateSpinnerVisibility_(false);
       // Exit full screen, but the window should still be maximized.
-      chrome.app.window.current().maximize();
+      if (chrome.app.window.current().isFullscreen())
+        chrome.app.window.current().maximize();
       // TODO(crbug.com/841968): Force refreshing the images. This is a
       // workaround until the issue is fixed.
       this.wallpaperGrid_.dataModel = null;
diff --git a/chrome/browser/resources/local_ntp/local_ntp.js b/chrome/browser/resources/local_ntp/local_ntp.js
index 378e1b2..d2c4925a 100644
--- a/chrome/browser/resources/local_ntp/local_ntp.js
+++ b/chrome/browser/resources/local_ntp/local_ntp.js
@@ -41,9 +41,6 @@
   DIM: 1 << 2,
 };
 
-/** @type {string} */
-let lastInput;
-
 /** @typedef {{inline: string, text: string}} */
 let RealboxOutput;
 
@@ -239,8 +236,8 @@
 
 // Local statics.
 
-/** @type {!Array<!AutocompleteMatch>} */
-let autocompleteMatches = [];
+/** @type {?AutocompleteResult} */
+let autocompleteResult = null;
 
 /**
  * The currently visible notification element. Null if no notification is
@@ -257,6 +254,13 @@
 let delayedHideNotification = null;
 
 /**
+ * Whether 'Enter' was pressed but did not navigate to a match due to matches
+ * being stale.
+ * @type {boolean}
+ */
+let enterWasPressed = false;
+
+/**
  * True if dark mode is enabled.
  * @type {boolean}
  */
@@ -275,8 +279,18 @@
  */
 let lastBlacklistedTile = null;
 
-/** @type {?number} */
-let lastRealboxFocusTime = null;
+/**
+ * The 'Enter' event that was ignored due to matches being stale. Will be used
+ * to navigate to the default match once up-to-date matches arrive.
+ * @type {?Event}
+ */
+let lastEnterEvent = null;
+
+/**
+ * The last queried input.
+ * @type {string|undefined}
+ */
+let lastInput;
 
 /**
  * Last text/inline autocompletion shown in the realbox (either by user input or
@@ -285,6 +299,9 @@
  */
 let lastOutput = {text: '', inline: ''};
 
+/** @type {?number} */
+let lastRealboxFocusTime = null;
+
 /**
  * The browser embeddedSearch.newTabPage object.
  * @type {Object}
@@ -1083,7 +1100,7 @@
  * @param {!Event} e
  */
 function navigateToMatch(match, e) {
-  const line = autocompleteMatches.indexOf(match);
+  const line = autocompleteResult.matches.indexOf(match);
   assert(line >= 0);
   assert(lastRealboxFocusTime);
   window.chrome.embeddedSearch.searchBox.openAutocompleteMatch(
@@ -1158,6 +1175,7 @@
   }
 
   populateAutocompleteMatches(result.matches);
+  autocompleteResult = result;
 
   $(IDS.REALBOX).focus();
 
@@ -1170,6 +1188,11 @@
   if (first && first.allowedToBeDefaultMatch) {
     selectMatchEl(assert($(IDS.REALBOX_MATCHES).firstElementChild));
     updateRealboxOutput({inline: first.inlineAutocompletion});
+
+    if (enterWasPressed) {
+      assert(lastEnterEvent);
+      navigateToMatch(first, lastEnterEvent);
+    }
   }
 }
 
@@ -1178,7 +1201,7 @@
   const realboxEl = $(IDS.REALBOX);
   if (!realboxEl.value || realboxEl.selectionStart !== 0 ||
       realboxEl.selectionEnd !== realboxEl.value.length ||
-      autocompleteMatches.length === 0) {
+      !autocompleteResult || autocompleteResult.matches.length === 0) {
     // Only handle cut/copy when realbox has content and it's all selected.
     return;
   }
@@ -1188,7 +1211,7 @@
     return matchEl.classList.contains(CLASSES.SELECTED);
   });
 
-  const selectedMatch = autocompleteMatches[selected];
+  const selectedMatch = autocompleteResult.matches[selected];
   if (selectedMatch && !selectedMatch.isSearchType) {
     e.clipboardData.setData('text/plain', selectedMatch.destinationUrl);
     e.preventDefault();
@@ -1235,7 +1258,7 @@
     // It doesn't really make sense to use fillFromMatch() here as the focus
     // change drops the selection (and is probably just noisy to
     // screenreaders).
-    const newFill = autocompleteMatches[selectedIndex].fillIntoEdit;
+    const newFill = autocompleteResult.matches[selectedIndex].fillIntoEdit;
     updateRealboxOutput({moveCursorToEnd: true, inline: '', text: newFill});
   }
 }
@@ -1302,11 +1325,22 @@
     return matchEl.classList.contains(CLASSES.SELECTED);
   });
 
-  assert(autocompleteMatches.length === matchEls.length);
+  assert(autocompleteResult.matches.length === matchEls.length);
 
   if (key === 'Enter') {
-    if (matchEls[selected] && matchEls.concat(realboxEl).includes(e.target)) {
-      navigateToMatch(autocompleteMatches[selected], e);
+    if (matchEls.concat(realboxEl).includes(e.target)) {
+      if (lastInput === autocompleteResult.input) {
+        if (autocompleteResult.matches[selected]) {
+          navigateToMatch(autocompleteResult.matches[selected], e);
+        }
+      } else {
+        // User typed and pressed 'Enter' too quickly. Ignore this for now
+        // because the matches are stale. Navigate to the default match (if one
+        // exists) once the up-to-date results arrive.
+        enterWasPressed = true;
+        lastEnterEvent = e;
+        e.preventDefault();
+      }
     }
     return;
   }
@@ -1324,7 +1358,7 @@
 
   if (key === 'Delete') {
     if (e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey) {
-      const selectedMatch = autocompleteMatches[selected];
+      const selectedMatch = autocompleteResult.matches[selected];
       if (selectedMatch && selectedMatch.supportsDeletion) {
         window.chrome.embeddedSearch.searchBox.deleteAutocompleteMatch(
             selected);
@@ -1365,7 +1399,7 @@
     matchEls[newSelected].focus();
   }
 
-  const newMatch = autocompleteMatches[newSelected];
+  const newMatch = autocompleteResult.matches[newSelected];
   const newFill = newMatch.fillIntoEdit;
   let newInline = '';
   if (newMatch.allowedToBeDefaultMatch) {
@@ -1557,7 +1591,6 @@
   const hasMatches = matches.length > 0;
   setRealboxMatchesVisible(hasMatches);
   setRealboxWrapperListenForKeydown(hasMatches);
-  autocompleteMatches = matches;
 }
 
 /**
@@ -1866,9 +1899,8 @@
   $(IDS.ATTRIBUTION).style.display = show ? '' : 'none';
 }
 
-/** @suppress {checkTypes} */
 function clearAutocompleteMatches() {
-  autocompleteMatches = [];
+  autocompleteResult = null;
   window.chrome.embeddedSearch.searchBox.stopAutocomplete(
       /*clearResult=*/ true);
   // Autocomplete sends updates once it is stopped. Invalidate those results
diff --git a/chrome/browser/signin/e2e_tests/live_sign_in_test.cc b/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
index fb3e13a..f6c4b65 100644
--- a/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
+++ b/chrome/browser/signin/e2e_tests/live_sign_in_test.cc
@@ -150,21 +150,10 @@
   }
 };
 
-// Consistently timing out on windows.  http://crbug.com/1025220
-#if defined(OS_WIN)
-#define MAYBE_SimpleSignInFlow DISABLED_SimpleSignInFlow
-#define MAYBE_WebSignOut DISABLED_WebSignOut
-#define MAYBE_TurnOffSync DISABLED_TurnOffSync
-#else
-#define MAYBE_SimpleSignInFlow SimpleSignInFlow
-#define MAYBE_WebSignOut WebSignOut
-#define MAYBE_TurnOffSync TurnOffSync
-#endif
-
 // Sings in an account through the settings page and checks that the account is
 // added to Chrome. Sync should be disabled because the test doesn't pass
 // through the Sync confirmation dialog.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MAYBE_SimpleSignInFlow) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTest, SimpleSignInFlow) {
   TestAccount ta;
   CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", ta));
   SignInFromSettings(ta);
@@ -185,7 +174,7 @@
 // Sync is enabled.
 // Then, signs out on the web and checks that the account is removed from
 // cookies and Sync paused error is displayed.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MAYBE_WebSignOut) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTest, WebSignOut) {
   TestAccount test_account;
   CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account));
   TurnOnSync(test_account);
@@ -265,7 +254,7 @@
 // Sync is enabled. Signs in a second account on the web.
 // Then, turns Sync off from the settings page and checks that both accounts are
 // removed from Chrome and from cookies.
-IN_PROC_BROWSER_TEST_F(LiveSignInTest, MAYBE_TurnOffSync) {
+IN_PROC_BROWSER_TEST_F(LiveSignInTest, TurnOffSync) {
   TestAccount test_account_1;
   CHECK(GetTestAccountsUtil()->GetAccount("TEST_ACCOUNT_1", test_account_1));
   TurnOnSync(test_account_1);
diff --git a/chrome/browser/signin/e2e_tests/live_test.cc b/chrome/browser/signin/e2e_tests/live_test.cc
index 2426a7e0..83d98ff 100644
--- a/chrome/browser/signin/e2e_tests/live_test.cc
+++ b/chrome/browser/signin/e2e_tests/live_test.cc
@@ -31,6 +31,8 @@
   // Only run live tests when specified.
   auto* cmd_line = base::CommandLine::ForCurrentProcess();
   if (!cmd_line->HasSwitch(kRunLiveTestFlag)) {
+    LOG(INFO) << "This test should get skipped.";
+    skip_test_ = true;
     GTEST_SKIP();
   }
   base::FilePath root_path;
@@ -41,5 +43,12 @@
   InProcessBrowserTest::SetUp();
 }
 
+void LiveTest::TearDown() {
+  // This test was skipped, no need to tear down.
+  if (skip_test_)
+    return;
+  InProcessBrowserTest::TearDown();
+}
+
 }  // namespace test
 }  // namespace signin
diff --git a/chrome/browser/signin/e2e_tests/live_test.h b/chrome/browser/signin/e2e_tests/live_test.h
index 7c4dc12..c001eb8 100644
--- a/chrome/browser/signin/e2e_tests/live_test.h
+++ b/chrome/browser/signin/e2e_tests/live_test.h
@@ -15,6 +15,7 @@
  protected:
   void SetUpInProcessBrowserTestFixture() override;
   void SetUp() override;
+  void TearDown() override;
 
   const TestAccountsUtil* GetTestAccountsUtil() const {
     return &test_accounts_;
@@ -22,6 +23,7 @@
 
  private:
   TestAccountsUtil test_accounts_;
+  bool skip_test_ = false;
 };
 
 }  // namespace test
diff --git a/chrome/browser/ui/tab_helpers.cc b/chrome/browser/ui/tab_helpers.cc
index e99c9fcd..9545a0c 100644
--- a/chrome/browser/ui/tab_helpers.cc
+++ b/chrome/browser/ui/tab_helpers.cc
@@ -101,6 +101,7 @@
 #include "content/public/browser/web_contents.h"
 #include "extensions/buildflags/buildflags.h"
 #include "media/base/media_switches.h"
+#include "ppapi/buildflags/buildflags.h"
 #include "printing/buildflags/buildflags.h"
 
 #if defined(OS_ANDROID)
@@ -112,10 +113,8 @@
 #include "chrome/browser/ui/android/view_android_helper.h"
 #else
 #include "chrome/browser/banners/app_banner_manager_desktop.h"
-#include "chrome/browser/plugins/plugin_observer.h"
 #include "chrome/browser/safe_browsing/safe_browsing_tab_observer.h"
 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
-#include "chrome/browser/ui/hung_plugin_tab_helper.h"
 #include "chrome/browser/ui/intent_picker_tab_helper.h"
 #include "chrome/browser/ui/sad_tab_helper.h"
 #include "chrome/browser/ui/search/search_tab_helper.h"
@@ -131,12 +130,6 @@
 #include "chrome/browser/ui/hats/hats_helper.h"
 #endif
 
-#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
-#include "chrome/browser/offline_pages/android/auto_fetch_page_load_watcher.h"
-#include "chrome/browser/offline_pages/offline_page_tab_helper.h"
-#include "chrome/browser/offline_pages/recent_tab_helper.h"
-#endif
-
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
 #include "chrome/browser/captive_portal/captive_portal_tab_helper.h"
 #endif
@@ -150,14 +143,25 @@
 #include "extensions/browser/view_type_utils.h"
 #endif
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-#include "chrome/browser/supervised_user/supervised_user_navigation_observer.h"
+#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
+#include "chrome/browser/offline_pages/android/auto_fetch_page_load_watcher.h"
+#include "chrome/browser/offline_pages/offline_page_tab_helper.h"
+#include "chrome/browser/offline_pages/recent_tab_helper.h"
+#endif
+
+#if BUILDFLAG(ENABLE_PLUGINS)
+#include "chrome/browser/plugins/plugin_observer.h"
+#include "chrome/browser/ui/hung_plugin_tab_helper.h"
 #endif
 
 #if BUILDFLAG(ENABLE_PRINTING)
 #include "chrome/browser/printing/printing_init.h"
 #endif
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+#include "chrome/browser/supervised_user/supervised_user_navigation_observer.h"
+#endif
+
 using content::WebContents;
 
 namespace {
@@ -301,13 +305,11 @@
       web_contents);
   extensions::WebNavigationTabObserver::CreateForWebContents(web_contents);
   FramebustBlockTabHelper::CreateForWebContents(web_contents);
-  HungPluginTabHelper::CreateForWebContents(web_contents);
   IntentPickerTabHelper::CreateForWebContents(web_contents);
   JavaScriptDialogTabHelper::CreateForWebContents(web_contents);
   ManagePasswordsUIController::CreateForWebContents(web_contents);
   pdf::PDFWebContentsHelper::CreateForWebContentsWithClient(
       web_contents, std::make_unique<ChromePDFWebContentsHelperClient>());
-  PluginObserver::CreateForWebContents(web_contents);
   SadTabHelper::CreateForWebContents(web_contents);
   safe_browsing::SafeBrowsingTabObserver::CreateForWebContents(web_contents);
   SearchTabHelper::CreateForWebContents(web_contents);
@@ -343,12 +345,6 @@
 
   // --- Feature tab helpers behind flags ---
 
-#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
-  offline_pages::OfflinePageTabHelper::CreateForWebContents(web_contents);
-  offline_pages::RecentTabHelper::CreateForWebContents(web_contents);
-  offline_pages::AutoFetchPageLoadWatcher::CreateForWebContents(web_contents);
-#endif
-
 #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
   CaptivePortalTabHelper::CreateForWebContents(web_contents);
 #endif
@@ -361,14 +357,24 @@
     web_app::WebAppMetrics::Get(profile);
 #endif
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  SupervisedUserNavigationObserver::CreateForWebContents(web_contents);
+#if BUILDFLAG(ENABLE_OFFLINE_PAGES)
+  offline_pages::OfflinePageTabHelper::CreateForWebContents(web_contents);
+  offline_pages::RecentTabHelper::CreateForWebContents(web_contents);
+  offline_pages::AutoFetchPageLoadWatcher::CreateForWebContents(web_contents);
 #endif
 
+#if BUILDFLAG(ENABLE_PLUGINS)
+  HungPluginTabHelper::CreateForWebContents(web_contents);
+  PluginObserver::CreateForWebContents(web_contents);
+#endif
 #if BUILDFLAG(ENABLE_PRINTING)
   printing::InitializePrinting(web_contents);
 #endif
 
+#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
+  SupervisedUserNavigationObserver::CreateForWebContents(web_contents);
+#endif
+
   if (dom_distiller::IsDomDistillerEnabled()) {
     dom_distiller::WebContentsMainFrameObserver::CreateForWebContents(
         web_contents);
diff --git a/chrome/browser/ui/webui/print_preview/policy_settings.cc b/chrome/browser/ui/webui/print_preview/policy_settings.cc
index 6d97de7a..cdd3632 100644
--- a/chrome/browser/ui/webui/print_preview/policy_settings.cc
+++ b/chrome/browser/ui/webui/print_preview/policy_settings.cc
@@ -12,7 +12,7 @@
 // static
 void PolicySettings::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
-  registry->RegisterListPref(prefs::kPrinterTypeBlacklist);
+  registry->RegisterListPref(prefs::kPrinterTypeDenyList);
   registry->RegisterBooleanPref(prefs::kPrintHeaderFooter, true);
 #if defined(OS_CHROMEOS)
   registry->RegisterIntegerPref(prefs::kPrintingAllowedBackgroundGraphicsModes,
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 35bd7cd..4d2c655a 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -251,9 +251,8 @@
 // Name of a dictionary field indicating whether the 'Save to PDF' destination
 // is disabled.
 const char kPdfPrinterDisabled[] = "pdfPrinterDisabled";
-// TODO(dhoss): Change this comment when the policy name is changed.
 // Name of a dictionary field indicating whether the destinations are managed by
-// the PrinterTypeBlacklist enterprise policy.
+// the PrinterTypeDenyList enterprise policy.
 const char kDestinationsManaged[] = "destinationsManaged";
 // Name of a dictionary field holding the cloud print URL.
 const char kCloudPrintURL[] = "cloudPrintURL";
@@ -564,7 +563,7 @@
   print_preview_ui()->SetPreviewUIId();
   // Now that the UI is initialized, any future account changes will require
   // a printer list refresh.
-  ReadPrinterTypeBlacklistFromPrefs();
+  ReadPrinterTypeDenyListFromPrefs();
   RegisterForGaiaCookieChanges();
 }
 
@@ -575,7 +574,7 @@
   print_preview_ui()->ClearPreviewUIId();
   preview_callbacks_.clear();
   preview_failures_.clear();
-  printer_type_blacklist_.clear();
+  printer_type_deny_list_.clear();
   UnregisterForGaiaCookieChanges();
 }
 
@@ -589,30 +588,32 @@
   return prefs;
 }
 
-void PrintPreviewHandler::ReadPrinterTypeBlacklistFromPrefs() {
+void PrintPreviewHandler::ReadPrinterTypeDenyListFromPrefs() {
   PrefService* prefs = GetPrefs();
-  if (!prefs->HasPrefPath(prefs::kPrinterTypeBlacklist))
+  if (!prefs->HasPrefPath(prefs::kPrinterTypeDenyList))
     return;
 
-  const base::Value* blacklist_types = prefs->Get(prefs::kPrinterTypeBlacklist);
-  if (!blacklist_types || !blacklist_types->is_list())
+  const base::Value* deny_list_types = prefs->Get(prefs::kPrinterTypeDenyList);
+  if (!deny_list_types || !deny_list_types->is_list())
     return;
 
-  for (const base::Value& blacklist_type : blacklist_types->GetList()) {
-    if (!blacklist_type.is_string())
+  for (const base::Value& deny_list_type : deny_list_types->GetList()) {
+    if (!deny_list_type.is_string())
       continue;
 
-    const std::string& blacklist_str = blacklist_type.GetString();
-    if (blacklist_str == "privet")
-      printer_type_blacklist_.insert(kPrivetPrinter);
-    else if (blacklist_str == "extension")
-      printer_type_blacklist_.insert(kExtensionPrinter);
-    else if (blacklist_str == "pdf")
-      printer_type_blacklist_.insert(kPdfPrinter);
-    else if (blacklist_str == "local")
-      printer_type_blacklist_.insert(kLocalPrinter);
-    else if (blacklist_str == "cloud")
-      printer_type_blacklist_.insert(kCloudPrinter);
+    // The expected printer type strings are enumerated in
+    // components/policy/resources/policy_templates.json
+    const std::string& deny_list_str = deny_list_type.GetString();
+    if (deny_list_str == "privet")
+      printer_type_deny_list_.insert(kPrivetPrinter);
+    else if (deny_list_str == "extension")
+      printer_type_deny_list_.insert(kExtensionPrinter);
+    else if (deny_list_str == "pdf")
+      printer_type_deny_list_.insert(kPdfPrinter);
+    else if (deny_list_str == "local")
+      printer_type_deny_list_.insert(kLocalPrinter);
+    else if (deny_list_str == "cloud")
+      printer_type_deny_list_.insert(kCloudPrinter);
   }
 }
 
@@ -660,8 +661,8 @@
   PrinterType printer_type = static_cast<PrinterType>(type);
 
   // Immediately resolve the callback without fetching printers if the printer
-  // type is blacklisted
-  if (base::Contains(printer_type_blacklist_, printer_type)) {
+  // type is on the deny list.
+  if (base::Contains(printer_type_deny_list_, printer_type)) {
     ResolveJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
@@ -709,8 +710,8 @@
   }
   PrinterType printer_type = static_cast<PrinterType>(type);
 
-  // Reject the callback if the printer type is blacklisted
-  if (base::Contains(printer_type_blacklist_, printer_type)) {
+  // Reject the callback if the printer type is on the deny list.
+  if (base::Contains(printer_type_deny_list_, printer_type)) {
     RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
@@ -1085,12 +1086,12 @@
 
   initial_settings.SetBoolKey(
       kPdfPrinterDisabled,
-      base::Contains(printer_type_blacklist_, kPdfPrinter));
+      base::Contains(printer_type_deny_list_, kPdfPrinter));
 
-  const bool destinationsManaged =
-      !printer_type_blacklist_.empty() &&
-      prefs->IsManagedPreference(prefs::kPrinterTypeBlacklist);
-  initial_settings.SetBoolKey(kDestinationsManaged, destinationsManaged);
+  const bool destinations_managed =
+      !printer_type_deny_list_.empty() &&
+      prefs->IsManagedPreference(prefs::kPrinterTypeDenyList);
+  initial_settings.SetBoolKey(kDestinationsManaged, destinations_managed);
 
   base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
   initial_settings.SetBoolKey(kIsInKioskAutoPrintMode,
@@ -1421,7 +1422,7 @@
 void PrintPreviewHandler::RegisterForGaiaCookieChanges() {
   DCHECK(!identity_manager_);
   cloud_print_enabled_ =
-      !base::Contains(printer_type_blacklist_, kCloudPrinter) &&
+      !base::Contains(printer_type_deny_list_, kCloudPrinter) &&
       GetPrefs()->GetBoolean(prefs::kCloudPrintSubmitEnabled);
 
   if (!cloud_print_enabled_)
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index 912e9c0..102da1f8 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -136,10 +136,10 @@
                            MANUAL_DummyTest);
   friend class PrintPreviewHandlerTest;
   FRIEND_TEST_ALL_PREFIXES(PrintPreviewHandlerTest, GetPrinters);
-  FRIEND_TEST_ALL_PREFIXES(PrintPreviewHandlerTest, GetNoBlacklistedPrinters);
+  FRIEND_TEST_ALL_PREFIXES(PrintPreviewHandlerTest, GetNoDenyListPrinters);
   FRIEND_TEST_ALL_PREFIXES(PrintPreviewHandlerTest, GetPrinterCapabilities);
   FRIEND_TEST_ALL_PREFIXES(PrintPreviewHandlerTest,
-                           GetNoBlacklistedPrinterCapabilities);
+                           GetNoDenyListPrinterCapabilities);
   FRIEND_TEST_ALL_PREFIXES(PrintPreviewHandlerTest, Print);
   FRIEND_TEST_ALL_PREFIXES(PrintPreviewHandlerTest, GetPreview);
   FRIEND_TEST_ALL_PREFIXES(PrintPreviewHandlerTest, SendPreviewUpdates);
@@ -157,9 +157,9 @@
 
   PrefService* GetPrefs() const;
 
-  // Checks policy preferences for a blacklist of printer types and initializes
+  // Checks policy preferences for a deny list of printer types and initializes
   // the set that stores them.
-  void ReadPrinterTypeBlacklistFromPrefs();
+  void ReadPrinterTypeDenyListFromPrefs();
 
   // Whether the the handler should be receiving messages from the renderer to
   // forward to the Print Preview JS in response to preview request with id
@@ -358,8 +358,8 @@
   // Set of preview request ids for failed previews.
   std::set<int> preview_failures_;
 
-  // Set of blacklisted printer types.
-  std::set<PrinterType> printer_type_blacklist_;
+  // Set of printer types on the deny list.
+  std::set<PrinterType> printer_type_deny_list_;
 
   base::WeakPtrFactory<PrintPreviewHandler> weak_factory_{this};
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
index e5e2cde..3b6994d 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
@@ -590,15 +590,15 @@
   }
 }
 
-// Validates the 'printing.printer_type_blacklist' pref by blacklisting the
-// extension and privet printer types. A 'getPrinters' Web UI message is then
-// called for all three fetchable printer types; only local printers should be
-// successfully fetched.
-TEST_F(PrintPreviewHandlerTest, GetNoBlacklistedPrinters) {
-  base::Value::ListStorage blacklist;
-  blacklist.push_back(base::Value("extension"));
-  blacklist.push_back(base::Value("privet"));
-  prefs()->Set(prefs::kPrinterTypeBlacklist, base::Value(std::move(blacklist)));
+// Validates the 'printing.printer_type_deny_list' pref by placing the extension
+// and privet printer types on a deny list. A 'getPrinters' Web UI message is
+// then called for all three fetchable printer types; only local printers should
+// be successfully fetched.
+TEST_F(PrintPreviewHandlerTest, GetNoDenyListPrinters) {
+  base::Value::ListStorage deny_list;
+  deny_list.push_back(base::Value("extension"));
+  deny_list.push_back(base::Value("privet"));
+  prefs()->Set(prefs::kPrinterTypeDenyList, base::Value(std::move(deny_list)));
   Initialize();
 
   size_t expected_callbacks = 1;
@@ -610,8 +610,9 @@
     SendGetPrinters(type, callback_id_in);
 
     // Start with 1 call from initial settings, then add 2 more for each printer
-    // type that isn't blacklisted (one for printers-added, and one for the
-    // response), and only 1 more for each blacklisted type (just for response).
+    // type that isn't on the deny list (one for printers-added, and one for the
+    // response), and only 1 more for each type on the deny list (just for
+    // response).
     const bool is_allowed_type = type == kLocalPrinter;
     EXPECT_EQ(is_allowed_type, handler()->CalledOnlyForType(type));
     expected_callbacks += is_allowed_type ? 2 : 1;
@@ -673,15 +674,15 @@
   }
 }
 
-// Validates the 'printing.printer_type_blacklist' pref by blacklisting the
-// local and PDF printer types. A 'getPrinterCapabilities' Web UI message is
-// then called for all supported printer types; only privet and extension
+// Validates the 'printing.printer_type_deny_list' pref by placing the local and
+// PDF printer types on the deny list. A 'getPrinterCapabilities' Web UI message
+// is then called for all supported printer types; only privet and extension
 // printer capabilties should be successfully fetched.
-TEST_F(PrintPreviewHandlerTest, GetNoBlacklistedPrinterCapabilities) {
-  base::Value::ListStorage blacklist;
-  blacklist.push_back(base::Value("local"));
-  blacklist.push_back(base::Value("pdf"));
-  prefs()->Set(prefs::kPrinterTypeBlacklist, base::Value(std::move(blacklist)));
+TEST_F(PrintPreviewHandlerTest, GetNoDenyListPrinterCapabilities) {
+  base::Value::ListStorage deny_list;
+  deny_list.push_back(base::Value("local"));
+  deny_list.push_back(base::Value("pdf"));
+  prefs()->Set(prefs::kPrinterTypeDenyList, base::Value(std::move(deny_list)));
   Initialize();
 
   // Check all four printer types that implement
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 3051127d..8b0ab89 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -1278,7 +1278,7 @@
 const char kInvertNotificationShown[] = "invert_notification_version_2_shown";
 
 // A pref holding the list of printer types to be disabled.
-const char kPrinterTypeBlacklist[] = "printing.printer_type_blacklist";
+const char kPrinterTypeDenyList[] = "printing.printer_type_deny_list";
 
 // The default value for the 'Headers and footers' checkbox, in Print Preview.
 // Takes priority over kPrintPreviewStickySettings if set.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 85371942..e6ca61d 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -406,7 +406,7 @@
 
 extern const char kInvertNotificationShown[];
 
-extern const char kPrinterTypeBlacklist[];
+extern const char kPrinterTypeDenyList[];
 extern const char kPrintingEnabled[];
 extern const char kPrintHeaderFooter[];
 extern const char kPrintPreviewDisabled[];
diff --git a/chrome/common/pref_names_util.cc b/chrome/common/pref_names_util.cc
index 3ab666f..0a113399 100644
--- a/chrome/common/pref_names_util.cc
+++ b/chrome/common/pref_names_util.cc
@@ -20,7 +20,7 @@
 // Chrome, there is an option to turn off captions styles, so any time the
 // captions are on, the styles should take priority.
 std::string AddCSSImportant(std::string css_string) {
-  return css_string + " !important";
+  return css_string.empty() ? "" : css_string + " !important";
 }
 
 }  // namespace
@@ -82,6 +82,12 @@
   style.text_shadow = AddCSSImportant(
       prefs->GetString(prefs::kAccessibilityCaptionsTextShadow));
 
+  if (style.text_size.empty() && style.font_family.empty() &&
+      style.text_color.empty() && style.background_color.empty() &&
+      style.text_shadow.empty()) {
+    return base::nullopt;
+  }
+
   return style;
 }
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ba94c48..d9e0146f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1469,7 +1469,10 @@
         "//url",
       ]
 
-      sources += [ "../browser/policy/accessibility_policy_browsertest.cc" ]
+      sources += [
+        "../browser/policy/accessibility_policy_browsertest.cc",
+        "../browser/policy/arc_policy_browsertest.cc",
+      ]
     }
 
     if (include_js_tests) {
diff --git a/chrome/test/DEPS b/chrome/test/DEPS
index 881c92f..ea91348 100644
--- a/chrome/test/DEPS
+++ b/chrome/test/DEPS
@@ -17,6 +17,7 @@
   "+components/domain_reliability",
   "+components/download/public/common",
   "+components/favicon/core",
+  "+components/gcm_driver/instance_id",
   "+components/google/core/common",
   "+components/guest_view/browser",
   "+components/history/content",
diff --git a/chrome/test/base/chrome_test_suite.h b/chrome/test/base/chrome_test_suite.h
index 56027a2..5bd027b1 100644
--- a/chrome/test/base/chrome_test_suite.h
+++ b/chrome/test/base/chrome_test_suite.h
@@ -31,7 +31,6 @@
   // Alternative path to browser binaries.
   base::FilePath browser_dir_;
 
-
   DISALLOW_COPY_AND_ASSIGN(ChromeTestSuite);
 };
 
diff --git a/chrome/test/base/chrome_unit_test_suite.h b/chrome/test/base/chrome_unit_test_suite.h
index 42dc9c3a..8455ed5f5 100644
--- a/chrome/test/base/chrome_unit_test_suite.h
+++ b/chrome/test/base/chrome_unit_test_suite.h
@@ -9,8 +9,13 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/test/test_discardable_memory_allocator.h"
+#include "build/build_config.h"
 #include "chrome/test/base/chrome_test_suite.h"
 
+#if defined(OS_ANDROID)
+#include "components/gcm_driver/instance_id/scoped_use_fake_instance_id_android.h"
+#endif
+
 // Test suite for unit tests. Creates additional stub services that are not
 // needed for browser tests (e.g. a TestingBrowserProcess).
 class ChromeUnitTestSuite : public ChromeTestSuite {
@@ -31,6 +36,14 @@
  private:
   base::TestDiscardableMemoryAllocator discardable_memory_allocator_;
 
+#if defined(OS_ANDROID)
+  // InstanceID can make network requests which will time out and make tests
+  // slow. Insert a fake one in all tests, as the prefetch service (perhaps
+  // among others in the future) causes us to use the InstanceID in a posted
+  // task which delays test completion.
+  instance_id::ScopedUseFakeInstanceIDAndroid fake_instance_id_android_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(ChromeUnitTestSuite);
 };
 
diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_self/test.js b/chrome/test/data/extensions/api_test/extension_options/embed_self/test.js
index b6d4c02..75191ef 100644
--- a/chrome/test/data/extensions/api_test/extension_options/embed_self/test.js
+++ b/chrome/test/data/extensions/api_test/extension_options/embed_self/test.js
@@ -14,10 +14,10 @@
 }
 
 function assertSenderIsOptionsPage(sender) {
-  chrome.test.assertEq({
-    'id': chrome.runtime.id,
-    'url': chrome.runtime.getURL('options.html')
-  }, sender);
+  var url = chrome.runtime.getURL('options.html');
+  var origin = new URL(url).origin;
+  chrome.test.assertEq(
+      {'id': chrome.runtime.id, 'url': url, 'origin': origin}, sender);
 }
 
 chrome.test.runTests([
diff --git a/chrome/test/data/extensions/api_test/messaging/connect/frame.js b/chrome/test/data/extensions/api_test/messaging/connect/frame.js
index a6f9df1c3..f2a4eff5 100644
--- a/chrome/test/data/extensions/api_test/messaging/connect/frame.js
+++ b/chrome/test/data/extensions/api_test/messaging/connect/frame.js
@@ -16,7 +16,9 @@
 });
 
 // continuation of testSendMessageFromFrame()
-if (location.search.lastIndexOf('?testSendMessageFromFrame', 0) === 0) {
+if (location.search.lastIndexOf('?testSendMessageFromFrame', 0) === 0 ||
+    location.search.lastIndexOf('?testSendMessageFromSandboxedFrame', 0) ===
+        0) {
   chrome.runtime.sendMessage({frameUrl: location.href});
 } else if (location.search === '?testConnectChildFrameAndNavigateSetup') {
   // continuation of connectChildFrameAndNavigate() 1/2
diff --git a/chrome/test/data/extensions/api_test/messaging/connect/manifest.json b/chrome/test/data/extensions/api_test/messaging/connect/manifest.json
index e9af6db..0ec2ada 100644
--- a/chrome/test/data/extensions/api_test/messaging/connect/manifest.json
+++ b/chrome/test/data/extensions/api_test/messaging/connect/manifest.json
@@ -17,6 +17,7 @@
       "all_frames": true,
       "matches": [
         "http://*/*?testSendMessageFromFrame*",
+        "http://*/*?testSendMessageFromSandboxedFrame*",
         "http://*/*?testConnectChildFrameAndNavigate*"
       ],
       "js": ["frame.js"]
diff --git a/chrome/test/data/extensions/api_test/messaging/connect/page.js b/chrome/test/data/extensions/api_test/messaging/connect/page.js
index 02c7675d..62ad0736 100644
--- a/chrome/test/data/extensions/api_test/messaging/connect/page.js
+++ b/chrome/test/data/extensions/api_test/messaging/connect/page.js
@@ -15,6 +15,8 @@
       testSendMessageFromTab();
     } else if (msg.testSendMessageFromFrame) {
       testSendMessageFromFrame();
+    } else if (msg.testSendMessageFromSandboxedFrame) {
+      testSendMessageFromSandboxedFrame();
     } else if (msg.testSendMessageToFrame) {
       port.postMessage('from_main');
     } else if (msg.testDisconnect) {
@@ -63,8 +65,8 @@
 function testSendMessageFromFrame() {
   // Add two frames. The content script declared in manifest.json (frame.js)
   // runs in frames whose URL matches ?testSendMessageFromFrame.
-  // frame.js sends a message to the background page, which checks that
-  // sender.frameId exists and is different for both frames.
+  // frame.js sends a message to the background page, which checks that the
+  // sender has the expected frameId, url, origin, tab and runtime id.
   for (var i = 0; i < 2; ++i) {
     var f = document.createElement('iframe');
     f.src = '?testSendMessageFromFrame' + i;
@@ -72,6 +74,19 @@
   }
 }
 
+function testSendMessageFromSandboxedFrame() {
+  // Add two frames. The content script declared in manifest.json (frame.js)
+  // runs in frames whose URL matches ?testSendMessageFromSandboxFrame.
+  // frame.js sends a message to the background page, which checks that the
+  // sender has the expected frameId, url, origin, tab and runtime id.
+  for (var i = 2; i < 4; ++i) {
+    var f = document.createElement('iframe');
+    f.sandbox = 'allow-scripts';
+    f.src = '?testSendMessageFromSandboxedFrame' + i;
+    document.body.appendChild(f);
+  }
+}
+
 function testConnectChildFrameAndNavigateSetup() {
   var frames = document.querySelectorAll('iframe');
   for (var i = 0; i < frames.length; ++i) {
@@ -106,6 +121,6 @@
 
 // For test sendMessage.
 chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
-  chrome.test.assertEq({id: chrome.runtime.id}, sender);
+  chrome.test.assertEq({id: chrome.runtime.id, origin: 'null'}, sender);
   sendResponse({success: (request.step2 == 1)});
 });
diff --git a/chrome/test/data/extensions/api_test/messaging/connect/test.js b/chrome/test/data/extensions/api_test/messaging/connect/test.js
index 0310b73..d748c27 100644
--- a/chrome/test/data/extensions/api_test/messaging/connect/test.js
+++ b/chrome/test/data/extensions/api_test/messaging/connect/test.js
@@ -23,9 +23,23 @@
 
   chrome.test.assertEq(expected.frameId, actual.frameId);
   chrome.test.assertEq(expected.url, actual.url);
+  chrome.test.assertEq(expected.origin, actual.origin);
   chrome.test.assertEq(expected.id, actual.id);
 }
 
+function createExpectedSenderWithOrigin(tab, frameId, url, origin, id) {
+  return {tab: tab, frameId: frameId, url: url, origin: origin, id: id};
+}
+
+function createExpectedSender(tab, frameId, url, id) {
+  var originUrl = null;
+  if (tab.url) {
+    var tabUrl = new URL(tab.url);
+    originUrl = tabUrl.origin;
+  }
+  return createExpectedSenderWithOrigin(tab, frameId, url, originUrl, id);
+}
+
 chrome.test.getConfig(function(config) {
   chrome.test.runTests([
     function setupTestTab() {
@@ -71,12 +85,10 @@
     // Tests that postMessage from the tab and its response works.
     function postMessageFromTab() {
       listenOnce(chrome.runtime.onConnect, function(port) {
-        let expectedSender = {
-          tab: testTab,
-          frameId: 0, // Main frame
-          url: testTab.url,
-          id: chrome.runtime.id,
-        };
+        expectedSender = createExpectedSender(
+            testTab,
+            0,  // Main frame
+            testTab.url, chrome.runtime.id);
         compareSenders(expectedSender, port.sender);
         listenOnce(port.onMessage, function(msg) {
           chrome.test.assertTrue(msg.testPostMessageFromTab);
@@ -96,18 +108,15 @@
     // Tests receiving a request from a content script and responding.
     function sendMessageFromTab() {
       var doneListening = listenForever(
-        chrome.runtime.onMessage,
-        function(request, sender, sendResponse) {
-          let expectedSender = {
-            tab: testTab,
-            frameId: 0, // Main frame
-            url: testTab.url,
-            id: chrome.runtime.id
-          };
+        chrome.runtime.onMessage, function(request, sender, sendResponse) {
+          expectedSender = createExpectedSender(
+              testTab,
+              0,  // Main frame
+              testTab.url, chrome.runtime.id);
           compareSenders(expectedSender, sender);
           if (request.step == 1) {
             // Step 1: Page should send another request for step 2.
-            chrome.test.log("sendMessageFromTab: got step 1");
+            chrome.test.log('sendMessageFromTab: got step 1');
             sendResponse({nextStep: true});
           } else {
             // Step 2.
@@ -123,49 +132,24 @@
       chrome.test.log("sendMessageFromTab: sent first message to tab");
     },
 
-    // Tests that a message from a child frame has a correct frameId.
+    // Tests that a message from a child frame is constructed properly.
     function sendMessageFromFrameInTab() {
-      var actualSenders = [];
-      var doneListening = listenForever(
-        chrome.runtime.onMessage,
-        function(request, sender, sendResponse) {
-          actualSenders.push(sender);
-
-          // testSendMessageFromFrame() in page.js adds 2 frames. Wait for
-          // messages from each.
-          if (actualSenders.length < 2)
-            return;
-
-          chrome.webNavigation.getAllFrames({
-            tabId: testTab.id
-          }, function(details) {
-            function sortByFrameId(a, b) {
-              return a.frameId < b.frameId ? 1 : -1;
-            }
-            var expectedSenders = details.filter(function(frame) {
-              return frame.frameId > 0; // Exclude main frame.
-            }).map(function(frame) {
-              return {
-                tab: testTab,
-                frameId: frame.frameId,
-                url: frame.url,
-                id: chrome.runtime.id
-              };
-            }).sort(sortByFrameId);
-
-            actualSenders.sort(sortByFrameId);
-
-            compareSenders(expectedSenders[0], actualSenders[0]);
-            compareSenders(expectedSenders[1], actualSenders[1]);
-            doneListening();
-          });
-        }
-      );
-
+      constructMessageSenderFromFrameInTab(false);
       var port = chrome.tabs.connect(testTab.id);
       port.postMessage({testSendMessageFromFrame: true});
       port.disconnect();
-      chrome.test.log("sendMessageFromFrameInTab: send 1st message to tab");
+      chrome.test.log('sendMessageFromFrameInTab: send 1st message to tab');
+    },
+
+    // Tests that a message sent from a sandboxed child frame in a tab is
+    // constructed properly.
+    function sendMessageFromSandboxFrameInTab() {
+      constructMessageSenderFromFrameInTab(true);
+      var port = chrome.tabs.connect(testTab.id);
+      port.postMessage({testSendMessageFromSandboxedFrame: true});
+      port.disconnect();
+      chrome.test.log(
+          'sendMessageFromSandboxFrameInTab: send 1st message to tab');
     },
 
     // connect to frameId 0 should trigger onConnect in the main frame only.
@@ -175,12 +159,14 @@
 
     // connect without frameId should trigger onConnect in every frame.
     function sendMessageToAllFramesInTab() {
-      connectToTabWithFrameId(undefined, ['from_main', 'from_0', 'from_1']);
+      connectToTabWithFrameId(
+          undefined, ['from_main', 'from_0', 'from_1', 'from_2', 'from_3']);
     },
 
     // connect with frameId null should trigger onConnect in every frame.
     function sendMessageToAllFramesInTab() {
-      connectToTabWithFrameId(null, ['from_main', 'from_0', 'from_1']);
+      connectToTabWithFrameId(
+          null, ['from_main', 'from_0', 'from_1', 'from_2', 'from_3']);
     },
 
     // connect with a positive frameId should trigger onConnect in that specific
@@ -423,3 +409,52 @@
     release();
   };
 }
+
+// Tests that a message from a child frame has the correct frameId and that the
+// message is constructed with the expected properties.
+function constructMessageSenderFromFrameInTab(isSandbox) {
+  // In page.js testSendMessageFromFrame() adds 2 frames, after which
+  // testSendMessageFromSandboxedFrame() adds 2 sandboxed frames that are given
+  // frameIds in the order in which they were added. Make sure we are checking
+  // the correct frames and excluding the main frame.
+  var minFrameId = isSandbox ? 2 : 0;
+  var actualSenders = [];
+  var doneListening = listenForever(
+      chrome.runtime.onMessage, function(request, sender, sendResponse) {
+        actualSenders.push(sender);
+
+        // testSendMessageFromFrame() in page.js adds 2 frames. Wait for
+        // messages from each.
+        if (actualSenders.length < 2)
+          return;
+
+        chrome.webNavigation.getAllFrames(
+            {tabId: testTab.id}, function(details) {
+              function sortByFrameId(a, b) {
+                return a.frameId < b.frameId ? 1 : -1;
+              }
+              var expectedSenders =
+                  details
+                      .filter(function(frame) {
+                        return frame.frameId > minFrameId;
+                      })
+                      .map(function(frame) {
+                        if (isSandbox) {
+                          return createExpectedSenderWithOrigin(
+                              testTab, frame.frameId, frame.url, 'null',
+                              chrome.runtime.id);
+                        }
+                        return createExpectedSender(
+                            testTab, frame.frameId, frame.url,
+                            chrome.runtime.id);
+                      })
+                      .sort(sortByFrameId);
+
+              actualSenders.sort(sortByFrameId);
+
+              compareSenders(expectedSenders[0], actualSenders[0]);
+              compareSenders(expectedSenders[1], actualSenders[1]);
+              doneListening();
+            });
+      });
+}
diff --git a/chrome/test/data/extensions/platform_apps/messaging/app2/background.js b/chrome/test/data/extensions/platform_apps/messaging/app2/background.js
index 2555b8a9..f0eff6b 100644
--- a/chrome/test/data/extensions/platform_apps/messaging/app2/background.js
+++ b/chrome/test/data/extensions/platform_apps/messaging/app2/background.js
@@ -17,7 +17,8 @@
 chrome.runtime.onMessageExternal.addListener(function(msg, sender, callback) {
   chrome.test.assertEq({
     id: otherId,
-    url: 'chrome-extension://' + otherId + '/_generated_background_page.html'
+    url: 'chrome-extension://' + otherId + '/_generated_background_page.html',
+    origin: 'chrome-extension://' + otherId
   }, sender);
   if (msg == 'hello')
     callback('hello_response');
diff --git a/chrome/test/data/local_ntp/realbox_browsertest.js b/chrome/test/data/local_ntp/realbox_browsertest.js
index 5f40e98..69df404 100644
--- a/chrome/test/data/local_ntp/realbox_browsertest.js
+++ b/chrome/test/data/local_ntp/realbox_browsertest.js
@@ -785,6 +785,53 @@
   assertEquals(1, test.realbox.opens.length);
 };
 
+test.realbox2.testPressEnterTooQuickly = function() {
+  test.realbox.realboxEl.dispatchEvent(new Event('focusin', {bubbles: true}));
+  test.realbox.realboxEl.value = 'hello';
+  test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
+
+  chrome.embeddedSearch.searchBox.autocompleteresultchanged({
+    input: test.realbox.realboxEl.value,
+    matches: [test.realbox.getSearchMatch({supportsDeletion: true})]
+  });
+
+  const matchEls1 = $(test.realbox.IDS.REALBOX_MATCHES).children;
+  assertEquals(1, matchEls1.length);
+
+  // First match is selected.
+  assertTrue(matchEls1[0].classList.contains(test.realbox.CLASSES.SELECTED));
+
+  test.realbox.realboxEl.value = 'hello world';
+  test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
+
+  const shiftEnter = new KeyboardEvent('keydown', {
+    bubbles: true,
+    cancelable: true,
+    key: 'Enter',
+    target: test.realbox.realboxEl,
+    shiftKey: true,
+  });
+  test.realbox.realboxEl.dispatchEvent(shiftEnter);
+
+  // Did not navigate to the first match as it is stale.
+  assertEquals(0, test.realbox.opens.length);
+
+  const matches = [test.realbox.getUrlMatch({supportsDeletion: true})];
+  chrome.embeddedSearch.searchBox.autocompleteresultchanged(
+      {input: test.realbox.realboxEl.value, matches});
+
+  const matchEls2 = $(test.realbox.IDS.REALBOX_MATCHES).children;
+  assertEquals(1, matchEls2.length);
+
+  // First match is selected.
+  assertTrue(matchEls2[0].classList.contains(test.realbox.CLASSES.SELECTED));
+
+  // Navigated to the first new match.
+  assertEquals(1, test.realbox.opens.length);
+  assertEquals(0, test.realbox.opens[0].index);
+  assertEquals(matches[0].destinationUrl, test.realbox.opens[0].url);
+};
+
 test.realbox2.testPressEnterNoSelectedMatch = function() {
   test.realbox.realboxEl.value = 'hello world';
   test.realbox.realboxEl.dispatchEvent(new CustomEvent('input'));
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index c5fa950..2df972c3 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2,73 +2,93 @@
   "-- Template --": {
     "intro": "Tests that policies map to prefs properly and whether the corresponding Chrome settings UI behaves properly, e.g. if a policy is managed, the UI should be readonly and an icon with a properly worded tooltip should show up.",
     "intro": "Top-level entries map a policy name to its test parameters, described below. The name of the top level entry should be of the form <policy name>[.suffix]. The optional suffix is used for defining multiple test cases for a single policy.",
-
-    "os": ["List of operating systems that support this policy. Valid values:", "win", "linux", "mac", "chromeos", "android", "Defaults to empty if not specified."],
+    "os": [
+      "List of operating systems that support this policy. Valid values:",
+      "win",
+      "linux",
+      "mac",
+      "chromeos",
+      "android",
+      "Defaults to empty if not specified."
+    ],
     "official_only": "Whether this policy exists in official builds only. Defaults to |false| if not specified.",
     "can_be_recommended": "Whether a recommended value may be set for the policy. Defaults to |false| if not specified.",
-    "test_policy": "A policy dictionary that should make the preferences affected by this policy become policy-controlled. Usually just sets the current policy. Defaults to an empty dictionary if not specified.",
     "note": "If the policy affects any preferences, the following array should be specified with one entry per such preference.",
-    "pref_mappings": [
-      { "pref": "The affected preference's name.",
-        "local_state": "Whether |pref| is registered in local state's PrefService instead of the profile's PrefService. Defaults to |false| if not specified.",
-        "check_for_mandatory": "Should the preference be tested when a mandatory value is set for the policy? Defaults to |true| if not specified.",
-        "check_for_recommended": "Should the preference be tested when a recommended value is set for the policy? Defaults to |true| if not specified.",
-        "note": "When |can_be_recommended| is |false|, the policy is never set to a recommended value so |check_for_recommended| has no effect.",
-        "note": "The following entries should be specified if controlled setting indicators exist for |pref| in the settings UI.",
-        "indicator_test_url": "The URL to navigate to in order to test the indicators. Defaults to |chrome://extensions-frame/| if not specified.",
-        "indicator_test_setup_js": "Any JavaScript that should be executed before testing the indicators. This should be specified only if an explicit user action must be simulated (e.g. clicking a button).",
-        "indicator_selector": "A CSS selector that locates all controlled setting indicators for |pref|. This is appended to the selector 'span.controlled-setting-indicator' and if not specified, defaults to '[pref=(the value of |pref|)', e.g. '[pref=homepage]'.",
-        "note": "Any number of test cases may be specified in the following array.",
+    "policy_pref_mapping_tests": [
+      {
+        "policies": "A policy dictionary that should affect |prefs| when set as mandatory or recommended policy.",
+        "prefs": {
+            "${pref}": {
+              "value": "The value that |pref| should take on.",
+              "expect_default": "Whether or not the pref value should be the default one.",
+              "local_state": "Whether |pref| is registered in local state's PrefService instead of the profile's PrefService. Defaults to |false| if not specified.",
+              "check_for_mandatory": "Should the preference be tested when a mandatory value is set for the policy? Defaults to |true| if not specified.",
+              "check_for_recommended": "Should the preference be tested when a recommended value is set for the policy? Defaults to |true| if not specified."
+            }
+          },
         "indicator_tests": [
-          { "policy": "A policy dictionary that should affect |pref| when set as mandatory or recommended policy.",
+          {
+            "pref": "The affected preference's name.",
+            "test_url": "The URL to navigate to in order to test the indicators. Defaults to |chrome://extensions-frame/| if not specified.",
+            "test_setup_js": "Any JavaScript that should be executed before testing the indicators. This should be specified only if an explicit user action must be simulated (e.g. clicking a button).",
+            "selector": "A CSS selector that locates all controlled setting indicators for |pref|. This is appended to the selector 'span.controlled-setting-indicator' and if not specified, defaults to '[pref=(the value of |pref|)', e.g. '[pref=homepage]'.",
             "value": "The value that |pref| should take on. This must only be specified if |pref| has multiple controlled setting indicators, each corresponding to a specific value (e.g. indicators next to radio buttons).",
             "readonly": "Whether setting the policy dictionary as recommended should cause |pref| to become read-only in the settings UI. This will be the case when the dictionary sets another policy that makes |pref| not applicable (e.g. setting 'homepage is NTP' makes the 'homepage URL' pref not applicable and read-only)."
           }
         ]
       }
-    ],
-    "note": "The following entry should be specified if there is a controlled setting indicator that reacts to the policy directly, without a preference serving as an intermediary.",
-    "indicator_selector": "A CSS selector that locates the controlled setting indicator directly affected by the policy. This is appended to the selector 'span.controlled-setting-indicator'."
+    ]
   },
 
   "TabFreezingEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "official_only": true,
-    "test_policy": { "TabFreezingEnabled": false },
-    "pref_mappings": [
-      { "pref": "tab_freezing_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "TabFreezingEnabled": false },
+        "prefs": { "tab_freezing_enabled": { "local_state": true } }
       }
     ]
   },
 
   "OverrideSecurityRestrictionsOnInsecureOrigin": {
     "os": ["win", "linux", "mac", "android", "chromeos"],
-    "test_policy": { "OverrideSecurityRestrictionsOnInsecureOrigin": ["http://example.com/","*.example.com"] },
-    "pref_mappings": [ { "pref": "unsafely_treat_insecure_origin_as_secure",
-                         "local_state": true } ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "OverrideSecurityRestrictionsOnInsecureOrigin": [
+            "http://example.com/",
+            "*.example.com"
+          ]
+        },
+        "prefs": { "unsafely_treat_insecure_origin_as_secure": { "local_state": true } }
+      }
+    ]
   },
 
   "UnsafelyTreatInsecureOriginAsSecure": {
     "note": "This policy is deprecated.",
     "os": ["win", "linux", "mac"],
-    "test_policy": { "UnsafelyTreatInsecureOriginAsSecure": ["http://example.com/","*.example.com"] },
-    "pref_mappings": [ { "pref": "unsafely_treat_insecure_origin_as_secure",
-                         "local_state": true } ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "UnsafelyTreatInsecureOriginAsSecure": [
+            "http://example.com/",
+            "*.example.com"
+          ]
+        },
+        "prefs": { "unsafely_treat_insecure_origin_as_secure": { "local_state": true } }
+      }
+    ]
   },
 
   "HomepageLocation": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "HomepageLocation": "http://chromium.org" },
-    "pref_mappings": [
-      { "pref": "homepage",
-        "indicator_tests": [
-          { "policy": { "HomepageIsNewTabPage": false, "HomepageLocation": "http://chromium.org" } },
-          { "policy": { "HomepageIsNewTabPage": true, "HomepageLocation": "http://chromium.org" },
-            "readonly": true
-          }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "HomepageLocation": "http://chromium.org" },
+        "prefs": { "homepage": {} }
       }
     ]
   },
@@ -76,15 +96,9 @@
   "HomepageIsNewTabPage": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "HomepageIsNewTabPage": true },
-    "pref_mappings": [
-      { "pref": "homepage_is_newtabpage",
-        "indicator_tests": [
-          { "policy": { "HomepageIsNewTabPage": false },
-            "value": "false"},
-          { "policy": { "HomepageIsNewTabPage": true },
-            "value": "true"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "HomepageIsNewTabPage": true }
       }
     ]
   },
@@ -92,19 +106,20 @@
   "NewTabPageLocation": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "NewTabPageLocation": "http://chromium.org" },
-    "pref_mappings": [
-      { "pref": "newtab_page_location_override"
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NewTabPageLocation": "http://.org" },
+        "prefs": { "newtab_page_location_override": {} }
       }
     ]
   },
 
   "DefaultBrowserSettingEnabled": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "DefaultBrowserSettingEnabled": true },
-    "pref_mappings": [
-      { "pref": "browser.default_browser_setting_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultBrowserSettingEnabled": true },
+        "prefs": { "browser.default_browser_setting_enabled": { "local_state": true } }
       }
     ]
   },
@@ -112,10 +127,10 @@
   "ApplicationLocaleValue": {
     "os": ["win"],
     "can_be_recommended": true,
-    "test_policy": { "ApplicationLocaleValue": "fr" },
-    "pref_mappings": [
-      { "pref": "intl.app_locale",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ApplicationLocaleValue": "fr" },
+        "prefs": { "intl.app_locale": { "local_state": true } }
       }
     ]
   },
@@ -123,12 +138,10 @@
   "AlternateErrorPagesEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "AlternateErrorPagesEnabled": false },
-    "pref_mappings": [
-      { "pref": "alternate_error_pages.enabled",
-        "indicator_tests": [
-          { "policy": { "AlternateErrorPagesEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AlternateErrorPagesEnabled": false },
+        "prefs": { "alternate_error_pages.enabled": {} }
       }
     ]
   },
@@ -136,12 +149,10 @@
   "SearchSuggestEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "SearchSuggestEnabled": false },
-    "pref_mappings": [
-      { "pref": "search.suggest_enabled",
-        "indicator_tests": [
-          { "policy": { "SearchSuggestEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SearchSuggestEnabled": false },
+        "prefs": { "search.suggest_enabled": {} }
       }
     ]
   },
@@ -152,21 +163,23 @@
 
   "DnsOverHttpsMode": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "DnsOverHttpsMode": "off" },
-    "pref_mappings": [
-      { "pref": "dns_over_https.mode",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DnsOverHttpsMode": "off" },
+        "prefs": { "dns_over_https.mode": { "local_state": true } }
       }
     ]
   },
 
   "DnsOverHttpsTemplates": {
-    "os": [ "win", "linux", "mac", "chromeos", "android" ],
-    "test_policy": { "DnsOverHttpsMode":  "automatic", "DnsOverHttpsTemplates": "https://cloudflare-dns.com/dns-query https://dns.quad9.net/dns-query{?dns}" },
-    "pref_mappings": [
+    "os": ["win", "linux", "mac", "chromeos", "android"],
+    "policy_pref_mapping_test": [
       {
-        "pref": "dns_over_https.templates",
-        "local_state": true
+        "policies": {
+          "DnsOverHttpsMode": "automatic",
+          "DnsOverHttpsTemplates": "https://cloudflare-dns.com/dns-query https://dns.quad9.net/dns-query{?dns}"
+        },
+        "prefs": { "dns_over_https.templates": { "local_state": true } }
       }
     ]
   },
@@ -174,12 +187,10 @@
   "NetworkPredictionOptions": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "NetworkPredictionOptions": 2 },
-    "pref_mappings": [
-      { "pref": "net.network_prediction_options",
-        "indicator_tests": [
-          { "policy": { "NetworkPredictionOptions": 2 } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NetworkPredictionOptions": 2 },
+        "prefs": { "net.network_prediction_options": {} }
       }
     ]
   },
@@ -190,29 +201,33 @@
 
   "QuicAllowed": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "QuicAllowed": true }
+    "policy_pref_mapping_test": [{ "policies": { "QuicAllowed": true } }]
   },
 
   "DisabledSchemes": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DisabledSchemes": ["file"] },
-    "pref_mappings": [
-      { "pref": "policy.url_blacklist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DisabledSchemes": ["file"] },
+        "prefs": { "policy.url_blacklist": {} }
+      }
     ]
   },
 
-  "Http09OnNonDefaultPortsEnabled": {
-  },
+  "Http09OnNonDefaultPortsEnabled": {},
 
   "JavascriptEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "JavascriptEnabled": false },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.javascript",
-        "indicator_selector": "[content-setting=javascript]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "JavascriptEnabled": false },
+        "prefs": { "profile.managed_default_content_settings.javascript": {}},
         "indicator_tests": [
-          { "policy": { "JavascriptEnabled": false },
-            "value": "block"}
+          {
+            "pref": "profile.managed_default_content_settings.javascript",
+            "selector": "[content-setting=javascript]",
+            "value": "block"
+          }
         ]
       }
     ]
@@ -220,367 +235,419 @@
 
   "IncognitoEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "IncognitoEnabled": false },
-    "pref_mappings": [
-      { "pref": "incognito.mode_availability" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "IncognitoEnabled": false },
+        "prefs": { "incognito.mode_availability": {} }
+      }
     ]
   },
 
   "IncognitoModeAvailability": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "IncognitoModeAvailability": 1 },
-    "pref_mappings": [
-      { "pref": "incognito.mode_availability" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "IncognitoModeAvailability": 1 },
+        "prefs": { "incognito.mode_availability": {} }
+      }
     ]
   },
 
   "SavingBrowserHistoryDisabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "SavingBrowserHistoryDisabled": true },
-    "pref_mappings": [
-      { "pref": "history.saving_disabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SavingBrowserHistoryDisabled": true },
+        "prefs": { "history.saving_disabled": {} }
+      }
     ]
   },
 
   "AllowDeletingBrowserHistory": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "AllowDeletingBrowserHistory": false },
-    "pref_mappings": [
-      { "pref": "history.deleting_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AllowDeletingBrowserHistory": false },
+        "prefs": { "history.deleting_enabled": {} }
+      }
     ]
   },
 
-  "RemoteAccessClientFirewallTraversal": {
-  },
+  "RemoteAccessClientFirewallTraversal": {},
 
-  "RemoteAccessHostClientDomain": {
-  },
+  "RemoteAccessHostClientDomain": {},
 
-  "RemoteAccessHostClientDomainList": {
-  },
+  "RemoteAccessHostClientDomainList": {},
 
-  "RemoteAccessHostFirewallTraversal": {
-  },
+  "RemoteAccessHostFirewallTraversal": {},
 
-  "RemoteAccessHostRequireTwoFactor": {
-  },
+  "RemoteAccessHostRequireTwoFactor": {},
 
-  "RemoteAccessHostDomain": {
-  },
+  "RemoteAccessHostDomain": {},
 
-  "RemoteAccessHostDomainList": {
-  },
+  "RemoteAccessHostDomainList": {},
 
-  "RemoteAccessHostTalkGadgetPrefix": {
-  },
+  "RemoteAccessHostTalkGadgetPrefix": {},
 
-  "RemoteAccessHostRequireCurtain": {
-  },
+  "RemoteAccessHostRequireCurtain": {},
 
-  "RemoteAccessHostAllowClientPairing": {
-  },
+  "RemoteAccessHostAllowClientPairing": {},
 
-  "RemoteAccessHostAllowGnubbyAuth": {
-  },
+  "RemoteAccessHostAllowGnubbyAuth": {},
 
-  "RemoteAccessHostAllowRelayedConnection": {
-  },
+  "RemoteAccessHostAllowRelayedConnection": {},
 
-  "RemoteAccessHostUdpPortRange": {
-  },
+  "RemoteAccessHostUdpPortRange": {},
 
-  "RemoteAccessHostMatchUsername": {
-  },
+  "RemoteAccessHostMatchUsername": {},
 
-  "RemoteAccessHostTokenUrl": {
-  },
+  "RemoteAccessHostTokenUrl": {},
 
-  "RemoteAccessHostTokenValidationUrl": {
-  },
+  "RemoteAccessHostTokenValidationUrl": {},
 
-  "RemoteAccessHostTokenValidationCertificateIssuer": {
-  },
+  "RemoteAccessHostTokenValidationCertificateIssuer": {},
 
-  "RemoteAccessHostDebugOverridePolicies": {
-  },
+  "RemoteAccessHostDebugOverridePolicies": {},
 
-  "RemoteAccessHostAllowUiAccessForRemoteAssistance": {
-  },
+  "RemoteAccessHostAllowUiAccessForRemoteAssistance": {},
 
-  "RemoteAccessHostAllowFileTransfer": {
-  },
+  "RemoteAccessHostAllowFileTransfer": {},
 
   "PrintingEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "PrintingEnabled": false },
-    "pref_mappings": [
-      { "pref": "printing.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingEnabled": false },
+        "prefs": { "printing.enabled": {} }
+      }
     ]
   },
 
   "PrintHeaderFooter": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "PrintHeaderFooter": true },
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintHeaderFooter": true },
+        "prefs": { "printing.print_header_footer": {} }
+      }
+    ]
+  },
+
+  "PrinterTypeDenyList": {
+    "os": ["win", "linux", "mac", "chromeos"],
+    "test_policy": { "PrinterTypeDenyList": ["privet", "extension"] },
     "pref_mappings": [
-      { "pref": "printing.print_header_footer" }
+      { "pref": "printing.printer_type_deny_list" }
     ]
   },
 
   "CloudPrintProxyEnabled": {
     "os": [],
-    "test_policy": { "CloudPrintProxyEnabled": true },
-    "pref_mappings": [
-      { "pref": "cloud_print.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CloudPrintProxyEnabled": true },
+        "prefs": { "cloud_print.enabled": {} }
+      }
     ]
   },
 
   "PrintingAllowedColorModes": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingAllowedColorModes": "monochrome" },
-    "pref_mappings": [
-      { "pref": "printing.allowed_color_modes" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingAllowedColorModes": "monochrome" },
+        "prefs": { "printing.allowed_color_modes": {} }
+      }
     ]
   },
 
   "PrintingAllowedDuplexModes": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingAllowedDuplexModes": "duplex" },
-    "pref_mappings": [
-      { "pref": "printing.allowed_duplex_modes" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingAllowedDuplexModes": "duplex" },
+        "prefs": { "printing.allowed_duplex_modes": {} }
+      }
     ]
   },
 
   "PrintingAllowedPinModes": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingAllowedPinModes": "pin" },
-    "pref_mappings": [
-      { "pref": "printing.allowed_pin_modes" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingAllowedPinModes": "pin" },
+        "prefs": { "printing.allowed_pin_modes": {} }
+      }
     ]
   },
 
   "PrintingAllowedBackgroundGraphicsModes": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingAllowedBackgroundGraphicsModes": "enabled" },
-    "pref_mappings": [
-      { "pref": "printing.allowed_background_graphics_modes" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingAllowedBackgroundGraphicsModes": "enabled" },
+        "prefs": { "printing.allowed_background_graphics_modes": {} }
+      }
     ]
   },
 
   "PrintingAllowedPageSizes": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingAllowedPageSizes": [{"WidthUm": 210000, "HeightUm": 297000}] },
-    "pref_mappings": [
-      { "pref": "printing.allowed_page_sizes" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingAllowedPageSizes": [{ "WidthUm": 210000, "HeightUm": 297000 }] },
+        "prefs": { "printing.allowed_page_sizes": {} }
+      }
     ]
   },
 
   "PrintingColorDefault": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingColorDefault": "monochrome" },
-    "pref_mappings": [
-      { "pref": "printing.color_default" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingColorDefault": "monochrome" },
+        "prefs": { "printing.color_default": {} }
+      }
     ]
   },
 
   "PrintingDuplexDefault": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingDuplexDefault": "long-edge" },
-    "pref_mappings": [
-      { "pref": "printing.duplex_default" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingDuplexDefault": "long-edge" },
+        "prefs": { "printing.duplex_default": {} }
+      }
     ]
   },
 
   "PrintingPinDefault": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingPinDefault": "pin" },
-    "pref_mappings": [
-      { "pref": "printing.pin_default" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingPinDefault": "pin" },
+        "prefs": { "printing.pin_default": {} }
+      }
     ]
   },
 
   "PrintingBackgroundGraphicsDefault": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingBackgroundGraphicsDefault": "enabled" },
-    "pref_mappings": [
-      { "pref": "printing.background_graphics_default" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingBackgroundGraphicsDefault": "enabled" },
+        "prefs": { "printing.background_graphics_default": {} }
+      }
     ]
   },
 
   "PrintingSizeDefault": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingSizeDefault": {"WidthUm": 210000, "HeightUm": 297000} },
-    "pref_mappings": [
-      { "pref": "printing.size_default" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingSizeDefault": { "WidthUm": 210000, "HeightUm": 297000 } },
+        "prefs": { "printing.size_default": {} }
+      }
     ]
   },
 
   "PrintingSendUsernameAndFilenameEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "PrintingSendUsernameAndFilenameEnabled": true },
-    "pref_mappings": [
-      { "pref": "printing.send_username_and_filename_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PrintingSendUsernameAndFilenameEnabled": true },
+        "prefs": { "printing.send_username_and_filename_enabled": {} }
+      }
     ]
   },
 
   "PrintJobHistoryExpirationPeriod": {
     "os": ["chromeos"],
-    "test_policy": { "PrintJobHistoryExpirationPeriod": 90 },
-    "pref_mappings": [
-      { "pref": "printing.print_job_history_expiration_period" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"PrintJobHistoryExpirationPeriod": 90},
+        "prefs": { "printing.print_job_history_expiration_period": {} }
+      }
     ]
   },
 
   "CloudPrintSubmitEnabled": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "CloudPrintSubmitEnabled": false },
-    "pref_mappings": [
-      { "pref": "cloud_print.submit_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"CloudPrintSubmitEnabled": false},
+        "prefs": { "cloud_print.submit_enabled": {} }
+      }
     ]
   },
 
   "ExternalPrintServers": {
     "os": ["chromeos"],
-    "test_policy": {
-      "ExternalPrintServers": {
-        "id": "id123",
-        "url": "https://example.com/policyfile",
-        "hash": "deadbeefdeadbeefdeadbeef"
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ExternalPrintServers": {
+            "id": "id123",
+            "url": "https://example.com/policyfile",
+            "hash": "deadbeefdeadbeefdeadbeef"
+          }
+        }
       }
-    }
+    ]
   },
 
   "ExternalPrintServersWhitelist": {
     "os": ["chromeos"],
-    "test_policy": {
-      "ExternalPrintServersWhitelist":  ["id4", "id7", "id10"]
-    },
-    "pref_mappings": [
-      { "pref": "native_printing.external_print_servers_whitelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ExternalPrintServersWhitelist": ["id4", "id7", "id10"]},
+        "prefs": { "native_printing.external_print_servers_whitelist": {} }
+      }
     ]
   },
 
   "NativePrinters": {
     "os": ["chromeos"],
-    "test_policy": {
-      "NativePrinters": [
-        {
-          "display_name": "Break Room",
-          "description": "The blue one",
-          "manufacturer": "PrtrMfgr",
-          "model": "MegaLazer",
-          "uri": "ipps://192.168.2.14",
-          "uuid": "aaaa-aaaa-eeee-eeee-1234",
-          "ppd_resource": {
-            "effective_manufacturer": "Printers, Ink",
-            "effective_model": "LaserMaster 2100"
-          }
-        }
-      ]
-    },
-    "pref_mappings": [
-      { "pref": "native_printing.recommended_printers" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "NativePrinters": [
+            {
+              "display_name": "Break Room",
+              "description": "The blue one",
+              "manufacturer": "PrtrMfgr",
+              "model": "MegaLazer",
+              "uri": "ipps://192.168.2.14",
+              "uuid": "aaaa-aaaa-eeee-eeee-1234",
+              "ppd_resource": {
+                "effective_manufacturer": "Printers, Ink",
+                "effective_model": "LaserMaster 2100"
+              }
+            }
+          ]
+        },
+        "prefs": { "native_printing.recommended_printers": {} }
+      }
     ]
   },
 
   "NativePrintersBulkConfiguration": {
     "os": ["chromeos"],
-    "test_policy": {
-      "NativePrintersBulkConfiguration": {
-        "url": "https://example.com/policyfile",
-        "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "NativePrintersBulkConfiguration": {
+            "url": "https://example.com/policyfile",
+            "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+          }
+        }
       }
-    }
+    ]
   },
 
   "NativePrintersBulkAccessMode": {
     "os": ["chromeos"],
-    "test_policy": {
-      "NativePrintersBulkAccessMode": 1
-    },
-    "pref_mappings": [
-      { "pref": "native_printing.recommended_printers_access_mode" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NativePrintersBulkAccessMode": 1 },
+        "prefs": { "native_printing.recommended_printers_access_mode": {} }
+      }
     ]
   },
 
   "NativePrintersBulkBlacklist": {
     "os": ["chromeos"],
-    "test_policy": {
-      "NativePrintersBulkBlacklist": ["id4", "id7", "id10"]
-    },
-    "pref_mappings": [
-      { "pref": "native_printing.recommended_printers_blacklist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NativePrintersBulkBlacklist": ["id4", "id7", "id10"] },
+        "prefs": { "native_printing.recommended_printers_blacklist": {} }
+      }
     ]
   },
+
   "NativePrintersBulkWhitelist": {
     "os": ["chromeos"],
-    "test_policy": {
-      "NativePrintersBulkWhitelist":  ["id4", "id7", "id10"]
-    },
-    "pref_mappings": [
-      { "pref": "native_printing.recommended_printers_whitelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NativePrintersBulkWhitelist": ["id4", "id7", "id10"] },
+        "prefs": { "native_printing.recommended_printers_whitelist": {} }
+      }
     ]
   },
 
   "UserNativePrintersAllowed": {
     "os": ["chromeos"],
-    "test_policy": {
-      "UserNativePrintersAllowed": true
-    },
-    "pref_mappings": [
-      { "pref": "native_printing.user_native_printers_allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "UserNativePrintersAllowed": true },
+        "prefs": { "native_printing.user_native_printers_allowed": {} }
+      }
     ]
   },
 
   "SafeBrowsingEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "SafeBrowsingEnabled": false },
-    "pref_mappings": [
-      { "pref": "safebrowsing.enabled",
-        "indicator_tests": [
-          { "policy": { "SafeBrowsingEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SafeBrowsingEnabled": false },
+        "prefs": { "safebrowsing.enabled": {} }
       }
     ]
   },
 
   "ForceSafeSearch": {
-    "pref": "settings.force_safesearch",
-    "test_policy": { "ForceSafeSearch": true },
-    "settings_pages": [],
-    "os": ["win", "linux", "mac", "chromeos", "android"]
+    "os": ["win", "linux", "mac", "chromeos", "android"],
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ForceSafeSearch": true }
+      }
+    ]
   },
 
   "ForceGoogleSafeSearch": {
-    "pref": "settings.force_google_safesearch",
-    "test_policy": { "ForceGoogleSafeSearch": true },
-    "settings_pages": [],
-    "os": ["win", "linux", "mac", "chromeos", "android"]
+    "os": ["win", "linux", "mac", "chromeos", "android"],
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ForceGoogleSafeSearch": true },
+        "prefs": { "settings.force_google_safesearch": {} }
+      }
+    ]
   },
 
   "ForceYouTubeSafetyMode": {
-    "pref": "settings.force_youtube_safety_mode",
-    "test_policy": { "ForceYouTubeSafetyMode": true },
-    "settings_pages": [],
-    "os": ["win", "linux", "mac", "chromeos", "android"]
+    "os": ["win", "linux", "mac", "chromeos", "android"],
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ForceYouTubeSafetyMode": true }
+      }
+    ]
   },
 
   "ForceYouTubeRestrict": {
-    "pref": "settings.force_youtube_restrict",
-    "test_policy": { "ForceYouTubeRestrict": 1 },
-    "settings_pages": [],
-    "os": ["win", "linux", "mac", "chromeos", "android"]
+    "os": ["win", "linux", "mac", "chromeos", "android"],
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ForceYouTubeRestrict": 1 },
+        "prefs": { "settings.force_youtube_restrict": {} }
+      }
+    ]
   },
 
   "MetricsReportingEnabled": {
     "os": ["win", "mac", "linux"],
     "official_only": true,
     "can_be_recommended": true,
-    "test_policy": { "MetricsReportingEnabled": false },
-    "indicator_selector": "#metrics-reporting-disabled-icon",
-    "pref_mappings": [
-      { "pref": "user_experience_metrics.reporting_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "MetricsReportingEnabled": false },
+        "prefs": { "user_experience_metrics.reporting_enabled": { "local_state": true } },
+        "indicator_tests": [
+          {
+            "pref": "user_experience_metrics.reporting_enabled",
+            "selector": "#metrics-reporting-disabled-icon"
+          }
+        ]
       }
     ]
   },
@@ -588,12 +655,10 @@
   "PasswordManagerEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "PasswordManagerEnabled": false },
-    "pref_mappings": [
-      { "pref": "credentials_enable_service",
-        "indicator_tests": [
-          { "policy": { "PasswordManagerEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PasswordManagerEnabled": false },
+        "prefs": { "credentials_enable_service": {} }
       }
     ]
   },
@@ -602,33 +667,30 @@
     "note": "This policy is retired, see http://crbug.com/598698."
   },
 
-  "PasswordLeakDetectionEnabled" : {
+  "PasswordLeakDetectionEnabled": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
     "can_be_recommended": true,
-    "test_policy": { "PasswordLeakDetectionEnabled": false },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "profile.password_manager_leak_detection",
-        "indicator_tests": [
-          { "policy": { "PasswordLeakDetectionEnabled" : false} }
-        ]
+        "policies": { "PasswordLeakDetectionEnabled": false },
+        "prefs": { "profile.password_manager_leak_detection": {} }
       }
     ]
   },
 
   "ContextualSearchEnabled": {
-    "os": ["android"]
+    "os": [
+      "android"
+    ]
   },
 
   "AutoFillEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "AutoFillEnabled": false },
-    "pref_mappings": [
-      { "pref": "autofill.enabled",
-        "indicator_tests": [
-          { "policy": { "AutoFillEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AutoFillEnabled": false },
+        "prefs": { "autofill.enabled": {} }
       }
     ]
   },
@@ -636,12 +698,10 @@
   "AutofillAddressEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "AutofillAddressEnabled": false },
-    "pref_mappings": [
-      { "pref": "autofill.profile_enabled",
-        "indicator_tests": [
-          { "policy": { "AutofillAddressEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AutofillAddressEnabled": false },
+        "prefs": { "autofill.profile_enabled": {} }
       }
     ]
   },
@@ -649,12 +709,10 @@
   "AutofillCreditCardEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "AutofillCreditCardEnabled": false },
-    "pref_mappings": [
-      { "pref": "autofill.credit_card_enabled",
-        "indicator_tests": [
-          { "policy": { "AutofillCreditCardEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AutofillCreditCardEnabled": false },
+        "prefs": { "autofill.credit_card_enabled": {} }
       }
     ]
   },
@@ -662,14 +720,11 @@
   "DisabledPlugins.0": {
     "note": "This policy is deprecated. This test tests its migration path.",
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DisabledPlugins": ["*Flash*"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.plugins",
-        "indicator_selector": "[content-setting=plugins]",
-        "indicator_tests": [
-          { "policy": { "DisabledPlugins": ["*Flash*"] },
-            "value": "block"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DisabledPlugins": [ "*Flash*" ] },
+        "prefs": { "profile.managed_default_content_settings.plugins": { "value": 2 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.plugins", "selector": "[content-setting=plugins]", "value": "block" }]
       }
     ]
   },
@@ -677,12 +732,10 @@
   "DisabledPlugins.1": {
     "note": "This policy is deprecated. This test tests its migration path.",
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DisabledPlugins": ["*PDF*"] },
-    "pref_mappings": [
-      { "pref": "plugins.always_open_pdf_externally",
-        "indicator_tests": [
-          { "policy": { "DisabledPlugins": ["*PDF*"] }}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DisabledPlugins": [ "*PDF*" ] },
+        "prefs": { "plugins.always_open_pdf_externally": {} }
       }
     ]
   },
@@ -690,14 +743,11 @@
   "EnabledPlugins.0": {
     "note": "This policy is deprecated. This test tests its migration path.",
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "EnabledPlugins": ["*Flash*"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.plugins",
-        "indicator_selector": "[content-setting=plugins]",
-        "indicator_tests": [
-          { "policy": { "EnabledPlugins": ["*Flash*"] },
-            "value": "allow"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "EnabledPlugins": [ "*Flash*" ] },
+        "prefs": { "profile.managed_default_content_settings.plugins": {"value": 1 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.plugins", "selector": "[content-setting=plugins]", "value": "allow" }]
       }
     ]
   },
@@ -705,12 +755,10 @@
   "EnabledPlugins.1": {
     "note": "This policy is deprecated. This test tests its migration path.",
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "EnabledPlugins": ["*PDF*"] },
-    "pref_mappings": [
-      { "pref": "plugins.always_open_pdf_externally",
-        "indicator_tests": [
-          { "policy": { "EnabledPlugins": ["*PDF*"] }}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "EnabledPlugins": ["*PDF*"] },
+        "prefs": { "plugins.always_open_pdf_externally": {} }
       }
     ]
   },
@@ -722,13 +770,10 @@
 
   "AlwaysOpenPdfExternally": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "AlwaysOpenPdfExternally": true },
-    "pref_mappings": [
-      { "pref": "plugins.always_open_pdf_externally",
-        "indicator_tests": [
-          { "policy": { "AlwaysOpenPdfExternally": false }},
-          { "policy": { "AlwaysOpenPdfExternally": true }}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AlwaysOpenPdfExternally": true },
+        "prefs": { "plugins.always_open_pdf_externally": {} }
       }
     ]
   },
@@ -739,17 +784,21 @@
 
   "SyncDisabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "SyncDisabled": true },
-    "pref_mappings": [
-      { "pref": "sync.managed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SyncDisabled": true },
+        "prefs": { "sync.managed": {} }
+      }
     ]
   },
 
   "SigninAllowed": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "SigninAllowed": true },
-    "pref_mappings": [
-      { "pref": "signin.allowed_on_next_startup" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SigninAllowed": true },
+        "prefs": { "signin.allowed_on_next_startup": {} }
+      }
     ]
   },
 
@@ -763,20 +812,20 @@
 
   "DiskCacheDir": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "DiskCacheDir": "${user_home}/test-cache" },
-    "pref_mappings": [
-      { "pref": "browser.disk_cache_dir",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DiskCacheDir": "${user_home}/test-cache" },
+        "prefs": { "browser.disk_cache_dir": { "local_state": true } }
       }
     ]
   },
 
   "DiskCacheSize": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "DiskCacheSize": 100 },
-    "pref_mappings": [
-      { "pref": "browser.disk_cache_size",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DiskCacheSize": 100 },
+        "prefs": { "browser.disk_cache_size": { "local_state": true } }
       }
     ]
   },
@@ -788,48 +837,45 @@
   "DownloadRestrictions": {
     "os": ["win", "mac", "linux", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "DownloadRestrictions": 3 },
-    "pref_mappings": [
-      { "pref": "download_restrictions" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DownloadRestrictions": 3 },
+        "prefs": { "download_restrictions": {} }
+      }
     ]
   },
 
   "SafeBrowsingForTrustedSourcesEnabled": {
     "os": ["win"],
     "can_be_recommended": true,
-    "test_policy": { "SafeBrowsingForTrustedSourcesEnabled": false },
-    "pref_mappings": [
-      { "pref": "safebrowsing_for_trusted_sources_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SafeBrowsingForTrustedSourcesEnabled": false },
+        "prefs": { "safebrowsing_for_trusted_sources_enabled": {} }
+      }
     ]
   },
 
   "DownloadDirectory.0": {
     "os": ["win", "mac", "linux", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "DownloadDirectory": "${user_home}/test-downloads" },
-    "pref_mappings": [
-      { "pref": "download.default_directory",
-        "indicator_tests": [
-          { "policy": { "DownloadDirectory": "${user_home}/test-downloads" } }
-        ]
-      },
-      { "pref": "download.prompt_for_download",
-        "check_for_recommended" : false,
-        "indicator_tests": [
-          { "policy": { "DownloadDirectory": "${user_home}/test-downloads" } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DownloadDirectory": "${user_home}/test-downloads" },
+        "prefs": {
+          "download.default_directory": {},
+          "download.prompt_for_download": { "check_for_recommended": false }
+        }
       }
     ]
   },
 
   "DownloadDirectory.1": {
     "os": ["chromeos"],
-    "test_policy": { "DownloadDirectory": "${google_drive}/downloads" },
-    "pref_mappings": [
-      { "pref": "gdata.disabled",
-        "indicator_tests": [
-          { "policy": { "DownloadDirectory": "${google_drive}/downloads" } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DownloadDirectory": "${google_drive}/downloads" },
+        "prefs": { "gdata.disabled": {} }
       }
     ]
   },
@@ -840,82 +886,71 @@
 
   "CaptivePortalAuthenticationIgnoresProxy": {
     "os": ["chromeos"],
-    "test_policy": { "CaptivePortalAuthenticationIgnoresProxy": true },
-    "pref_mappings": [
-      { "pref": "proxy.captive_portal_ignores_proxy" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CaptivePortalAuthenticationIgnoresProxy": true },
+        "prefs": { "proxy.captive_portal_ignores_proxy": {} }
+      }
     ]
   },
 
   "ProxyMode": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "ProxyMode": "direct" },
-    "pref_mappings": [
-      { "pref": "proxy",
-        "indicator_tests": [
-          { "policy": { "ProxyMode": "direct" } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ProxyMode": "direct" },
+        "prefs": { "proxy": {} }
       }
     ]
   },
 
   "ProxyServerMode": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "ProxyServerMode": 0 },
-    "pref_mappings": [
-      { "pref": "proxy",
-        "indicator_tests": [
-          { "policy": { "ProxyServerMode": 0 } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ProxyServerMode": 0 },
+        "prefs": { "proxy": {} }
       }
     ]
   },
 
   "ProxyServer": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "ProxyMode": "fixed_servers", "ProxyServer": "http://localhost:8080" },
-    "pref_mappings": [
-      { "pref": "proxy",
-        "indicator_tests": [
-          { "policy": { "ProxyMode": "fixed_servers", "ProxyServer": "http://localhost:8080" } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ProxyMode": "fixed_servers", "ProxyServer": "http://localhost:8080" },
+        "prefs": { "proxy": {} }
       }
     ]
   },
 
   "ProxyPacUrl": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "ProxyMode": "pac_script", "ProxyPacUrl": "http://localhost:8080/proxy.pac" },
-    "pref_mappings": [
-      { "pref": "proxy",
-        "indicator_tests": [
-          { "policy": { "ProxyMode": "pac_script", "ProxyPacUrl": "http://localhost:8080/proxy.pac" } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ProxyMode": "pac_script", "ProxyPacUrl": "http://localhost:8080/proxy.pac" },
+        "prefs": { "proxy": {} }
       }
     ]
   },
 
   "ProxyBypassList": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "ProxyMode": "fixed_servers", "ProxyServer": "http://localhost:8080", "ProxyBypassList": "localhost" },
-    "pref_mappings": [
-      { "pref": "proxy",
-        "indicator_tests": [
-          { "policy": { "ProxyMode": "fixed_servers", "ProxyServer": "http://localhost:8080", "ProxyBypassList": "localhost" } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ProxyMode": "fixed_servers", "ProxyServer": "http://localhost:8080", "ProxyBypassList": "localhost" },
+        "prefs": { "proxy": {} }
       }
     ]
   },
 
   "ProxySettings": {
     "os": ["linux", "win"],
-    "test_policy": { "ProxySettings": { "ProxyMode": "direct" } },
-    "pref_mappings": [
-      { "pref": "proxy",
-        "indicator_tests": [
-          { "policy": { "ProxySettings": { "ProxyMode": "direct" } } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ProxySettings": { "ProxyMode": "direct" } },
+        "prefs": { "proxy": {} }
       }
-
     ]
   },
 
@@ -929,20 +964,20 @@
 
   "EnableOnlineRevocationChecks": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "EnableOnlineRevocationChecks": true },
-    "pref_mappings": [
-      { "pref": "ssl.rev_checking.enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "EnableOnlineRevocationChecks": true },
+        "prefs": { "ssl.rev_checking.enabled": { "local_state": true } }
       }
     ]
   },
 
   "RequireOnlineRevocationChecksForLocalAnchors": {
     "os": ["win", "linux", "chromeos"],
-    "test_policy": { "RequireOnlineRevocationChecksForLocalAnchors": true },
-    "pref_mappings": [
-      { "pref": "ssl.rev_checking.required_for_local_anchors",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "RequireOnlineRevocationChecksForLocalAnchors": true },
+        "prefs": { "ssl.rev_checking.required_for_local_anchors": { "local_state": true } }
       }
     ]
   },
@@ -960,201 +995,224 @@
   },
 
   "BuiltinCertificateVerifierEnabled": {
-    "os": ["chromeos", "linux"],
-    "test_policy": { "BuiltinCertificateVerifierEnabled": true },
-    "pref_mappings": [
-      { "pref": "builtin_certificate_verifier_enabled",
-        "local_state": true
+    "os": ["chromeos", "linux" ],
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "BuiltinCertificateVerifierEnabled": true },
+        "prefs": { "builtin_certificate_verifier_enabled": { "local_state": true } }
       }
     ]
   },
 
   "AuthSchemes": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "AuthSchemes": "AuthSchemes" },
-    "pref_mappings": [
-      { "pref": "auth.schemes",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AuthSchemes": "AuthSchemes" },
+        "prefs": { "auth.schemes": { "local_state": true } }
       }
     ]
   },
 
   "DisableAuthNegotiateCnameLookup": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "DisableAuthNegotiateCnameLookup": true },
-    "pref_mappings": [
-      { "pref": "auth.disable_negotiate_cname_lookup",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DisableAuthNegotiateCnameLookup": true },
+        "prefs": { "auth.disable_negotiate_cname_lookup": { "local_state": true } }
       }
     ]
   },
 
   "EnableAuthNegotiatePort": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "EnableAuthNegotiatePort": true },
-    "pref_mappings": [
-      { "pref": "auth.enable_negotiate_port",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "EnableAuthNegotiatePort": true },
+        "prefs": { "auth.enable_negotiate_port": { "local_state": true } }
       }
     ]
   },
 
   "AuthServerWhitelist": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "AuthServerWhitelist": "localhost" },
-    "pref_mappings": [
-      { "pref": "auth.server_whitelist",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AuthServerWhitelist": "localhost" },
+        "prefs": { "auth.server_whitelist": { "local_state": true } }
       }
     ]
   },
 
   "AuthNegotiateDelegateWhitelist": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "AuthNegotiateDelegateWhitelist": "localhost" },
-    "pref_mappings": [
-      { "pref": "auth.negotiate_delegate_whitelist",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AuthNegotiateDelegateWhitelist": "localhost" },
+        "prefs": { "auth.negotiate_delegate_whitelist": { "local_state": true } }
       }
     ]
   },
 
   "AuthNegotiateDelegateByKdcPolicy": {
     "os": ["linux", "mac", "chromeos"],
-    "test_policy": { "AuthNegotiateDelegateByKdcPolicy": true },
-    "pref_mappings": [
-      { "pref": "auth.negotiate_delegate_by_kdc_policy",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AuthNegotiateDelegateByKdcPolicy": true },
+        "prefs": { "auth.negotiate_delegate_by_kdc_policy": { "local_state": true } }
       }
     ]
   },
 
   "GSSAPILibraryName": {
     "os": ["linux"],
-    "test_policy": { "GSSAPILibraryName": "libwhatever.so" },
-    "pref_mappings": [
-      { "pref": "auth.gssapi_library_name",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "GSSAPILibraryName": "libwhatever.so" },
+        "prefs": { "auth.gssapi_library_name": { "local_state": true } }
       }
     ]
   },
 
   "AuthAndroidNegotiateAccountType": {
     "os": ["android"],
-    "test_policy": { "AuthAndroidNegotiateAccountType": "com.example.spnego" },
-    "pref_mappings": [
-      { "pref": "auth.android_negotiate_account_type",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AuthAndroidNegotiateAccountType": "com.example.spnego" },
+        "prefs": { "auth.android_negotiate_account_type": { "local_state": true } }
       }
     ]
   },
 
   "AllowCrossOriginAuthPrompt": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "AllowCrossOriginAuthPrompt": true },
-    "pref_mappings": [
-      { "pref": "auth.allow_cross_origin_prompt",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AllowCrossOriginAuthPrompt": true },
+        "prefs": { "auth.allow_cross_origin_prompt": { "local_state": true } }
       }
     ]
   },
 
   "NtlmV2Enabled": {
     "os": ["linux", "mac", "chromeos", "android"],
-    "test_policy": { "NtlmV2Enabled": true },
-    "pref_mappings": [
-      { "pref": "auth.ntlm_v2_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NtlmV2Enabled": true },
+        "prefs": { "auth.ntlm_v2_enabled": { "local_state": true } }
       }
     ]
   },
 
   "KerberosEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "KerberosEnabled": true },
-    "pref_mappings": [
-      { "pref": "kerberos.enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "KerberosEnabled": true },
+        "prefs": { "kerberos.enabled": { "local_state": true } }
       }
     ]
   },
 
   "KerberosRememberPasswordEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "KerberosRememberPasswordEnabled": false },
-    "pref_mappings": [
-      { "pref": "kerberos.remember_password_enabled",
-        "local_state": true }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "KerberosRememberPasswordEnabled": false },
+        "prefs": { "kerberos.remember_password_enabled": { "local_state": true } }
+      }
     ]
   },
 
   "KerberosAddAccountsAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "KerberosAddAccountsAllowed": false },
-    "pref_mappings": [
-      { "pref": "kerberos.add_accounts_allowed",
-        "local_state": true }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "KerberosAddAccountsAllowed": false },
+        "prefs": { "kerberos.add_accounts_allowed": { "local_state": true } }
+      }
     ]
   },
 
   "KerberosAccounts": {
     "os": ["chromeos"],
-    "test_policy": {
-      "KerberosAccounts": [
-        {
-          "principal": "user1@realm",
-          "password": "password1",
-          "remember_password": true
-        },
-        {
-          "principal": "${LOGIN_EMAIL}"
-        },
-        {
-          "principal": "${LOGIN_ID}",
-          "password": "${PASSWORD}",
-          "krb5conf": [
-            "line1",
-            "line2"
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "KerberosAccounts": [
+            {
+              "principal": "user1@realm",
+              "password": "password1",
+              "remember_password": true
+            },
+            {
+              "principal": "${LOGIN_EMAIL}"
+            },
+            {
+              "principal": "${LOGIN_ID}",
+              "password": "${PASSWORD}",
+              "krb5conf": [
+                "line1",
+                "line2"
+              ]
+            }
           ]
-        }
-      ]
-    },
-    "pref_mappings": [
-      { "pref": "kerberos.accounts",
-        "local_state": true }
+        },
+        "prefs": { "kerberos.accounts": { "local_state": true } }
+      }
     ]
   },
 
   "PromptForDownloadLocation": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "PromptForDownloadLocation": false },
-    "pref_mappings": [
-      { "pref": "download.prompt_for_download" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PromptForDownloadLocation": false },
+        "prefs": { "download.prompt_for_download": {} }
+      }
     ]
   },
 
   "IsolateOrigins": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "IsolateOrigins": "" },
-    "note": "There is a pref behind this setting, but due to the fact that tests override the default with a command-line flag on some trybots, this setting cannot be verified with the common test."
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "IsolateOrigins": "" },
+        "note": "There is a pref behind this setting, but due to the fact that tests override the default with a command-line flag on some trybots, this setting cannot be verified with the common test."
+      }
+    ]
   },
 
   "SitePerProcess": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "SitePerProcess": false },
-    "note": "There is a pref behind this setting, but due to the fact that tests override the default with a command-line flag on some trybots, this setting cannot be verified with the common test."
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SitePerProcess": false },
+        "note": "There is a pref behind this setting, but due to the fact that tests override the default with a command-line flag on some trybots, this setting cannot be verified with the common test."
+      }
+    ]
   },
 
   "IsolateOriginsAndroid": {
-    "os": ["android"],
-    "test_policy": { "IsolateOriginsAndroid": "" },
-    "note": "There is a pref behind this setting, but due to the fact that tests override the default with a command-line flag on some trybots, this setting cannot be verified with the common test."
+    "os": [
+      "android"
+    ],
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "IsolateOriginsAndroid": "" },
+        "note": "There is a pref behind this setting, but due to the fact that tests override the default with a command-line flag on some trybots, this setting cannot be verified with the common test."
+      }
+    ]
   },
 
   "SitePerProcessAndroid": {
     "os": ["android"],
-    "test_policy": { "SitePerProcessAndroid": false },
-    "note": "There is a pref behind this setting, but due to the fact that tests override the default with a command-line flag on some trybots, this setting cannot be verified with the common test."
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SitePerProcessAndroid": false },
+        "note": "There is a pref behind this setting, but due to the fact that tests override the default with a command-line flag on some trybots, this setting cannot be verified with the common test."
+      }
+    ]
   },
 
   "WebDriverOverridesIncompatiblePolicies": {
@@ -1163,44 +1221,34 @@
 
   "DefaultDownloadDirectory": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultDownloadDirectory": "${user_home}/test-downloads" },
-    "can_be_recommended": true,
-    "pref_mappings": [
-      { "pref": "download.default_directory",
-        "check_for_mandatory": false,
-        "check_for_recommended": true
-      },
-      { "pref": "savefile.default_directory",
-        "check_for_mandatory": false,
-        "check_for_recommended": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultDownloadDirectory": "${user_home}/test-downloads" },
+        "can_be_recommended": true,
+        "prefs": {
+          "download.default_directory": { "check_for_mandatory": false, "check_for_recommended": true },
+          "savefile.default_directory": { "check_for_mandatory": false, "check_for_recommended": true }
+        }
       }
     ]
   },
 
   "SpellcheckLanguage": {
     "os": ["win", "linux", "chromeos"],
-    "test_policy": { "SpellcheckLanguage": [ "fr" ] },
-    "pref_mappings": [
-      { "pref": "spellcheck.forced_dictionaries",
-        "indicator_tests": [
-          { "policy": { "SpellcheckLanguage": [ "en-US" ] } },
-          { "policy": { "SpellcheckLanguage": [ "en-US", "ru", "sk" ] } },
-          { "policy": { "SpellcheckLanguage": [] } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SpellcheckLanguage": ["fr"] },
+        "prefs": { "spellcheck.forced_dictionaries": {} }
       }
     ]
   },
 
   "SpellcheckLanguageBlacklist": {
     "os": ["win", "linux", "chromeos"],
-    "test_policy": { "SpellcheckLanguageBlacklist": [ "fr" ] },
-    "pref_mappings": [
-      { "pref": "spellcheck.blacklisted_dictionaries",
-        "indicator_tests": [
-          { "policy": { "SpellcheckLanguage": [ "en-US" ] } },
-          { "policy": { "SpellcheckLanguage": [ "en-US", "ru", "sk" ] } },
-          { "policy": { "SpellcheckLanguage": [] } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SpellcheckLanguageBlacklist": ["fr"]},
+        "prefs": { "spellcheck.blacklisted_dictionaries": {} }
       }
     ]
   },
@@ -1208,20 +1256,21 @@
   "ThirdPartyBlockingEnabled": {
     "os": ["win"],
     "official_only": true,
-    "test_policy": { "ThirdPartyBlockingEnabled": false },
-    "pref_mappings": [
-      { "pref": "third_party_blocking_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ThirdPartyBlockingEnabled": false },
+        "prefs": { "third_party_blocking_enabled": { "local_state": true } }
       }
     ]
   },
 
   "SpellcheckEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "SpellcheckEnabled": false },
-    "pref_mappings": [
-      { "pref": "browser.enable_spellchecking",
-        "indicator_tests": [ { "policy": { "SpellcheckEnabled": false } } ] }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SpellcheckEnabled": false },
+        "prefs": { "browser.enable_spellchecking": {} }
+      }
     ]
   },
 
@@ -1231,67 +1280,83 @@
 
   "ExtensionInstallBlacklist": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "ExtensionInstallBlacklist": ["*"] },
-    "pref_mappings": [
-      { "pref": "extensions.install.denylist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ExtensionInstallBlacklist": ["*"] },
+        "prefs": { "extensions.install.denylist": {} }
+      }
     ]
   },
 
   "ExtensionInstallWhitelist": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "ExtensionInstallWhitelist": ["lcncmkcnkcdbbanbjakcencbaoegdjlp"] },
-    "pref_mappings": [
-      { "pref": "extensions.install.allowlist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ExtensionInstallWhitelist": ["lcncmkcnkcdbbanbjakcencbaoegdjlp"]},
+        "prefs": { "extensions.install.allowlist": {} }
+      }
     ]
   },
 
   "ExtensionInstallForcelist": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "ExtensionInstallForcelist": ["lcncmkcnkcdbbanbjakcencbaoegdjlp;https://clients2.google.com/service/update2/crx"] },
-    "pref_mappings": [
-      { "pref": "extensions.install.forcelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ExtensionInstallForcelist": ["lcncmkcnkcdbbanbjakcencbaoegdjlp;https://clients2.google.com/service/update2/crx"] },
+        "prefs": { "extensions.install.forcelist": {} }
+      }
     ]
   },
 
   "ExtensionInstallSources": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "ExtensionInstallSources": ["https://www.corp.monkey.net/*"] },
-    "pref_mappings": [
-      { "pref": "extensions.allowed_install_sites" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ExtensionInstallSources": ["https://www.corp.monkey.net/*"] },
+        "prefs": { "extensions.allowed_install_sites": {} }
+      }
     ]
   },
 
   "ExtensionAllowedTypes": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "ExtensionAllowedTypes": ["hosted_app"] },
-    "pref_mappings": [
-      { "pref": "extensions.allowed_types" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ExtensionAllowedTypes": ["hosted_app"]},
+        "prefs": { "extensions.allowed_types": {} }
+      }
     ]
   },
 
   "ExtensionSettings": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "ExtensionSettings": {
-        "abcdefghijklmnopabcdefghijklmnop" : {
-          "installation_mode": "allowed",
-          "blocked_permissions": ["history"]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ExtensionSettings": {
+            "abcdefghijklmnopabcdefghijklmnop": {
+              "installation_mode": "allowed",
+              "blocked_permissions": [
+                "history"
+              ]
+            },
+            "bcdefghijklmnopabcdefghijklmnopa": {
+              "installation_mode": "force_installed",
+              "update_url": "https://clients2.google.com/service/update2/crx",
+              "allowed_permissions": ["downloads"]
+            },
+            "*": {
+              "installation_mode": "blocked",
+              "blocked_permissions": ["downloads", "bookmarks", "u2fDevices"],
+              "install_sources": [
+                "http://company-intranet/chromeapps"
+              ],
+              "allowed_types": ["hosted_app"]
+            }
+          }
         },
-        "bcdefghijklmnopabcdefghijklmnopa" : {
-          "installation_mode": "force_installed",
-          "update_url": "https://clients2.google.com/service/update2/crx",
-          "allowed_permissions": ["downloads"]
-        },
-        "*": {
-          "installation_mode": "blocked",
-          "blocked_permissions": ["downloads", "bookmarks", "u2fDevices"],
-          "install_sources": ["http://company-intranet/chromeapps"],
-          "allowed_types": ["hosted_app"]
-        }
+        "prefs": { "extensions.management": {} }
       }
-    },
-    "pref_mappings": [
-      { "pref": "extensions.management" }
     ]
   },
 
@@ -1301,12 +1366,10 @@
 
   "BlockExternalExtensions": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "BlockExternalExtensions": true },
-    "pref_mappings": [
-      { "pref": "extensions.block_external_extensions",
-        "indicator_tests": [
-          { "policy": { "BlockExternalExtensions": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "BlockExternalExtensions": true },
+        "prefs": { "extensions.block_external_extensions": {} }
       }
     ]
   },
@@ -1314,41 +1377,32 @@
   "ShowHomeButton": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "ShowHomeButton": true },
-    "pref_mappings": [
-      { "pref": "browser.show_home_button",
-        "indicator_tests": [
-          { "policy": { "ShowHomeButton": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ShowHomeButton": true },
+        "prefs": { "browser.show_home_button": {} }
       }
     ]
   },
 
   "DeveloperToolsDisabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DeveloperToolsDisabled": true },
-    "pref_mappings": [
-      { "pref": "devtools.availability",
-        "indicator_test_url": "chrome://extensions-frame/",
-        "indicator_selector": "#dev-toggle-disabled-by-policy-indicator",
-        "indicator_tests": [
-          { "policy": { "DeveloperToolsDisabled": true },
-	    "value": 2 }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DeveloperToolsDisabled": true },
+        "prefs": { "devtools.availability": { "value": 2 }},
+        "indicator_tests": [{ "pref": "devtools.availability", "test_url": "chrome://extensions-frame/", "selector": "#dev-toggle-disabled-by-policy-indicator", "value": 2 }]
       }
     ]
   },
 
   "DeveloperToolsAvailability": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DeveloperToolsAvailability": 2 },
-    "pref_mappings": [
-      { "pref": "devtools.availability",
-        "indicator_test_url": "chrome://extensions-frame/",
-        "indicator_selector": "#dev-toggle-disabled-by-policy-indicator",
-        "indicator_tests": [
-          { "policy": { "DeveloperToolsAvailability": 2 } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DeveloperToolsAvailability": 2 },
+        "prefs": { "devtools.availability": { "value": 2 }},
+        "indicator_tests": [{ "pref": "devtools.availability", "test_url": "chrome://extensions-frame/", "selector": "#dev-toggle-disabled-by-policy-indicator", "value": 2 }]
       }
     ]
   },
@@ -1356,17 +1410,26 @@
   "RestoreOnStartup": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "RestoreOnStartup": 4 },
-    "pref_mappings": [
-      { "pref": "session.restore_on_startup",
-        "indicator_tests": [
-          { "policy": { "RestoreOnStartup": 1 },
-            "value": "1"},
-          { "policy": { "RestoreOnStartup": 4 },
-            "value": "4"},
-          { "policy": { "RestoreOnStartup": 5 },
-            "value": "5"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "RestoreOnStartup": 1 },
+        "prefs": { "session.restore_on_startup": { "value": 1 } }
+      },
+      {
+        "policies": { "RestoreOnStartup": 2 },
+        "prefs": { "session.restore_on_startup": { "value": 2 } }
+      },
+      {
+        "policies": { "RestoreOnStartup": 3 },
+        "prefs": { "session.restore_on_startup": { "value": 3 } }
+      },
+      {
+        "policies": { "RestoreOnStartup": 4 },
+        "prefs": { "session.restore_on_startup": { "value": 4 } }
+      },
+      {
+        "policies": { "RestoreOnStartup": 5 },
+        "prefs": { "session.restore_on_startup": { "value": 5 } }
       }
     ]
   },
@@ -1374,12 +1437,10 @@
   "RestoreOnStartupURLs": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "RestoreOnStartupURLs": ["chromium.org"] },
-    "pref_mappings": [
-      { "pref": "session.startup_urls",
-        "indicator_tests": [
-          { "policy": { "RestoreOnStartupURLs": ["chromium.org"] } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "RestoreOnStartupURLs": ["chromium.org"]},
+        "prefs": { "session.startup_urls": {} }
       }
     ]
   },
@@ -1387,81 +1448,91 @@
   "BlockThirdPartyCookies": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "BlockThirdPartyCookies": true },
-    "pref_mappings": [
-      { "pref": "profile.block_third_party_cookies",
-        "indicator_tests": [
-          { "policy": { "BlockThirdPartyCookies": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "BlockThirdPartyCookies": true },
+        "prefs": { "profile.block_third_party_cookies": {} }
       }
     ]
   },
 
   "DefaultSearchProviderEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultSearchProviderEnabled": false },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data",
-        "indicator_tests": [
-          { "policy": { "DefaultSearchProviderEnabled": false } },
-          { "policy": { "DefaultSearchProviderEnabled": true, "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}", "DefaultSearchProviderKeyword": "google" } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultSearchProviderEnabled": false },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      },
+      {
+        "policies": { "DefaultSearchProviderEnabled": false },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      },
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google"
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
       }
     ]
   },
 
   "DefaultSearchProviderName": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderName": "google.com"
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderName": "google.com"
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
   "DefaultSearchProviderKeyword": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}"
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}"
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
   "DefaultSearchProviderSearchURL": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google"
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data",
-        "indicator_selector": "[setting=search-engine]",
-        "indicator_tests": [
-          { "policy": { "DefaultSearchProviderEnabled": true, "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}" } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google"
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
       }
     ]
   },
 
   "DefaultSearchProviderSuggestURL": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderSuggestURL": "http://www.google.com/suggest?q={searchTerms}"
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderSuggestURL": "http://www.google.com/suggest?q={searchTerms}"
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
@@ -1471,53 +1542,66 @@
 
   "DefaultSearchProviderNewTabURL": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderNewTabURL": "http://www.google.com/newtab"
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderNewTabURL": "http://www.google.com/newtab"
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
   "DefaultSearchProviderIconURL": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderIconURL": "http://www.google.com/favicon.ico"
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderIconURL": "http://www.google.com/favicon.ico"
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
   "DefaultSearchProviderEncodings": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderEncodings": ["UTF-8"]
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderEncodings": [
+            "UTF-8"
+          ]
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
   "DefaultSearchProviderAlternateURLs": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderAlternateURLs": ["http://www.google.com/#q={searchTerms}", "http://www.google.com/search#q={searchTerms}"]
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderAlternateURLs": [
+            "http://www.google.com/#q={searchTerms}",
+            "http://www.google.com/search#q={searchTerms}"
+          ]
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
@@ -1527,40 +1611,46 @@
 
   "DefaultSearchProviderImageURL": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderImageURL": "http://www.google.com/searchbyimage/upload"
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderImageURL": "http://www.google.com/searchbyimage/upload"
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
   "DefaultSearchProviderSearchURLPostParams": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderSearchURLPostParams": ""
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderSearchURLPostParams": ""
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
   "DefaultSearchProviderSuggestURLPostParams": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderSuggestURLPostParams": ""
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderSuggestURLPostParams": ""
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {}}
+      }
     ]
   },
 
@@ -1570,89 +1660,95 @@
 
   "DefaultSearchProviderImageURLPostParams": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultSearchProviderEnabled": true,
-      "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
-      "DefaultSearchProviderKeyword": "google",
-      "DefaultSearchProviderImageURLPostParams": "image_content={imageThumbnail},image_url={imageURL},sbisrc={imageSearchSource}"
-    },
-    "pref_mappings": [
-      { "pref": "default_search_provider_data.template_url_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultSearchProviderEnabled": true,
+          "DefaultSearchProviderSearchURL": "http://www.google.com/?q={searchTerms}",
+          "DefaultSearchProviderKeyword": "google",
+          "DefaultSearchProviderImageURLPostParams": "image_content={imageThumbnail},image_url={imageURL},sbisrc={imageSearchSource}"
+        },
+        "prefs": { "default_search_provider_data.template_url_data": {} }
+      }
     ]
   },
 
   "DefaultCookiesSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultCookiesSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.cookies",
-        "indicator_selector": "[content-setting=cookies]",
-        "indicator_tests": [
-          { "policy": { "DefaultCookiesSetting": 1 },
-            "value": "allow"},
-          { "policy": { "DefaultCookiesSetting": 2 },
-            "value": "block"},
-          { "policy": { "DefaultCookiesSetting": 4 },
-            "value": "session_only"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultCookiesSetting": 1 },
+        "prefs": { "profile.managed_default_content_settings.cookies": { "value": 1 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.cookies", "selector": "[content-setting=cookies]", "value": 1 }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultCookiesSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.cookies": { "value": 2 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.cookies", "selector": "[content-setting=cookies]", "value": "block" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultCookiesSetting": 3 },
+        "prefs": { "profile.managed_default_content_settings.cookies": { "value": 3 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.cookies", "selector": "[content-setting=cookies]", "value": "session_only" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "DefaultImagesSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultImagesSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.images",
-        "indicator_selector": "[content-setting=images]",
-        "indicator_tests": [
-          { "policy": { "DefaultImagesSetting": 1 },
-            "value": "allow"},
-          { "policy": { "DefaultImagesSetting": 2 },
-            "value": "block"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultImagesSetting": 1 },
+        "prefs": { "profile.managed_default_content_settings.images": { "value": 1 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.images", "selector": "[content-setting=images]", "value": "allow" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultImagesSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.images": { "value": 2 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.images", "selector": "[content-setting=images]", "value": "block" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "DefaultInsecureContentSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultInsecureContentSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.insecure_content",
-        "indicator_selector": "[content-setting=mixed-script]",
-        "indicator_tests": [
-          { "policy": { "DefaultInsecureContentSetting": 2 },
-            "value": "block"},
-          { "policy": { "DefaultInsecureContentSetting": 3 },
-            "value": "ask"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultInsecureContentSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.insecure_content": { "value": 2 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.insecure_content", "selector": "[content-setting=mixed-script]", "value": "block" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultInsecureContentSetting": 3 },
+        "prefs": { "profile.managed_default_content_settings.insecure_content": { "value": 3 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.insecure_content", "selector": "[content-setting=mixed-script]", "value": "ask" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "DefaultJavaScriptSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultJavaScriptSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.javascript",
-        "indicator_selector": "[content-setting=javascript]",
-        "indicator_tests": [
-          { "policy": { "DefaultJavaScriptSetting": 1 },
-            "value": "allow"},
-          { "policy": { "DefaultJavaScriptSetting": 2 },
-            "value": "block"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultJavaScriptSetting": 1 },
+        "prefs": { "profile.managed_default_content_settings.javascript": { "value": 1 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.javascript", "selector": "[content-setting=javascript]", "value": "allow" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultJavaScriptSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.javascript": { "value": 2 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.javascript", "selector": "[content-setting=javascript]", "value": "block" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "DefaultKeygenSetting": {
@@ -1661,133 +1757,170 @@
 
   "LegacySameSiteCookieBehaviorEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "LegacySameSiteCookieBehaviorEnabled": 1 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.legacy_cookie_access" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "LegacySameSiteCookieBehaviorEnabled": 1 },
+        "prefs": { "profile.managed_default_content_settings.legacy_cookie_access": {} }
+      }
     ]
   },
 
   "LegacySameSiteCookieBehaviorEnabledForDomainList": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "LegacySameSiteCookieBehaviorEnabledForDomainList": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_legacy_cookie_access_allowed_for_domains" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "LegacySameSiteCookieBehaviorEnabledForDomainList": ["[*.]google.com"] },
+        "prefs": { "profile.managed_legacy_cookie_access_allowed_for_domains": {} }
+      }
     ]
   },
 
   "DefaultPluginsSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultPluginsSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.plugins",
-        "indicator_selector": "[content-setting=plugins]",
-        "indicator_tests": [
-          { "policy": { "DefaultPluginsSetting": 1 },
-            "value": "allow"},
-          { "policy": { "DefaultPluginsSetting": 2 },
-            "value": "block"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultPluginsSetting": 1 },
+        "prefs": { "profile.managed_default_content_settings.plugins": { "value": 1 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.plugins", "selector": "[content-setting=plugins]", "value": "allow" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultPluginsSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.plugins": { } },
+        "indicator_tests" :[{ "pref": "profile.managed_default_content_settings.plugins", "selector": "[content-setting=plugins]", "value": "block" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "DefaultPopupsSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultPopupsSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.popups",
-        "indicator_selector": "[content-setting=popups]",
-        "indicator_tests": [
-          { "policy": { "DefaultPopupsSetting": 1 },
-            "value": "allow"},
-          { "policy": { "DefaultPopupsSetting": 2 },
-            "value": "block"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultPopupsSetting": 1 },
+        "prefs": { "profile.managed_default_content_settings.popups": { "value": 1 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.popups", "selector": "[content-setting=popups]", "value": "allow" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultPopupsSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.popups": {} },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.popups","selector": "[content-setting=popups]", "value": "block" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "DefaultNotificationsSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultNotificationsSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.notifications",
-        "indicator_selector": "[content-setting=notifications]",
-        "indicator_tests": [
-          { "policy": { "DefaultNotificationsSetting": 1 },
-            "value": "allow"},
-          { "policy": { "DefaultNotificationsSetting": 2 },
-            "value": "block"},
-          { "policy": { "DefaultNotificationsSetting": 3 },
-            "value": "ask"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultNotificationsSetting": 1 },
+        "prefs": { "profile.managed_default_content_settings.notifications": { "value": 1 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.notifications", "selector": "[content-setting=notifications]", "value": "allow" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultNotificationsSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.notifications": { "value": 2 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.notifications", "selector": "[content-setting=notifications]", "value": "block" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultNotificationsSetting": 3 },
+        "prefs": { "profile.managed_default_content_settings.notifications": { "value": 3 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.notifications", "selector": "[content-setting=notifications]", "value": "ask" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "DefaultGeolocationSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultGeolocationSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.geolocation",
-        "indicator_selector": "[content-setting=location]",
-        "indicator_tests": [
-          { "policy": { "DefaultGeolocationSetting": 1 },
-            "value": "allow"},
-          { "policy": { "DefaultGeolocationSetting": 2 },
-            "value": "block"},
-          { "policy": { "DefaultGeolocationSetting": 3 },
-            "value": "ask"}
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultGeolocationSetting": 1 },
+        "prefs": { "profile.managed_default_content_settings.geolocation": { "value": 1 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.geolocation", "selector": "[content-setting=location]","value": "allow"}],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultGeolocationSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.geolocation": { "value": 2 } },
+        "indicator_tests": [{"pref": "profile.managed_default_content_settings.geolocation", "selector": "[content-setting=location]", "value": "block" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultGeolocationSetting": 3 },
+        "prefs": { "profile.managed_default_content_settings.geolocation": { "value": 3 } },
+        "indicator_tests": [{ "pref": "profile.managed_default_content_settings.geolocation", "selector": "[content-setting=location]", "value": "ask" }],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "DefaultMediaStreamSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultMediaStreamSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.media_stream",
-        "indicator_selector": "[content-setting=media-stream-mic],[content-setting=media-stream-camera]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultMediaStreamSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.media_stream": { "value": 2 } },
         "indicator_tests": [
-          { "policy": { "DefaultMediaStreamSetting": 2 },
+          {
+            "pref": "profile.managed_default_content_settings.media_stream",
+            "selector": "[content-setting=media-stream-mic],[content-setting=media-stream-camera]",
             "value": "block"
-          },
-          { "policy": { "DefaultMediaStreamSetting": 3 },
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      },
+      {
+        "policies": { "DefaultMediaStreamSetting": 3 },
+        "prefs": { "profile.managed_default_content_settings.media_stream": { "value": 3 } },
+        "indicator_tests": [
+          {
+            "pref": "profile.managed_default_content_settings.media_stream",
+            "selector": "[content-setting=media-stream-mic],[content-setting=media-stream-camera]",
             "value": "ask"
           }
-        ]
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "DefaultMediaStreamSetting.OverriddenByAudioCaptureAllowed": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultMediaStreamSetting": 3,
-                     "AudioCaptureAllowed": false },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.media_stream",
-        "indicator_selector": "[content-setting=media-stream-mic]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AudioCaptureAllowed": false },
+        "prefs": { "profile.managed_default_content_settings.media_stream": { "value": 0, "expect_default": true } },
         "indicator_tests": [
-          { "policy": { "AudioCaptureAllowed": false },
+          {
+            "pref": "profile.managed_default_content_settings.media_stream",
+            "selector": "[content-setting=media-stream-mic]",
             "value": "block"
-          },
-          { "policy": { "AudioCaptureAllowed": true,
-                        "DefaultMediaStreamSetting": 2 },
+          }
+        ]
+      },
+      {
+        "policies": { "DefaultMediaStreamSetting": 2, "AudioCaptureAllowed": true },
+        "prefs": { "profile.managed_default_content_settings.media_stream": { "value": 2 } },
+        "indicator_tests": [
+          {
+            "pref": "profile.managed_default_content_settings.media_stream",
+            "selector": "[content-setting=media-stream-mic]",
             "value": "block"
-          },
-          { "policy": { "AudioCaptureAllowed": true,
-                        "DefaultMediaStreamSetting": 3 },
+          }
+        ]
+      },
+      {
+        "policies": { "DefaultMediaStreamSetting": 3, "AudioCaptureAllowed": true },
+        "prefs": { "profile.managed_default_content_settings.media_stream": { "value": 3 } },
+        "indicator_tests": [
+          {
+            "pref": "profile.managed_default_content_settings.media_stream",
+            "selector": "[content-setting=media-stream-mic]",
             "value": "ask"
           }
         ]
@@ -1797,36 +1930,54 @@
 
   "DefaultMediaStreamSetting.OverriddenByVideoCaptureAllowed": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultMediaStreamSetting": 3,
-                     "VideoCaptureAllowed": false },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.media_stream",
-        "indicator_selector": "[content-setting=media-stream-camera]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "VideoCaptureAllowed": false },
+        "prefs": { "profile.managed_default_content_settings.media_stream": { "value": 0, "expect_default": true }},
         "indicator_tests": [
-          { "policy": { "VideoCaptureAllowed": false },
+          {
+            "pref": "profile.managed_default_content_settings.media_stream",
+            "selector": "[content-setting=media-stream-mic]",
             "value": "block"
-          },
-          { "policy": { "VideoCaptureAllowed": true,
-                        "DefaultMediaStreamSetting": 2 },
+          }
+        ]
+      },
+      {
+        "policies": { "DefaultMediaStreamSetting": 2, "VideoCaptureAllowed": true },
+        "prefs": { "profile.managed_default_content_settings.media_stream": { "value": 2 } },
+        "indicator_tests": [
+          {
+            "pref": "profile.managed_default_content_settings.media_stream",
+            "selector": "[content-setting=media-stream-mic]",
             "value": "block"
-          },
-          { "policy": { "VideoCaptureAllowed": true,
-                        "DefaultMediaStreamSetting": 3 },
+          }
+        ]
+      },
+      {
+        "policies": { "DefaultMediaStreamSetting": 3, "VideoCaptureAllowed": true },
+        "prefs": { "profile.managed_default_content_settings.media_stream": { "value": 3 } },
+        "indicator_tests": [
+          {
+            "pref": "profile.managed_default_content_settings.media_stream",
+            "selector": "[content-setting=media-stream-mic]",
             "value": "ask"
           }
         ]
       }
+
     ]
   },
 
   "AudioCaptureAllowed": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "AudioCaptureAllowed": false },
-    "pref_mappings": [
-      { "pref": "hardware.audio_capture_enabled",
-        "indicator_selector": "[content-setting=media-stream-mic][value=block]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AudioCaptureAllowed": false },
+        "prefs": { "hardware.audio_capture_enabled": { "value": false } },
         "indicator_tests": [
-          { "policy": { "AudioCaptureAllowed": false },
+          {
+            "pref": "hardware.audio_capture_enabled",
+            "selector": "[content-setting=media-stream-mic][value=block]",
             "value": "block"
           }
         ]
@@ -1836,26 +1987,25 @@
 
   "AudioCaptureAllowedUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "AudioCaptureAllowedUrls": ["[*.]google.com"] },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "hardware.audio_capture_allowed_urls",
-        "indicator_selector": "[content-exception=media-stream-mic]",
-        "indicator_tests": [
-          { "policy": { "AudioCaptureAllowedUrls": ["[*.]google.com"] } }
-        ]
+        "policies": { "AudioCaptureAllowedUrls": ["[*.]google.com"] },
+        "prefs": { "hardware.audio_capture_allowed_urls": {}},
+        "indicator_tests": [{ "pref": "hardware.audio_capture_allowed_urls", "selector": "[content-exception=media-stream-mic]" }]
       }
     ]
   },
 
   "VideoCaptureAllowed": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "VideoCaptureAllowed": false },
-    "pref_mappings": [
-      { "pref": "hardware.video_capture_enabled",
-        "indicator_selector": "[content-setting=media-stream-camera][value=block]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "VideoCaptureAllowed": false },
+        "prefs": { "hardware.video_capture_enabled": { "value": false }},
         "indicator_tests": [
-          { "policy": { "VideoCaptureAllowed": false },
+          {
+            "pref": "hardware.video_capture_enabled",
+            "selector": "[content-setting=media-stream-camera][value=block]",
             "value": "block"
           }
         ]
@@ -1865,186 +2015,216 @@
 
   "VideoCaptureAllowedUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "VideoCaptureAllowedUrls": ["[*.]google.com"] },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "hardware.video_capture_allowed_urls",
-        "indicator_selector": "[content-exception=media-stream-camera]",
-        "indicator_tests": [
-          { "policy": { "VideoCaptureAllowedUrls": ["[*.]google.com"] } }
-        ]
+        "policies": { "VideoCaptureAllowedUrls": ["[*.]google.com"] },
+        "prefs": { "hardware.video_capture_allowed_urls": {}},
+        "indicator_tests": [{ "pref": "hardware.video_capture_allowed_urls", "selector": "[content-exception=media-stream-camera]" }]
       }
     ]
   },
 
   "DefaultWebBluetoothGuardSetting": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DefaultWebBluetoothGuardSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.web_bluetooth_guard" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultWebBluetoothGuardSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.web_bluetooth_guard": {} }
+      }
     ]
   },
 
   "DefaultWebUsbGuardSetting": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "DefaultWebUsbGuardSetting": 2 },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.web_usb_guard" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DefaultWebUsbGuardSetting": 2 },
+        "prefs": { "profile.managed_default_content_settings.web_usb_guard": {} }
+      }
     ]
   },
 
   "AutoSelectCertificateForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "AutoSelectCertificateForUrls": ["{'pattern':'https://example.com','filter':{'ISSUER':{'CN': 'issuer-name'}}}"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_auto_select_certificate_for_urls" }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AutoSelectCertificateForUrls": [
+            "{'pattern':'https://example.com','filter':{'ISSUER':{'CN': 'issuer-name'}}}"
+          ]
+        },
+        "prefs": { "profile.managed_auto_select_certificate_for_urls": {}},
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      }
+    ]
   },
 
   "CookiesAllowedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "CookiesAllowedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_cookies_allowed_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=cookies]').click();",
-        "indicator_selector": "[content-exception=cookies]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CookiesAllowedForUrls": ["[*.]google.com"] },
+        "prefs": { "profile.managed_cookies_allowed_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "CookiesAllowedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_cookies_allowed_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=cookies]').click();",
+            "selector": "[content-exception=cookies]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "CookiesBlockedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "CookiesBlockedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_cookies_blocked_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=cookies]').click();",
-        "indicator_selector": "[content-exception=cookies]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CookiesBlockedForUrls": [ "[*.]google.com" ] },
+        "prefs": { "profile.managed_cookies_blocked_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "CookiesBlockedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_cookies_blocked_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=cookies]').click();",
+            "selector": "[content-exception=cookies]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "CookiesSessionOnlyForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "CookiesSessionOnlyForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_cookies_sessiononly_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=cookies]').click();",
-        "indicator_selector": "[content-exception=cookies]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CookiesSessionOnlyForUrls": ["[*.]google.com"] },
+        "prefs": { "profile.managed_cookies_sessiononly_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "CookiesSessionOnlyForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_cookies_sessiononly_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=cookies]').click();",
+            "selector": "[content-exception=cookies]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "ImagesAllowedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "ImagesAllowedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_images_allowed_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=images]').click();",
-        "indicator_selector": "[content-exception=images]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ImagesAllowedForUrls": ["[*.]google.com"] },
+        "prefs": { "profile.managed_images_allowed_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "ImagesAllowedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_images_allowed_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=images]').click();",
+            "selector": "[content-exception=images]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "ImagesBlockedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "ImagesBlockedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_images_blocked_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=images]').click();",
-        "indicator_selector": "[content-exception=images]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ImagesBlockedForUrls": ["[*.]google.com"]},
+        "prefs": { "profile.managed_images_blocked_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "ImagesBlockedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_images_blocked_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=images]').click();",
+            "selector": "[content-exception=images]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "InsecureContentAllowedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "InsecureContentAllowedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_insecure_content_allowed_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=mixed-script]').click();",
-        "indicator_selector": "[content-exception=mixed-script]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "InsecureContentAllowedForUrls": ["[*.]google.com"]
+        },
+        "prefs": { "profile.managed_insecure_content_allowed_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "InsecureContentAllowedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_insecure_content_allowed_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=mixed-script]').click();",
+            "selector": "[content-exception=mixed-script]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "InsecureContentBlockedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "InsecureContentBlockedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_insecure_content_blocked_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=mixed-script]').click();",
-        "indicator_selector": "[content-exception=mixed-script]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "InsecureContentBlockedForUrls": ["[*.]google.com"]
+        },
+        "prefs": { "profile.managed_insecure_content_blocked_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "InsecureContentBlockedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_insecure_content_blocked_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=mixed-script]').click();",
+            "selector": "[content-exception=mixed-script]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "JavaScriptAllowedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "JavaScriptAllowedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_javascript_allowed_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=javascript]').click();",
-        "indicator_selector": "[content-exception=javascript]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "JavaScriptAllowedForUrls": ["[*.]google.com"]
+        },
+        "prefs": { "profile.managed_javascript_allowed_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "JavaScriptAllowedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_javascript_allowed_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=javascript]').click();",
+            "selector": "[content-exception=javascript]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "JavaScriptBlockedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "JavaScriptBlockedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_javascript_blocked_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=javascript]').click();",
-        "indicator_selector": "[content-exception=javascript]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "JavaScriptBlockedForUrls": ["[*.]google.com"]},
+        "prefs": { "profile.managed_javascript_blocked_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "JavaScriptBlockedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_javascript_blocked_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=javascript]').click();",
+            "selector": "[content-exception=javascript]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "KeygenAllowedForUrls": {
@@ -2057,184 +2237,226 @@
 
   "PluginsAllowedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "PluginsAllowedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_plugins_allowed_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=plugins]').click();",
-        "indicator_selector": "[content-exception=plugins]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PluginsAllowedForUrls": ["[*.]google.com"]},
+        "prefs": { "profile.managed_plugins_allowed_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "PluginsAllowedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_plugins_allowed_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=plugins]').click();",
+            "selector": "[content-exception=plugins]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "PluginsBlockedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "PluginsBlockedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_plugins_blocked_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=plugins]').click();",
-        "indicator_selector": "[content-exception=plugins]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PluginsBlockedForUrls": ["[*.]google.com"]},
+        "prefs": { "profile.managed_plugins_blocked_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "PluginsBlockedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_plugins_blocked_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=plugins]').click();",
+            "selector": "[content-exception=plugins]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "PopupsAllowedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "PopupsAllowedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_popups_allowed_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=popups]').click();",
-        "indicator_selector": "[content-exception=popups]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PopupsAllowedForUrls": ["[*.]google.com"]},
+        "prefs": { "profile.managed_popups_allowed_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "PopupsAllowedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_popups_allowed_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=popups]').click();",
+            "selector": "[content-exception=popups]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "PopupsBlockedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "PopupsBlockedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_popups_blocked_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=popups]').click();",
-        "indicator_selector": "[content-exception=popups]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PopupsBlockedForUrls": ["[*.]google.com"]},
+        "prefs": { "profile.managed_popups_blocked_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "PopupsBlockedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_popups_blocked_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=popups]').click();",
+            "selector": "[content-exception=popups]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "NotificationsAllowedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "NotificationsAllowedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_notifications_allowed_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=notifications]').click();",
-        "indicator_selector": "[content-exception=notifications]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NotificationsAllowedForUrls": ["[*.]google.com"]},
+        "prefs": { "profile.managed_notifications_allowed_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "NotificationsAllowedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_notifications_allowed_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=notifications]').click();",
+            "selector": "[content-exception=notifications]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "NotificationsBlockedForUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "NotificationsBlockedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_notifications_blocked_for_urls",
-        "indicator_test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=notifications]').click();",
-        "indicator_selector": "[content-exception=notifications]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NotificationsBlockedForUrls": ["[*.]google.com"]},
+        "prefs": { "profile.managed_notifications_blocked_for_urls": {} },
         "indicator_tests": [
-          { "policy": { "NotificationsBlockedForUrls": ["[*.]google.com"] } }
-        ]
+          {
+            "pref": "profile.managed_notifications_blocked_for_urls",
+            "test_setup_js": "document.querySelector('button.exceptions-list-button[contentType=notifications]').click();",
+            "selector": "[content-exception=notifications]"
+          }
+        ],
+        "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
       }
-    ],
-
-    "note": "TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    ]
   },
 
   "WebUsbAllowDevicesForUrls": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": {
-      "WebUsbAllowDevicesForUrls": [
-        {
-          "devices": [{"vendor_id": 1234, "product_id": 5678}],
-          "urls": ["https://google.com,https://google.com", "https://www.youtube.com"]
-        }
-      ]
-    },
-    "pref_mappings": [
-      { "pref": "profile.managed_web_usb_allow_devices_for_urls" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "WebUsbAllowDevicesForUrls": [
+            {
+              "devices": [
+                {
+                  "vendor_id": 1234,
+                  "product_id": 5678
+                }
+              ],
+              "urls": [
+                "https://google.com,https://google.com",
+                "https://www.youtube.com"
+              ]
+            }
+          ]
+        },
+        "prefs": { "profile.managed_web_usb_allow_devices_for_urls": {} }
+      }
     ]
   },
 
   "DeviceLoginScreenWebUsbAllowDevicesForUrls": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceLoginScreenWebUsbAllowDevicesForUrls": [{
-      "devices": [{"vendor_id": 1234, "product_id": 5678}],
-      "urls": ["https://google.com", "https://requesting.com,https://embedded.com"]
-    }]},
-    "pref_mappings": [
-      { "pref": "device_login_screen_webusb_allow_devices_for_urls",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceLoginScreenWebUsbAllowDevicesForUrls": [
+            {
+              "devices": [
+                {
+                  "vendor_id": 1234,
+                  "product_id": 5678
+                }
+              ],
+              "urls": [
+                "https://google.com",
+                "https://requesting.com,https://embedded.com"
+              ]
+            }
+          ]
+        },
+        "prefs": { "device_login_screen_webusb_allow_devices_for_urls": { "local_state": true } }
       }
     ]
   },
 
   "WebUsbAskForUrls": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "WebUsbAskForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_web_usb_ask_for_urls" }
-    ],
-
-    "note": "TODO(reillyg): Add indicator tests. TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"WebUsbAskForUrls": ["[*.].com"]},
+        "prefs": { "profile.managed_web_usb_ask_for_urls": {}},
+        "note": "TODO(reillyg): Add indicator tests. TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      }
+    ]
   },
 
   "WebUsbBlockedForUrls": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "WebUsbBlockedForUrls": ["[*.]google.com"] },
-    "pref_mappings": [
-      { "pref": "profile.managed_web_usb_blocked_for_urls" }
-    ],
-
-    "note": "TODO(reillyg): Add indicator tests. TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"WebUsbBlockedForUrls": ["[*.]google.com"]},
+        "prefs": { "profile.managed_web_usb_blocked_for_urls": {} },
+        "note": "TODO(reillyg): Add indicator tests. TODO(bartfab): Flag this with can_be_recommended when http://crbug.com/106682 is fixed."
+      }
+    ]
   },
 
   "Disable3DAPIs": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "Disable3DAPIs": true },
-    "pref_mappings": [
-      { "pref": "disable_3d_apis" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"Disable3DAPIs": true },
+        "prefs": { "disable_3d_apis": {}}
+      }
     ]
   },
 
-  "InstantEnabled": {
-  },
+  "InstantEnabled": {},
 
   "TranslateEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "TranslateEnabled": false },
-    "pref_mappings": [
-      { "pref": "translate.enabled",
-        "indicator_tests": [
-          { "policy": { "TranslateEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"TranslateEnabled": false},
+        "prefs": { "translate.enabled": {} }
       }
     ]
   },
+
   "AppRecommendationZeroStateEnabled": {
-    "os:": ["chromeos"],
-    "test_policy": { "AppRecommendationZeroStateEnabled": false },
-    "pref_mappings": [
-      { "pref": "zero_state_app_install_recommendation.enabled"
+    "os:": [
+      "chromeos"
+    ],
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"AppRecommendationZeroStateEnabled": false},
+        "prefs": { "zero_state_app_install_recommendation.enabled": {}}
       }
     ]
   },
+
   "AllowOutdatedPlugins": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "AllowOutdatedPlugins": true },
-    "pref_mappings": [
-      { "pref": "plugins.allow_outdated" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"AllowOutdatedPlugins": true},
+        "prefs": { "plugins.allow_outdated": {}}
+      }
     ]
   },
 
@@ -2244,73 +2466,80 @@
 
   "RunAllFlashInAllowMode": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "RunAllFlashInAllowMode": true },
-    "pref_mappings": [
-      { "pref": "plugins.run_all_flash_in_allow_mode" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"RunAllFlashInAllowMode": true},
+        "prefs": { "plugins.run_all_flash_in_allow_mode": {}}
+      }
     ]
   },
 
   "BookmarkBarEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "BookmarkBarEnabled": true },
-    "pref_mappings": [
-      { "pref": "bookmark_bar.show_on_all_tabs",
-        "indicator_tests": [
-          { "policy": { "BookmarkBarEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"BookmarkBarEnabled": true},
+        "prefs": { "bookmark_bar.show_on_all_tabs": {}}
       }
     ]
   },
 
   "EditBookmarksEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "EditBookmarksEnabled": false },
-    "pref_mappings": [
-      { "pref": "bookmarks.editing_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"EditBookmarksEnabled": false},
+        "prefs": { "bookmarks.editing_enabled": {}}
+      }
     ]
   },
 
   "ShowAppsShortcutInBookmarkBar": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "ShowAppsShortcutInBookmarkBar": false },
-    "pref_mappings": [
-      { "pref": "bookmark_bar.show_apps_shortcut" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"ShowAppsShortcutInBookmarkBar": false},
+        "prefs": { "bookmark_bar.show_apps_shortcut": {}}
+      }
     ]
   },
 
   "AllowFileSelectionDialogs": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "AllowFileSelectionDialogs": false },
-    "pref_mappings": [
-      { "pref": "select_file_dialogs.allowed",
-        "local_state": true
-      },
-      { "pref": "download.prompt_for_download",
-        "indicator_tests": [
-          { "policy": { "AllowFileSelectionDialogs": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"AllowFileSelectionDialogs": false},
+        "prefs": {
+          "select_file_dialogs.allowed": { "local_state": true },
+          "download.prompt_for_download": {}
+        }
       }
     ]
   },
 
   "SecurityKeyPermitAttestation": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "SecurityKeyPermitAttestation": ["https://example.com", "example.net"] },
-    "pref_mappings": [
-      { "pref": "securitykey.permit_attestation" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SecurityKeyPermitAttestation": [
+            "https://example.com",
+            "example.net"
+          ]
+        },
+        "prefs": { "securitykey.permit_attestation": {}}
+      }
     ]
   },
 
   "ImportBookmarks": {
     "os": ["win", "mac", "linux"],
     "can_be_recommended": true,
-    "test_policy": { "ImportBookmarks": false },
-    "pref_mappings": [
-      { "pref": "import_dialog_bookmarks",
-        "indicator_tests": [
-          { "policy": { "ImportBookmarks": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"ImportBookmarks": false},
+        "prefs": { "import_dialog_bookmarks": {}}
       }
     ]
   },
@@ -2318,33 +2547,31 @@
   "ImportHistory": {
     "os": ["win", "mac", "linux"],
     "can_be_recommended": true,
-    "test_policy": { "ImportHistory": false },
-    "pref_mappings": [
-      { "pref": "import_dialog_history",
-        "indicator_tests": [
-          { "policy": { "ImportHistory": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"ImportHistory": false},
+        "prefs": { "import_dialog_history": {}}
       }
     ]
   },
 
   "ImportHomepage": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "ImportHomepage": false },
-    "pref_mappings": [
-      { "pref": "import_home_page" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"ImportHomepage": false},
+        "prefs": { "import_home_page": {}}
+      }
     ]
   },
 
   "ImportSearchEngine": {
     "os": ["win", "mac", "linux"],
     "can_be_recommended": true,
-    "test_policy": { "ImportSearchEngine": false },
-    "pref_mappings": [
-      { "pref": "import_dialog_search_engine",
-        "indicator_tests": [
-          { "policy": { "ImportSearchEngine": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"ImportSearchEngine": false},
+        "prefs": { "import_dialog_search_engine": {}}
       }
     ]
   },
@@ -2352,12 +2579,10 @@
   "ImportSavedPasswords": {
     "os": ["win", "mac", "linux"],
     "can_be_recommended": true,
-    "test_policy": { "ImportSavedPasswords": false },
-    "pref_mappings": [
-      { "pref": "import_dialog_saved_passwords",
-        "indicator_tests": [
-          { "policy": { "ImportSavedPasswords": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"ImportSavedPasswords": false},
+        "prefs": { "import_dialog_saved_passwords": {}}
       }
     ]
   },
@@ -2365,42 +2590,43 @@
   "ImportAutofillFormData": {
     "os": ["win", "mac", "linux"],
     "can_be_recommended": true,
-    "test_policy": { "ImportAutofillFormData": false },
-    "pref_mappings": [
-      { "pref": "import_dialog_autofill_form_data",
-        "indicator_tests": [
-          { "policy": { "ImportAutofillFormData": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"ImportAutofillFormData": false},
+        "prefs": { "import_dialog_autofill_form_data": {}}
       }
     ]
   },
 
   "MaxConnectionsPerProxy": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "MaxConnectionsPerProxy": 16 },
-    "pref_mappings": [
-      { "pref": "net.max_connections_per_proxy",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"MaxConnectionsPerProxy": 16},
+        "prefs": { "net.max_connections_per_proxy": { "local_state": true }}
       }
     ]
   },
 
-  "HideWebStorePromo": {
-  },
+  "HideWebStorePromo": {},
 
   "URLBlacklist": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "URLBlacklist": ["google.com"] },
-    "pref_mappings": [
-      { "pref": "policy.url_blacklist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"URLBlacklist": ["google.com"]},
+        "prefs": { "policy.url_blacklist": {}}
+      }
     ]
   },
 
   "URLWhitelist": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "URLWhitelist": ["google.com"] },
-    "pref_mappings": [
-      { "pref": "policy.url_whitelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"URLWhitelist": ["google.com"]},
+        "prefs": { "policy.url_whitelist": {}}
+      }
     ]
   },
 
@@ -2430,68 +2656,78 @@
 
   "DisablePrintPreview": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "DisablePrintPreview": false },
-    "pref_mappings": [
-      { "pref": "printing.print_preview_disabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"DisablePrintPreview": false},
+        "prefs": { "printing.print_preview_disabled": {}}
+      }
     ]
   },
 
   "DefaultPrinterSelection": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "DefaultPrinterSelection": "{ 'kind': 'local', 'namePattern': '.*' }"
-    },
-    "pref_mappings": [
-      { "pref": "printing.default_destination_selection_rules" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DefaultPrinterSelection": "{ 'kind': 'local', 'namePattern': '.*' }"
+        },
+        "prefs": { "printing.default_destination_selection_rules": {}}
+      }
     ]
   },
 
   "PrintPreviewUseSystemDefaultPrinter": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "PrintPreviewUseSystemDefaultPrinter": false },
-    "pref_mappings": [
-      { "pref": "printing.use_system_default_printer" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"PrintPreviewUseSystemDefaultPrinter": false},
+        "prefs": { "printing.use_system_default_printer": {}}
+      }
     ]
   },
 
   "BackgroundModeEnabled": {
-    "os": ["win", "linux"],
+    "os": [
+      "win",
+      "linux"
+    ],
     "can_be_recommended": true,
-    "test_policy": { "BackgroundModeEnabled": false },
-    "pref_mappings": [
-      { "pref": "background_mode.enabled",
-        "local_state": true,
-        "indicator_tests": [
-          { "policy": { "BackgroundModeEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"BackgroundModeEnabled": false},
+        "prefs": { "background_mode.enabled": { "local_state": true }}
       }
     ]
   },
 
   "RestrictSigninToPattern": {
     "os": ["win", "mac", "linux"],
-    "test_policy": { "RestrictSigninToPattern": ".*@google.com" },
-    "pref_mappings": [
-      { "pref": "google.services.username_pattern",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"RestrictSigninToPattern": ".*@google.com"},
+        "prefs": { "google.services.username_pattern": { "local_state": true }}
       }
     ]
   },
 
   "DisableSafeBrowsingProceedAnyway": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DisableSafeBrowsingProceedAnyway": true },
-    "pref_mappings": [
-      { "pref": "safebrowsing.proceed_anyway_disabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"DisableSafeBrowsingProceedAnyway": true},
+        "prefs": { "safebrowsing.proceed_anyway_disabled": {}}
+      }
     ]
   },
 
   "SafeBrowsingExtendedReportingOptInAllowed": {
     "note": "This policy is being deprecated.",
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "SafeBrowsingExtendedReportingOptInAllowed": true },
-    "pref_mappings": [
-      { "pref": "safebrowsing.extended_reporting_opt_in_allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"SafeBrowsingExtendedReportingOptInAllowed": true},
+        "prefs": { "safebrowsing.extended_reporting_opt_in_allowed": {}}
+      }
     ]
   },
 
@@ -2499,40 +2735,44 @@
     "os": ["win", "linux", "mac", "chromeos"],
     "official_only": true,
     "can_be_recommended": true,
-    "test_policy": { "SpellCheckServiceEnabled": false },
-    "pref_mappings": [
-      { "pref": "spellcheck.use_spelling_service",
-        "indicator_tests": [
-          { "policy": { "SpellCheckServiceEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"SpellCheckServiceEnabled": false},
+        "prefs": { "spellcheck.use_spelling_service": {}}
       }
     ]
   },
 
   "DisableScreenshots": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "DisableScreenshots": true },
-    "pref_mappings": [
-      { "pref": "disable_screenshots" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DisableScreenshots": true },
+        "prefs": { "disable_screenshots": {} }
+      }
     ]
   },
 
   "BuiltInDnsClientEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "BuiltInDnsClientEnabled": true },
-    "pref_mappings": [
-      { "pref": "async_dns.enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BuiltInDnsClientEnabled": true
+        },
+        "prefs": { "async_dns.enabled": { "local_state": true } }
       }
     ]
   },
 
   "WPADQuickCheckEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "WPADQuickCheckEnabled": true },
-    "pref_mappings": [
-      { "pref": "proxy.quick_check_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "WPADQuickCheckEnabled": true
+        },
+        "prefs": { "proxy.quick_check_enabled": { "local_state": true } }
       }
     ]
   },
@@ -2544,177 +2784,227 @@
   "RegisteredProtocolHandlers": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "RegisteredProtocolHandlers": {"protocol": "test", "url": "http://example.com/%s", "default": "true"} },
-    "pref_mappings": [
-      { "pref": "custom_handlers.policy.registered_protocol_handlers",
-        "check_for_mandatory": false
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "RegisteredProtocolHandlers": {
+            "protocol": "test",
+            "url": "http://example.com/%s",
+            "default": "true"
+          }
+        },
+        "prefs": { "custom_handlers.policy.registered_protocol_handlers": { "check_for_mandatory": false }}
       }
     ]
   },
 
   "HideWebStoreIcon": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "HideWebStoreIcon": true },
-    "pref_mappings": [
-      { "pref": "hide_web_store_icon" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "HideWebStoreIcon": true
+        },
+        "prefs": { "hide_web_store_icon": {} }
+      }
     ]
   },
 
   "VariationsRestrictParameter": {
-    "test_policy": { "VariationsRestrictParameter": "restricted" },
-    "pref_mappings": [
-      { "pref": "variations_restrict_parameter",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "VariationsRestrictParameter": "restricted"
+        },
+        "prefs": { "variations_restrict_parameter": { "local_state": true } }
       }
     ]
   },
 
   "DataCompressionProxyEnabled": {
-    "os": ["android"],
-    "test_policy": { "DataCompressionProxyEnabled": false },
-    "pref_mappings": [
-      { "pref": "spdy_proxy.enabled" }
+    "os": [
+      "android"
+    ],
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DataCompressionProxyEnabled": false
+        },
+        "prefs": { "spdy_proxy.enabled": {} }
+      }
     ]
   },
 
-  "ForceEphemeralProfiles": {
-  },
+  "ForceEphemeralProfiles": {},
 
   "UserAvatarImage": {
     "os": ["chromeos"],
-    "test_policy": {
-      "UserAvatarImage": {
-        "url": "http://localhost/",
-        "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "UserAvatarImage": {
+            "url": "http://localhost/",
+            "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+          }
+        },
+        "selector": "#account-picture-indicator"
       }
-    },
-    "indicator_selector": "#account-picture-indicator"
+    ]
   },
 
   "WallpaperImage": {
     "os": ["chromeos"],
-    "test_policy": {
-      "WallpaperImage": {
-        "url": "http://localhost/",
-        "hash": "baddecafbaddecafbaddecafbaddecafbaddecafbaddecafbaddecafbaddecaf"
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "WallpaperImage": {
+            "url": "http://localhost/",
+            "hash": "baddecafbaddecafbaddecafbaddecafbaddecafbaddecafbaddecafbaddecaf"
+          }
+        },
+        "selector": "#wallpaper-indicator"
       }
-    },
-    "indicator_selector": "#wallpaper-indicator"
+    ]
   },
 
   "BrowserGuestModeEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "BrowserGuestModeEnabled": true },
-    "pref_mappings": [
-      { "pref": "profile.browser_guest_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserGuestModeEnabled": true
+        },
+        "prefs": { "profile.browser_guest_enabled": { "local_state": true } }
       }
     ]
   },
 
   "BrowserGuestModeEnforced": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "BrowserGuestModeEnforced": true },
-    "pref_mappings": [
-      { "pref": "profile.browser_guest_enforced",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserGuestModeEnforced": true
+        },
+        "prefs": { "profile.browser_guest_enforced": { "local_state": true } }
       }
     ]
   },
 
   "BrowserAddPersonEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "BrowserAddPersonEnabled": true },
-    "pref_mappings": [
-      { "pref": "profile.add_person_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserAddPersonEnabled": true
+        },
+        "prefs": { "profile.add_person_enabled": { "local_state": true } }
       }
     ]
   },
 
   "ForceBrowserSignin": {
     "os": ["win"],
-    "test_policy": { "ForceBrowserSignin": false },
-    "pref_mappings": [
-      { "pref": "profile.force_browser_signin",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ForceBrowserSignin": false
+        },
+        "prefs": { "profile.force_browser_signin": { "local_state": true } }
       }
     ]
   },
 
   "BrowserSignin.DesktopNoForce": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "BrowserSignin": 1 },
-    "pref_mappings": [
-      { "pref": "signin.allowed_on_next_startup",
-        "indicator_tests": [
-          { "policy": { "BrowserSignin": 1 } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSignin": 1
+        },
+        "prefs": { "signin.allowed_on_next_startup": {} }
       }
     ]
   },
 
   "BrowserSignin.DesktopForce": {
-    "os": ["win", "mac"],
-    "test_policy": { "BrowserSignin": 2 },
-    "pref_mappings": [
-      { "pref": "signin.allowed_on_next_startup" },
-      { "pref": "profile.force_browser_signin",
-        "local_state": true
-      },
-      { "pref": "profile.browser_guest_enabled",
-        "local_state": true
+    "os": [
+      "win",
+      "mac"
+    ],
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSignin": 2
+        },
+        "prefs": {
+          "signin.allowed_on_next_startup": {},
+          "profile.force_browser_signin": { "local_state": true },
+          "profile.browser_guest_enabled": { "local_state": true }
+          }
       }
     ]
   },
 
   "BrowserSignin.LinuxForceNotSupported": {
-    "os": ["linux"],
-    "test_policy": { "BrowserSignin": 2 },
-    "pref_mappings": [
-      { "pref": "signin.allowed_on_next_startup" }
+    "os": [
+      "linux"
+    ],
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSignin": 2
+        },
+        "prefs": { "signin.allowed_on_next_startup": {} }
+      }
     ]
   },
 
   "BrowserSignin.AndroidForce": {
-    "os": ["android"],
-    "test_policy": { "BrowserSignin": 2 },
-    "pref_mappings": [
-      { "pref": "signin.allowed" },
-      { "pref": "profile.force_browser_signin",
-        "local_state": true
+    "os": [
+      "android"
+    ],
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSignin": 2
+        },
+        "prefs": {
+          "signin.allowed": {},
+          "profile.force_browser_signin": { "local_state": true }
+        }
       }
     ]
   },
 
   "MachineLevelUserCloudPolicyEnrollmentToken": {
     "os": ["win", "linux", "mac"],
-    "test_policy": {"MachineLevelUserCloudPolicyEnrollmentToken": ""},
-    "pref_mappings": [
-        { "pref": "policy.machine_level_user_cloud_policy_enrollment_token",
-          "local_state": true
-        }
-      ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "MachineLevelUserCloudPolicyEnrollmentToken": "" },
+        "prefs": { "policy.machine_level_user_cloud_policy_enrollment_token": { "local_state": true } }
+      }
+    ]
   },
 
   "CloudManagementEnrollmentToken": {},
 
   "CloudManagementEnrollmentMandatory": {
     "os": ["win", "linux", "mac"],
-    "test_policy": {"CloudManagementEnrollmentMandatory": true},
-    "pref_mappings": [
-        { "pref": "policy.cloud_management_enrollment_mandatory",
-          "local_state": true
-        }
-      ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CloudManagementEnrollmentMandatory": true },
+        "prefs": { "policy.cloud_management_enrollment_mandatory": { "local_state": true } }
+      }
+    ]
   },
 
   "SSLVersionMin": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "SSLVersionMin": "tls1.1" },
-    "pref_mappings": [
-      { "pref": "ssl.version_min",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SSLVersionMin": "tls1.1" },
+        "prefs": { "ssl.version_min": { "local_state": true } }
       }
     ]
   },
@@ -2724,30 +3014,48 @@
   },
 
   "SSLVersionMax": {
-     "note": "This policy has been removed, see https://crbug.com/939760."
+    "note": "This policy has been removed, see https://crbug.com/939760."
   },
 
   "CertificateTransparencyEnforcementDisabledForUrls": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "CertificateTransparencyEnforcementDisabledForUrls": ["example.com"] },
-    "pref_mappings": [
-      { "pref": "certificate_transparency.excluded_hosts" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CertificateTransparencyEnforcementDisabledForUrls": [
+            "example.com"
+          ]
+        },
+        "prefs": { "certificate_transparency.excluded_hosts": {} }
+      }
     ]
   },
 
   "CertificateTransparencyEnforcementDisabledForCas": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "CertificateTransparencyEnforcementDisabledForCas": ["sha256/AAAAAAAAAAAAAAAAAAAAAA=="] },
-    "pref_mappings": [
-      { "pref": "certificate_transparency.excluded_spkis" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CertificateTransparencyEnforcementDisabledForCas": [
+            "sha256/AAAAAAAAAAAAAAAAAAAAAA=="
+          ]
+        },
+        "prefs": { "certificate_transparency.excluded_spkis": {} }
+      }
     ]
   },
 
   "CertificateTransparencyEnforcementDisabledForLegacyCas": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "CertificateTransparencyEnforcementDisabledForLegacyCas": ["sha256/AAAAAAAAAAAAAAAAAAAAAA=="] },
-    "pref_mappings": [
-      { "pref": "certificate_transparency.excluded_legacy_spkis" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CertificateTransparencyEnforcementDisabledForLegacyCas": [
+            "sha256/AAAAAAAAAAAAAAAAAAAAAA=="
+          ]
+        },
+        "prefs": { "certificate_transparency.excluded_legacy_spkis": {} }
+      }
     ]
   },
 
@@ -2764,203 +3072,267 @@
   },
 
   "SuppressUnsupportedOSWarning": {
-    "os": ["chromeos", "linux", "mac", "win"],
-    "test_policy": { "SuppressUnsupportedOSWarning": true },
-    "pref_mappings": [
-      { "pref": "browser.suppress_unsupported_os_warning",
-        "local_state": true
+    "os": [
+      "chromeos",
+      "linux",
+      "mac",
+      "win"
+    ],
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SuppressUnsupportedOSWarning": true
+        },
+        "prefs": { "browser.suppress_unsupported_os_warning": { "local_state": true } }
       }
     ]
   },
 
   "AllowedDomainsForApps": {
-    "pref": "settings.allowed_domains_for_apps",
-    "test_policy": { "AllowedDomainsForApps": "google.com,mit.edu" },
-    "os": ["win", "linux", "mac", "chromeos", "android"],
-    "pref_mappings": [
-      { "pref": "settings.allowed_domains_for_apps"
-      }
-    ]
-  },
-
-  "AllowedDomainsForApps": {
-    "pref": "settings.allowed_domains_for_apps",
-    "test_policy": { "AllowedDomainsForApps": "google.com,mit.edu" },
-    "os": ["win", "linux", "mac", "chromeos", "android"],
-    "pref_mappings": [
-      { "pref": "settings.allowed_domains_for_apps"
-      }
-    ]
-  },
-
-  "AllowedDomainsForApps": {
-    "pref": "settings.allowed_domains_for_apps",
-    "test_policy": { "AllowedDomainsForApps": "google.com,mit.edu" },
-    "os": ["win", "linux", "mac", "chromeos", "android"],
-    "pref_mappings": [
-      { "pref": "settings.allowed_domains_for_apps"
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AllowedDomainsForApps": "google.com,mit.edu"
+        },
+        "os": [
+          "win",
+          "linux",
+          "mac",
+          "chromeos",
+          "android"
+        ],
+        "prefs": { "settings.allowed_domains_for_apps": {} }
       }
     ]
   },
 
   "EnableMediaRouter": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "EnableMediaRouter": true },
-    "pref_mappings": [
-      { "pref": "media_router.enable_media_router" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "EnableMediaRouter": true
+        },
+        "prefs": { "media_router.enable_media_router": {} }
+      }
     ]
   },
 
   "ShowCastIconInToolbar": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "ShowCastIconInToolbar": false },
-    "pref_mappings": [
-      { "pref": "media_router.show_cast_icon_in_toolbar" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ShowCastIconInToolbar": false
+        },
+        "prefs": { "media_router.show_cast_icon_in_toolbar": {} }
+      }
     ]
   },
 
   "MediaRouterCastAllowAllIPs": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "MediaRouterCastAllowAllIPs": false },
-    "pref_mappings": [
-      { "pref": "media_router.cast_allow_all_ips",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "MediaRouterCastAllowAllIPs": false
+        },
+        "prefs": { "media_router.cast_allow_all_ips": { "local_state": true } }
       }
     ]
   },
 
   "NTPContentSuggestionsEnabled": {
-    "os": ["android"],
-    "test_policy": { "NTPContentSuggestionsEnabled": false },
-    "pref_mappings": [
-      { "pref": "ntp_snippets.enable" }
+    "os": [
+      "android"
+    ],
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "NTPContentSuggestionsEnabled": false
+        },
+        "prefs": { "ntp_snippets.enable": {} }
+      }
     ]
   },
 
   "WebRtcUdpPortRange": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "WebRtcUdpPortRange": "10000-11999" },
-    "pref_mappings": [
-      { "pref": "webrtc.udp_port_range" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "WebRtcUdpPortRange": "10000-11999"
+        },
+        "prefs": { "webrtc.udp_port_range": {} }
+      }
     ]
   },
 
   "WebRtcLocalIpsAllowedUrls": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "WebRtcLocalIpsAllowedUrls": ["https://www.example.com", "*example.com*"]},
-    "pref_mappings": [
-      { "pref": "webrtc.local_ips_allowed_urls" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "WebRtcLocalIpsAllowedUrls": [
+            "https://www.example.com",
+            "*example.com*"
+          ]
+        },
+        "prefs": { "webrtc.local_ips_allowed_urls": {} }
+      }
     ]
   },
 
   "ComponentUpdatesEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "ComponentUpdatesEnabled": true },
-    "pref_mappings": [
-      { "pref": "component_updates.component_updates_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ComponentUpdatesEnabled": true
+        },
+        "prefs": { "component_updates.component_updates_enabled": { "local_state": true } }
       }
     ]
   },
 
   "RoamingProfileSupportEnabled": {
     "os": ["win"],
-    "test_policy": { "RoamingProfileSupportEnabled": true },
-    "pref_mappings": [
-      { "pref": "sync.enable_local_sync_backend" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "RoamingProfileSupportEnabled": true
+        },
+        "prefs": { "sync.enable_local_sync_backend": {} }
+      }
     ]
   },
 
   "RoamingProfileLocation": {
     "os": ["win"],
-    "test_policy": { "RoamingProfileLocation": "${roaming_app_data}" },
-    "pref_mappings": [
-      { "pref": "sync.local_sync_backend_dir" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "RoamingProfileLocation": "${roaming_app_data}"
+        },
+        "prefs": { "sync.local_sync_backend_dir": {} }
+      }
     ]
   },
 
   "RelaunchNotification": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "RelaunchNotification": 1 },
-    "pref_mappings": [
-      { "pref": "browser.relaunch_notification",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "RelaunchNotification": 1
+        },
+        "prefs": { "browser.relaunch_notification": { "local_state": true } }
       }
     ]
   },
 
   "RelaunchNotificationPeriod": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "RelaunchNotificationPeriod": 86400000 },
-    "pref_mappings": [
-      { "pref": "browser.relaunch_notification_period",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "RelaunchNotificationPeriod": 86400000
+        },
+        "prefs": { "browser.relaunch_notification_period": { "local_state": true } }
       }
     ]
   },
 
   "RelaunchHeadsUpPeriod": {
     "os": ["chromeos"],
-    "test_policy": { "RelaunchHeadsUpPeriod": 86400000 },
-    "pref_mappings": [
-      { "pref": "browser.relaunch_heads_up_period",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "RelaunchHeadsUpPeriod": 86400000
+        },
+        "prefs": { "browser.relaunch_heads_up_period": { "local_state": true } }
       }
     ]
   },
 
   "PromotionalTabsEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "PromotionalTabsEnabled": false },
-    "pref_mappings": [
-      { "pref": "browser.promotional_tabs_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PromotionalTabsEnabled": false
+        },
+        "prefs": { "browser.promotional_tabs_enabled": { "local_state": true } }
       }
     ]
   },
 
   "AllowPopupsDuringPageUnload": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "AllowPopupsDuringPageUnload": true },
-    "pref_mappings": [{ "pref": "allow_popups_during_page_unload" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AllowPopupsDuringPageUnload": true
+        },
+        "prefs": { "allow_popups_during_page_unload": {} }
+      }
+    ]
   },
 
   "CommandLineFlagSecurityWarningsEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "CommandLineFlagSecurityWarningsEnabled": true },
-    "pref_mappings": [
-      { "pref": "browser.command_line_flag_security_warnings_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CommandLineFlagSecurityWarningsEnabled": true
+        },
+        "prefs": { "browser.command_line_flag_security_warnings_enabled": { "local_state": true } }
       }
     ]
   },
 
   "AllowSyncXHRInPageDismissal": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "AllowSyncXHRInPageDismissal": true },
-    "pref_mappings": [{ "pref": "allow_sync_xhr_in_page_dismissal" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AllowSyncXHRInPageDismissal": true
+        },
+        "prefs": { "allow_sync_xhr_in_page_dismissal": {} }
+      }
+    ]
   },
 
   "ExternalProtocolDialogShowAlwaysOpenCheckbox": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "ExternalProtocolDialogShowAlwaysOpenCheckbox": true },
-    "pref_mappings": [
-      { "pref": "external_protocol_dialog.show_always_open_checkbox" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ExternalProtocolDialogShowAlwaysOpenCheckbox": true
+        },
+        "policy_pref_mapping_tests": [
+          {
+            "policies": {
+              "ExternalProtocolDialogShowAlwaysOpenCheckbox": true
+            },
+            "prefs": { "external_protocol_dialog.show_always_open_checkbox": { "local_state": false, "value": true }
+            }
+          }
+        ]
+      }
     ]
   },
 
   "WebComponentsV0Enabled": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
     "test_policy": { "WebComponentsV0Enabled": true },
-    "pref_mappings": [{ "pref": "web_components_v0_enabled" }]
+    "policy_pref_mapping_tests": { "web_components_v0_enabled": {} }
   },
 
   "GloballyScopeHTTPAuthCacheEnabled": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
     "test_policy": { "GloballyScopeHTTPAuthCacheEnabled": true },
-    "pref_mappings": [
-      { "pref": "auth.globally_scoped_http_auth_cache_enabled" }
-    ]
+    "policy_pref_mapping_tests": { "auth.globally_scoped_http_auth_cache_enabled": {} }
   },
 
   "----- Chrome OS policies ------------------------------------------------": {},
@@ -2968,624 +3340,763 @@
   "ChromeOsLockOnIdleSuspend": {
     "os": ["chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "ChromeOsLockOnIdleSuspend": true },
-    "pref_mappings": [
-      { "pref": "settings.enable_screen_lock",
-        "indicator_tests": [
-          { "policy": { "ChromeOsLockOnIdleSuspend": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ChromeOsLockOnIdleSuspend": true
+        },
+        "prefs": { "settings.enable_screen_lock": {} }
       }
     ]
   },
 
   "PolicyRefreshRate": {
     "os": ["chromeos"],
-    "test_policy": { "PolicyRefreshRate": 300000 },
-    "pref_mappings": [
-      { "pref": "policy.user_refresh_rate",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PolicyRefreshRate": 300000
+        },
+        "prefs": { "policy.user_refresh_rate": { "local_state": true } }
       }
     ]
   },
 
   "MaxInvalidationFetchDelay": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "PolicyRefreshRate": 15000 },
-    "pref_mappings": []
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PolicyRefreshRate": 15000
+        }
+      }
+    ]
   },
 
-  "OpenNetworkConfiguration": {
-  },
+  "OpenNetworkConfiguration": {},
 
   "SAMLOfflineSigninTimeLimit": {
     "os": ["chromeos"],
-    "test_policy": { "SAMLOfflineSigninTimeLimit": 0 },
-    "pref_mappings": [
-      { "pref": "saml.offline_signin_time_limit" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SAMLOfflineSigninTimeLimit": 0
+        },
+        "prefs": { "saml.offline_signin_time_limit": {} }
+      }
     ]
   },
 
   "DriveDisabled": {
     "os": ["chromeos"],
-    "test_policy": { "DriveDisabled": true },
-    "pref_mappings": [
-      { "pref": "gdata.disabled",
-        "indicator_tests": [
-          { "policy": { "DriveDisabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DriveDisabled": true
+        },
+        "prefs": { "gdata.disabled": {} }
       }
     ]
   },
 
   "DriveDisabledOverCellular": {
     "os": ["chromeos"],
-    "test_policy": { "DriveDisabledOverCellular": true },
-    "pref_mappings": [
-      { "pref": "gdata.cellular.disabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DriveDisabledOverCellular": true
+        },
+        "prefs": { "gdata.cellular.disabled": {} }
+      }
     ]
   },
 
   "PinnedLauncherApps": {
     "os": ["chromeos"],
     "can_be_recommended": true,
-    "test_policy": { "PinnedLauncherApps": [] },
-    "pref_mappings": [
-      { "pref": "policy_pinned_launcher_apps" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PinnedLauncherApps": []
+        },
+        "prefs": { "policy_pinned_launcher_apps": {} }
+      }
     ]
   },
 
   "ExternalStorageDisabled": {
     "os": ["chromeos"],
-    "test_policy": { "ExternalStorageDisabled": true },
-    "pref_mappings": [
-      { "pref": "hardware.external_storage_disabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ExternalStorageDisabled": true
+        },
+        "prefs": { "hardware.external_storage_disabled": {} }
+      }
     ]
   },
 
   "ExternalStorageReadOnly": {
     "os": ["chromeos"],
-    "test_policy": { "ExternalStorageReadOnly": true },
-    "pref_mappings": [
-      { "pref": "hardware.external_storage_read_only" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ExternalStorageReadOnly": true
+        },
+        "prefs": { "hardware.external_storage_read_only": {} }
+      }
     ]
   },
 
   "AudioOutputAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "AudioOutputAllowed": true },
-    "pref_mappings": [
-      { "pref": "hardware.audio_output_enabled",
-        "local_state": true }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AudioOutputAllowed": true
+        },
+        "prefs": { "hardware.audio_output_enabled": { "local_state": true } }
+      }
     ]
   },
 
   "ShowLogoutButtonInTray": {
     "os": ["chromeos"],
-    "test_policy": { "ShowLogoutButtonInTray": true },
-    "pref_mappings": [
-      { "pref": "show_logout_button_in_tray" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ShowLogoutButtonInTray": true
+        },
+        "prefs": { "show_logout_button_in_tray": {} }
+      }
     ]
   },
 
   "ShelfAutoHideBehavior": {
     "os": ["chromeos"],
-    "test_policy": { "ShelfAutoHideBehavior": "Always" },
-    "pref_mappings": [
-      { "pref": "auto_hide_behavior_local" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ShelfAutoHideBehavior": "Always"
+        },
+        "prefs": { "auto_hide_behavior_local": {} }
+      }
     ]
   },
 
   "ShelfAlignment": {
     "os": ["chromeos"],
-    "test_policy": { "ShelfAlignment": "Left" },
-    "pref_mappings": [
-      { "pref": "shelf_alignment_local" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ShelfAlignment": "Left"
+        },
+        "prefs": { "shelf_alignment_local": {} }
+      }
     ]
   },
 
   "UserDisplayName": {
-    "os": ["chromeos"]
+    "os": [
+      "chromeos"
+    ]
   },
 
   "SessionLengthLimit": {
     "os": ["chromeos"],
-    "test_policy": { "SessionLengthLimit": 3600000 },
-    "pref_mappings": [
-      { "pref": "session.length_limit",
-        "local_state": true }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SessionLengthLimit": 3600000
+        },
+        "prefs": { "session.length_limit": { "local_state": true } }
+      }
     ]
   },
 
   "ScreenDimDelayAC": {
     "os": ["chromeos"],
-    "test_policy": { "ScreenDimDelayAC": 420000 },
-    "pref_mappings": [
-      { "pref": "power.ac_screen_dim_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ScreenDimDelayAC": 420000
+        },
+        "prefs": { "power.ac_screen_dim_delay_ms": {} }
+      }
     ]
   },
 
   "ScreenOffDelayAC": {
     "os": ["chromeos"],
-    "test_policy": { "ScreenOffDelayAC": 480000 },
-    "pref_mappings": [
-      { "pref": "power.ac_screen_off_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ScreenOffDelayAC": 480000
+        },
+        "prefs": { "power.ac_screen_off_delay_ms": {} }
+      }
     ]
   },
 
   "ScreenLockDelayAC": {
     "os": ["chromeos"],
-    "test_policy": { "ScreenLockDelayAC": 600000 },
-    "pref_mappings": [
-      { "pref": "power.ac_screen_lock_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ScreenLockDelayAC": 600000
+        },
+        "prefs": { "power.ac_screen_lock_delay_ms": {} }
+      }
     ]
   },
 
   "IdleWarningDelayAC": {
     "os": ["chromeos"],
-    "test_policy": { "IdleWarningDelayAC": 1800000 },
-    "pref_mappings": [
-      { "pref": "power.ac_idle_warning_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "IdleWarningDelayAC": 1800000
+        },
+        "prefs": { "power.ac_idle_warning_delay_ms": {} }
+      }
     ]
   },
 
   "IdleDelayAC": {
     "os": ["chromeos"],
-    "test_policy": { "IdleDelayAC": 1800000 },
-    "pref_mappings": [
-      { "pref": "power.ac_idle_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "IdleDelayAC": 1800000
+        },
+        "prefs": { "power.ac_idle_delay_ms": {} }
+      }
     ]
   },
 
   "ScreenDimDelayBattery": {
     "os": ["chromeos"],
-    "test_policy": { "ScreenDimDelayBattery": 300000 },
-    "pref_mappings": [
-      { "pref": "power.battery_screen_dim_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ScreenDimDelayBattery": 300000
+        },
+        "prefs": { "power.battery_screen_dim_delay_ms": {} }
+      }
     ]
   },
 
   "ScreenOffDelayBattery": {
     "os": ["chromeos"],
-    "test_policy": { "ScreenOffDelayBattery": 360000 },
-    "pref_mappings": [
-      { "pref": "power.battery_screen_off_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ScreenOffDelayBattery": 360000
+        },
+        "prefs": { "power.battery_screen_off_delay_ms": {} }
+      }
     ]
   },
 
   "ScreenLockDelayBattery": {
     "os": ["chromeos"],
-    "test_policy": { "ScreenLockDelayBattery": 600000 },
-    "pref_mappings": [
-      { "pref": "power.battery_screen_lock_delay_ms"  }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ScreenLockDelayBattery": 600000
+        },
+        "prefs": { "power.battery_screen_lock_delay_ms": {} }
+      }
     ]
   },
 
   "IdleWarningDelayBattery": {
     "os": ["chromeos"],
-    "test_policy": { "IdleWarningDelayBattery": 600000 },
-    "pref_mappings": [
-      { "pref": "power.battery_idle_warning_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "IdleWarningDelayBattery": 600000
+        },
+        "prefs": { "power.battery_idle_warning_delay_ms": {} }
+      }
     ]
   },
 
   "IdleDelayBattery": {
     "os": ["chromeos"],
-    "test_policy": { "IdleDelayBattery": 600000 },
-    "pref_mappings": [
-      { "pref": "power.battery_idle_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "IdleDelayBattery": 600000 },
+        "prefs": { "power.battery_idle_delay_ms": {} }
+      }
     ]
   },
 
   "IdleAction": {
     "os": ["chromeos"],
-    "test_policy": { "IdleAction": 0 },
-    "pref_mappings": [
-      { "pref": "power.ac_idle_action" },
-      { "pref": "power.battery_idle_action" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "IdleAction": 0 },
+        "prefs": { "power.ac_idle_action": {}, "power.battery_idle_action": {} }
+      }
     ]
   },
 
   "IdleActionAC": {
     "os": ["chromeos"],
-    "test_policy": { "IdleActionAC": 0 },
-    "pref_mappings": [
-      { "pref": "power.ac_idle_action" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "IdleActionAC": 0 },
+        "prefs": { "power.ac_idle_action": {} }
+      }
     ]
   },
 
   "IdleActionBattery": {
     "os": ["chromeos"],
-    "test_policy": { "IdleActionBattery": 0 },
-    "pref_mappings": [
-      { "pref": "power.battery_idle_action" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "IdleActionBattery": 0 },
+        "prefs": { "power.battery_idle_action": {} }
+      }
     ]
   },
 
   "LidCloseAction": {
     "os": ["chromeos"],
-    "test_policy": { "LidCloseAction": 0 },
-    "pref_mappings": [
-      { "pref": "power.lid_closed_action" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "LidCloseAction": 0 },
+        "prefs": { "power.lid_closed_action": {} }
+      }
     ]
   },
 
   "PowerManagementUsesAudioActivity": {
     "os": ["chromeos"],
-    "test_policy": { "PowerManagementUsesAudioActivity": true },
-    "pref_mappings": [
-      { "pref": "power.use_audio_activity" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PowerManagementUsesAudioActivity": true },
+        "prefs": { "power.use_audio_activity": {} }
+      }
     ]
   },
 
   "PowerManagementUsesVideoActivity": {
     "os": ["chromeos"],
-    "test_policy": { "PowerManagementUsesVideoActivity": true },
-    "pref_mappings": [
-      { "pref": "power.use_video_activity" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PowerManagementUsesVideoActivity": true },
+        "prefs": { "power.use_video_activity": {} }
+      }
     ]
   },
 
-  "PresentationIdleDelayScale": {
-  },
+  "PresentationIdleDelayScale": {},
 
   "PresentationScreenDimDelayScale": {
     "os": ["chromeos"],
-    "test_policy": { "PresentationScreenDimDelayScale": 200 },
-    "pref_mappings": [
-      { "pref": "power.presentation_screen_dim_delay_factor" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PresentationScreenDimDelayScale": 200 },
+        "prefs": { "power.presentation_screen_dim_delay_factor": {} }
+      }
     ]
   },
 
   "AllowWakeLocks": {
     "os": ["chromeos"],
-    "test_policy": { "AllowWakeLocks": false },
-    "pref_mappings": [
-      { "pref": "power.allow_wake_locks" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AllowWakeLocks": false },
+        "prefs": { "power.allow_wake_locks": {} }
+      }
     ]
   },
 
   "AllowScreenWakeLocks": {
     "os": ["chromeos"],
-    "test_policy": { "AllowScreenWakeLocks": false },
-    "pref_mappings": [
-      { "pref": "power.allow_screen_wake_locks" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AllowScreenWakeLocks": false },
+        "prefs": { "power.allow_screen_wake_locks": {} }
+      }
     ]
   },
 
   "UserActivityScreenDimDelayScale": {
     "os": ["chromeos"],
-    "test_policy": { "UserActivityScreenDimDelayScale": 200 },
-    "pref_mappings": [
-      { "pref": "power.user_activity_screen_dim_delay_factor" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "UserActivityScreenDimDelayScale": 200 },
+        "prefs": { "power.user_activity_screen_dim_delay_factor": {} }
+      }
     ]
   },
 
   "WaitForInitialUserActivity": {
     "os": ["chromeos"],
-    "test_policy": { "WaitForInitialUserActivity": true },
-    "pref_mappings": [
-      { "pref": "session.wait_for_initial_user_activity",
-        "local_state": true },
-      { "pref": "power.wait_for_initial_user_activity" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "WaitForInitialUserActivity": true
+        },
+        "prefs": {
+          "session.wait_for_initial_user_activity": { "local_state": true },
+          "power.wait_for_initial_user_activity": {}
+        }
+      }
     ]
   },
 
   "PowerManagementIdleSettings": {
     "os": ["chromeos"],
-    "test_policy": { "PowerManagementIdleSettings" : { "AC": { "Delays": { "ScreenDim": 5000, "ScreenOff": 7000, "IdleWarning": 8000, "Idle": 9000 }, "IdleAction": "Logout" }, "Battery": { "Delays": { "ScreenDim": 1000, "ScreenOff": 3000, "IdleWarning": 4000, "Idle": 5000 }, "IdleAction": "Logout"} } },
-    "pref_mappings": [
-      { "pref": "power.ac_screen_dim_delay_ms" },
-      { "pref": "power.ac_screen_off_delay_ms" },
-      { "pref": "power.ac_idle_warning_delay_ms" },
-      { "pref": "power.ac_idle_delay_ms" },
-      { "pref": "power.battery_screen_dim_delay_ms" },
-      { "pref": "power.battery_screen_off_delay_ms" },
-      { "pref": "power.battery_idle_warning_delay_ms" },
-      { "pref": "power.battery_idle_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PowerManagementIdleSettings": {
+            "AC": {
+              "Delays": {
+                "ScreenDim": 5000,
+                "ScreenOff": 7000,
+                "IdleWarning": 8000,
+                "Idle": 9000
+              },
+              "IdleAction": "Logout"
+            },
+            "Battery": {
+              "Delays": {
+                "ScreenDim": 1000,
+                "ScreenOff": 3000,
+                "IdleWarning": 4000,
+                "Idle": 5000
+              },
+              "IdleAction": "Logout"
+            }
+          }
+        },
+        "prefs": {
+          "power.ac_screen_dim_delay_ms": {},
+          "power.ac_screen_off_delay_ms": {},
+          "power.ac_idle_warning_delay_ms": {},
+          "power.ac_idle_delay_ms": {},
+          "power.battery_screen_dim_delay_ms": {},
+          "power.battery_screen_off_delay_ms": {},
+          "power.battery_idle_warning_delay_ms": {},
+          "power.battery_idle_delay_ms": {}
+        }
+      }
     ]
   },
 
   "ScreenLockDelays": {
     "os": ["chromeos"],
-    "test_policy": { "ScreenLockDelays": { "AC": 6000, "Battery": 2000 } },
-    "pref_mappings": [
-      { "pref": "power.ac_screen_lock_delay_ms" },
-      { "pref": "power.battery_screen_lock_delay_ms" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ScreenLockDelays": {
+            "AC": 6000,
+            "Battery": 2000
+          }
+        },
+        "prefs": {
+          "power.ac_screen_lock_delay_ms": {},
+          "power.battery_screen_lock_delay_ms": {}
+        }
+      }
     ]
   },
 
   "TermsOfServiceURL": {
     "os": ["chromeos"],
-    "test_policy": { "TermsOfServiceURL": "http://www.example.com/terms_of_service.txt" },
-    "pref_mappings": [
-      { "pref": "terms_of_service.url" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"TermsOfServiceURL": "http://www.example.com/terms_of_service.txt" },
+        "prefs": { "terms_of_service.url": {} }
+      }
     ]
   },
 
   "ShowAccessibilityOptionsInSystemTrayMenu": {
     "os": ["chromeos"],
-    "test_policy": { "ShowAccessibilityOptionsInSystemTrayMenu": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.enable_menu",
-        "indicator_tests": [
-          { "policy": { "ShowAccessibilityOptionsInSystemTrayMenu": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ShowAccessibilityOptionsInSystemTrayMenu": true },
+        "prefs": { "settings.a11y.enable_menu": {} }
       }
     ]
   },
 
   "LargeCursorEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "LargeCursorEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.large_cursor_enabled",
-        "indicator_tests": [
-          { "policy": { "LargeCursorEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "LargeCursorEnabled": true },
+        "prefs": { "settings.a11y.large_cursor_enabled": {} }
       }
     ]
   },
 
   "StickyKeysEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "StickyKeysEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.sticky_keys_enabled",
-        "indicator_tests": [
-          { "policy": { "StickyKeysEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "StickyKeysEnabled": true },
+        "prefs": { "settings.a11y.sticky_keys_enabled": {} }
       }
     ]
   },
 
   "SpokenFeedbackEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "SpokenFeedbackEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.accessibility",
-        "indicator_tests": [
-          { "policy": { "SpokenFeedbackEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SpokenFeedbackEnabled": true },
+        "prefs": { "settings.accessibility": {} }
       }
     ]
   },
 
   "DictationEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "DictationEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.dictation",
-        "indicator_tests": [
-          { "policy": { "DictationEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "DictationEnabled": true },
+        "prefs": { "settings.a11y.dictation": {} }
       }
     ]
   },
 
   "KeyboardFocusHighlightEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "KeyboardFocusHighlightEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.focus_highlight",
-        "indicator_tests": [
-          { "policy": { "KeyboardFocusHighlightEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "KeyboardFocusHighlightEnabled": true },
+        "prefs": { "settings.a11y.focus_highlight": {} }
       }
     ]
   },
 
   "CursorHighlightEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "CursorHighlightEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.cursor_highlight",
-        "indicator_tests": [
-          { "policy": { "CursorHighlightEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CursorHighlightEnabled": true },
+        "prefs": { "settings.a11y.cursor_highlight": {} }
       }
     ]
   },
 
   "CaretHighlightEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "CaretHighlightEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.caret_highlight",
-        "indicator_tests": [
-          { "policy": { "CaretHighlightEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CaretHighlightEnabled": true },
+        "prefs": { "settings.a11y.caret_highlight": {} }
       }
     ]
   },
 
   "MonoAudioEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "MonoAudioEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.mono_audio",
-        "indicator_tests": [
-          { "policy": { "MonoAudioEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "MonoAudioEnabled": true },
+        "prefs": { "settings.a11y.mono_audio": {} }
       }
     ]
   },
 
   "AutoclickEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "AutoclickEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.autoclick",
-        "indicator_tests": [
-          { "policy": { "AutoclickEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AutoclickEnabled": true },
+        "prefs": { "settings.a11y.autoclick": {} }
       }
     ]
   },
 
   "HighContrastEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "HighContrastEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.high_contrast_enabled",
-        "indicator_tests": [
-          { "policy": { "HighContrastEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "HighContrastEnabled": true },
+        "prefs": { "settings.a11y.high_contrast_enabled": {} }
       }
     ]
   },
 
   "SelectToSpeakEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "SelectToSpeakEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.select_to_speak",
-        "indicator_tests": [
-          { "policy": { "SelectToSpeakEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SelectToSpeakEnabled": true },
+        "prefs": { "settings.a11y.select_to_speak": {} }
       }
     ]
   },
 
   "ScreenMagnifierType": {
     "os": ["chromeos"],
-    "test_policy": { "ScreenMagnifierType": 1 },
-    "pref_mappings": [
-      { "pref": "settings.a11y.screen_magnifier",
-        "indicator_tests": [
-          { "policy": { "ScreenMagnifierType": 1 } }
-        ]
-      },
-      { "pref": "settings.a11y.screen_magnifier" },
-      { "pref": "ash.docked_magnifier.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ScreenMagnifierType": 1 },
+        "prefs": {
+          "settings.a11y.screen_magnifier": {},
+          "ash.docked_magnifier.enabled": {}
+        }
+      }
     ]
   },
 
   "VirtualKeyboardEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "VirtualKeyboardEnabled": true },
-    "pref_mappings": [
-      { "pref": "settings.a11y.virtual_keyboard",
-        "indicator_tests": [
-          { "policy": { "VirtualKeyboardEnabled": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "VirtualKeyboardEnabled": true },
+        "prefs": { "settings.a11y.virtual_keyboard": {} }
       }
     ]
   },
 
   "KeyboardDefaultToFunctionKeys": {
     "os": ["chromeos"],
-    "test_policy": { "KeyboardDefaultToFunctionKeys": true },
-    "pref_mappings": [
-      { "pref": "settings.language.send_function_keys",
-        "indicator_tests": [
-          { "policy": { "KeyboardDefaultToFunctionKeys": true } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {"KeyboardDefaultToFunctionKeys": true },
+        "prefs": { "settings.language.send_function_keys": {} }
       }
     ]
   },
 
   "AttestationEnabledForUser": {
     "os": ["chromeos"],
-    "test_policy": { "AttestationEnabledForUser": true },
-    "pref_mappings": [
-      { "pref": "attestation.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AttestationEnabledForUser": true },
+        "prefs": { "attestation.enabled": {} }
+      }
     ]
   },
 
   "AttestationExtensionWhitelist": {
     "os": ["chromeos"],
-    "test_policy": { "AttestationExtensionWhitelist": ["test_ext_id1", "test_ext_id2"] },
-    "pref_mappings": [
-      { "pref": "attestation.extension_whitelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AttestationExtensionWhitelist": [
+            "test_ext_id1",
+            "test_ext_id2"
+          ]
+        },
+        "prefs": { "attestation.extension_whitelist": {} }
+      }
     ]
   },
 
-  "ContentPackDefaultFilteringBehavior": {
-  },
+  "ContentPackDefaultFilteringBehavior": {},
 
-  "ContentPackManualBehaviorHosts": {
-  },
+  "ContentPackManualBehaviorHosts": {},
 
-  "ContentPackManualBehaviorURLs": {
-  },
+  "ContentPackManualBehaviorURLs": {},
 
-  "ManagedBookmarks": {
-  },
+  "ManagedBookmarks": {},
 
   "FullscreenAllowed": {
     "os": ["win", "linux", "chromeos"],
-    "test_policy": { "FullscreenAllowed": false },
-    "pref_mappings": [
-      { "pref": "fullscreen.allowed" },
-      { "pref": "apps.fullscreen.allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "FullscreenAllowed": false },
+        "prefs": {
+          "fullscreen.allowed": {},
+          "apps.fullscreen.allowed": {}
+        }
+      }
     ]
   },
 
   "ChromeOsMultiProfileUserBehavior": {
     "os": ["chromeos"],
-    "test_policy": { "ChromeOsMultiProfileUserBehavior": "unrestricted" },
-    "pref_mappings": [
-      { "pref": "settings.multiprofile_user_behavior" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ChromeOsMultiProfileUserBehavior": "unrestricted" },
+        "prefs": { "settings.multiprofile_user_behavior": {} }
+      }
     ]
   },
 
   "SecondaryGoogleAccountSigninAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "SecondaryGoogleAccountSigninAllowed": false },
-    "pref_mappings": [
-      { "pref": "account_consistency_mirror.required" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SecondaryGoogleAccountSigninAllowed": false },
+        "prefs": { "account_consistency_mirror.required": {} }
+      }
     ]
   },
 
   "NativeMessagingBlacklist": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "NativeMessagingBlacklist": ["*"] },
-    "pref_mappings": [
-      { "pref": "native_messaging.blacklist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NativeMessagingBlacklist": ["*"] },
+        "prefs": { "native_messaging.blacklist": {} }
+      }
     ]
   },
 
   "NativeMessagingWhitelist": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "NativeMessagingWhitelist": ["native.messaging.host.name"] },
-    "pref_mappings": [
-      { "pref": "native_messaging.whitelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NativeMessagingWhitelist": ["native.messaging.host.name"]},
+        "prefs": { "native_messaging.whitelist": {} }
+      }
     ]
   },
 
   "NativeMessagingUserLevelHosts": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "NativeMessagingUserLevelHosts": false },
-    "pref_mappings": [
-      { "pref": "native_messaging.user_level_hosts" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "NativeMessagingUserLevelHosts": false },
+        "prefs": { "native_messaging.user_level_hosts": {} }
+      }
     ]
   },
 
   "EnableDeprecatedWebPlatformFeatures": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "EnableDeprecatedWebPlatformFeatures": [
-        "ShowModalDialog_EffectiveUntil20150430"
-      ]
-    },
-    "pref_mappings": [
-      { "pref": "enable_deprecated_web_platform_features" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "EnableDeprecatedWebPlatformFeatures": [
+            "ShowModalDialog_EffectiveUntil20150430"
+          ]
+        },
+        "prefs": { "enable_deprecated_web_platform_features": {} }
+      }
     ]
   },
 
   "TouchVirtualKeyboardEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "TouchVirtualKeyboardEnabled": false },
-    "pref_mappings": [
-      { "pref": "ui.touch_virtual_keyboard_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "TouchVirtualKeyboardEnabled": false },
+        "prefs": { "ui.touch_virtual_keyboard_enabled": {} }
+      }
     ]
   },
 
   "EasyUnlockAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "EasyUnlockAllowed": false },
-    "pref_mappings": [
-      { "pref": "easy_unlock.allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "EasyUnlockAllowed": false },
+        "prefs": { "easy_unlock.allowed": {} }
+      }
     ]
   },
 
@@ -3596,41 +4107,42 @@
 
   "ForceMaximizeOnFirstRun": {
     "os": ["chromeos"],
-    "test_policy": { "ForceMaximizeOnFirstRun": true },
-    "pref_mappings": [
-      { "pref": "ui.force_maximize_on_first_run" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ForceMaximizeOnFirstRun": true },
+        "prefs": { "ui.force_maximize_on_first_run": {} }
+      }
     ]
   },
 
   "SSLErrorOverrideAllowed": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "SSLErrorOverrideAllowed": true },
-    "pref_mappings": [
-      { "pref": "ssl.error_override_allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SSLErrorOverrideAllowed": true },
+        "prefs": { "ssl.error_override_allowed": {} }
+      }
     ]
   },
 
   "AllowDinosaurEasterEgg": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "AllowDinosaurEasterEgg": true },
-    "pref_mappings": [
-      { "pref": "allow_dinosaur_easter_egg"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AllowDinosaurEasterEgg": true },
+        "prefs": { "allow_dinosaur_easter_egg": {} }
+      }
     ]
   },
 
-  "KeyPermissions": {
-  },
+  "KeyPermissions": {},
 
   "HardwareAccelerationModeEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "HardwareAccelerationModeEnabled": true },
-    "pref_mappings": [
-      { "pref": "hardware_acceleration_mode.enabled",
-        "local_state": true,
-        "indicator_tests": [
-          { "policy": { "HardwareAccelerationModeEnabled": true } },
-          { "policy": { "HardwareAccelerationModeEnabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "HardwareAccelerationModeEnabled": true },
+        "prefs": { "hardware_acceleration_mode.enabled": { "local_state": true } }
       }
     ]
   },
@@ -3638,89 +4150,114 @@
   "UnifiedDesktopEnabledByDefault": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "UnifiedDesktopEnabledByDefault": true },
-    "pref_mappings": [
-      { "pref": "settings.display.unified_desktop_enabled_by_default" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "UnifiedDesktopEnabledByDefault": true },
+        "prefs": { "settings.display.unified_desktop_enabled_by_default": {} }
+      }
     ]
   },
 
   "UserFeedbackAllowed": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "UserFeedbackAllowed": false },
-    "pref_mappings": [
-      { "pref": "feedback_allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "UserFeedbackAllowed": false },
+        "prefs": { "feedback_allowed": {} }
+      }
     ]
   },
 
-
   "ArcEnabled": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "ArcEnabled": false },
-    "pref_mappings": [
-      { "pref": "arc.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ArcEnabled": false },
+        "prefs": { "arc.enabled": {} }
+      }
     ]
   },
 
   "ArcPolicy": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "ArcPolicy": "" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ArcPolicy": "" }
+      }
+    ]
   },
 
   "AllowScreenLock": {
     "os": ["chromeos"],
-    "test_policy": { "AllowScreenLock": true },
-    "pref_mappings": [
-      { "pref": "allow_screen_lock"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AllowScreenLock": true },
+        "prefs": { "allow_screen_lock": {} }
+      }
     ]
   },
 
   "QuickUnlockModeWhitelist": {
     "os": ["chromeos"],
-    "test_policy": { "QuickUnlockModeWhitelist": ["PIN"] },
-    "pref_mappings": [
-      { "pref": "quick_unlock_mode_whitelist"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "QuickUnlockModeWhitelist": ["PIN"] },
+        "prefs": { "quick_unlock_mode_whitelist": {} }
+      }
     ]
   },
 
   "QuickUnlockTimeout": {
     "os": ["chromeos"],
-    "test_policy": { "QuickUnlockTimeout": 0 },
-    "pref_mappings": [
-      { "pref": "quick_unlock_timeout"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "QuickUnlockTimeout": 0 },
+        "prefs": { "quick_unlock_timeout": {} }
+      }
     ]
   },
 
   "PinUnlockMinimumLength": {
     "os": ["chromeos"],
-    "test_policy": { "PinUnlockMinimumLength": 4 },
-    "pref_mappings": [
-      { "pref": "pin_unlock_minimum_length"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PinUnlockMinimumLength": 4 },
+        "prefs": { "pin_unlock_minimum_length": {} }
+      }
     ]
   },
 
   "PinUnlockMaximumLength": {
     "os": ["chromeos"],
-    "test_policy": { "PinUnlockMaximumLength": 0 },
-    "pref_mappings": [
-      { "pref": "pin_unlock_maximum_length"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PinUnlockMaximumLength": 0 },
+        "prefs": { "pin_unlock_maximum_length": {} }
+      }
     ]
   },
 
   "PinUnlockWeakPinsAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "PinUnlockWeakPinsAllowed": true },
-    "pref_mappings": [
-      { "pref": "pin_unlock_weak_pins_allowed"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "PinUnlockWeakPinsAllowed": true },
+        "prefs": { "pin_unlock_weak_pins_allowed": {} }
+      }
     ]
   },
 
   "ArcCertificatesSyncMode": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "ArcCertificatesSyncMode": 0 }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ArcCertificatesSyncMode": 0 }
+      }
+    ]
   },
 
   "ArcBackupRestoreEnabled": {
@@ -3734,69 +4271,89 @@
   "ReportArcStatusEnabled": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "ReportArcStatusEnabled": false },
-    "pref_mappings": [
-      { "pref": "arc.status_reporting_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ReportArcStatusEnabled": false },
+        "prefs": { "arc.status_reporting_enabled": {} }
+      }
     ]
   },
 
   "ReportCrostiniUsageEnabled": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "ReportCrostiniUsageEnabled": false },
-    "pref_mappings": [
-      { "pref": "crostini.usage_reporting_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "ReportCrostiniUsageEnabled": false },
+        "prefs": { "crostini.usage_reporting_enabled": {} }
+      }
     ]
   },
 
   "SmsMessagesAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "SmsMessagesAllowed": false },
-    "pref_mappings": [
-      { "pref": "multidevice.sms_connect_allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SmsMessagesAllowed": false },
+        "prefs": { "multidevice.sms_connect_allowed": {} }
+      }
     ]
   },
 
   "SmartLockSigninAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "SmartLockSigninAllowed": false },
-    "pref_mappings": [
-      { "pref": "smart_lock_signin.allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "SmartLockSigninAllowed": false },
+        "prefs": { "smart_lock_signin.allowed": {} }
+      }
     ]
   },
 
   "InstantTetheringAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "InstantTetheringAllowed": false },
-    "pref_mappings": [
-      { "pref": "tether.allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "InstantTetheringAllowed": false
+        },
+        "prefs": { "tether.allowed": {} }
+      }
     ]
   },
 
   "EcryptfsMigrationStrategy": {
     "os": ["chromeos"],
-    "test_policy": { "EcryptfsMigrationStrategy": 1 },
-    "pref_mappings": [
-      { "pref": "ecryptfs_migration_strategy" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "EcryptfsMigrationStrategy": 1
+        },
+        "prefs": { "ecryptfs_migration_strategy": {} }
+      }
     ]
   },
 
   "SchedulerConfiguration": {
     "os": ["chromeos"],
-    "test_policy": { "SchedulerConfiguration": "performance" },
-    "pref_mappings": [
-      { "pref": "chromeos.scheduler_configuration",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SchedulerConfiguration": "performance"
+        },
+        "prefs": { "chromeos.scheduler_configuration": { "local_state": true } }
       }
     ]
   },
 
   "CloudPolicyOverridesPlatformPolicy": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "CloudPolicyOverridesPlatformPolicy": false },
-    "pref_mappings": [
-      { "pref": "policy.cloud_override",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CloudPolicyOverridesPlatformPolicy": false
+        },
+        "prefs": { "policy.cloud_override": { "local_state": true } }
       }
     ]
   },
@@ -3804,182 +4361,270 @@
   "AllowedLanguages": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "AllowedLanguages": ["en-US", "de"] },
-    "pref_mappings": [
-      { "pref": "intl.allowed_languages" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AllowedLanguages": [
+            "en-US",
+            "de"
+          ]
+        },
+        "prefs": { "intl.allowed_languages": {} }
+      }
     ]
   },
 
   "AllowedInputMethods": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "AllowedInputMethods": ["xkb:us::en"] },
-    "pref_mappings": [
-      { "pref": "settings.language.allowed_input_methods" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AllowedInputMethods": [
+            "xkb:us::en"
+          ]
+        },
+        "prefs": { "settings.language.allowed_input_methods": {} }
+      }
     ]
   },
 
   "UrlKeyedAnonymizedDataCollectionEnabled": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "UrlKeyedAnonymizedDataCollectionEnabled": false },
-    "pref_mappings": [
-      { "pref": "url_keyed_anonymized_data_collection.enabled",
-        "indicator_tests": [
-          { "policy": { "url_keyed_anonymized_data_collection.enabled": false } }
-        ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "UrlKeyedAnonymizedDataCollectionEnabled": false
+        },
+        "prefs": { "url_keyed_anonymized_data_collection.enabled": {} }
       }
     ]
   },
 
   "NetworkFileSharesAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "NetworkFileSharesAllowed": false },
-    "pref_mappings": [
-      { "pref": "network_file_shares.allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "NetworkFileSharesAllowed": false
+        },
+        "prefs": { "network_file_shares.allowed": {} }
+      }
     ]
   },
 
   "WebRtcEventLogCollectionAllowed": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "WebRtcEventLogCollectionAllowed": false },
-    "pref_mappings": [
-      { "pref": "webrtc.event_logs_collection" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "WebRtcEventLogCollectionAllowed": false
+        },
+        "prefs": { "webrtc.event_logs_collection": {} }
+      }
     ]
   },
 
   "PowerSmartDimEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "PowerSmartDimEnabled": false },
-    "pref_mappings": [
-      { "pref": "power.smart_dim_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PowerSmartDimEnabled": false
+        },
+        "prefs": { "power.smart_dim_enabled": {} }
+      }
     ]
   },
 
   "CoalesceH2ConnectionsWithClientCertificatesForHosts": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "CoalesceH2ConnectionsWithClientCertificatesForHosts": ["example.com"] },
-    "pref_mappings": [ { "pref": "ssl.client_certs.h2_coalescing_hosts", "local_state": true } ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CoalesceH2ConnectionsWithClientCertificatesForHosts": [
+            "example.com"
+          ]
+        },
+        "prefs": { "ssl.client_certs.h2_coalescing_hosts": { "local_state": true } }
+      }
+    ]
   },
 
   "NetBiosShareDiscoveryEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "NetBiosShareDiscoveryEnabled": true },
-    "pref_mappings": [
-      { "pref": "network_file_shares.netbios_discovery.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "NetBiosShareDiscoveryEnabled": true
+        },
+        "prefs": { "network_file_shares.netbios_discovery.enabled": {} }
+      }
     ]
   },
 
   "EnterpriseHardwarePlatformAPIEnabled": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "EnterpriseHardwarePlatformAPIEnabled": false },
-    "pref_mappings": [
-      { "pref": "enterprise_hardware_platform_api.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "EnterpriseHardwarePlatformAPIEnabled": false
+        },
+        "prefs": { "enterprise_hardware_platform_api.enabled": {} }
+      }
     ]
   },
 
   "NTLMShareAuthenticationEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "NTLMShareAuthenticationEnabled": true },
-    "pref_mappings": [
-      { "pref": "network_file_shares.ntlm_share_authentication.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "NTLMShareAuthenticationEnabled": true
+        },
+        "prefs": { "network_file_shares.ntlm_share_authentication.enabled": {} }
+      }
     ]
   },
 
-  "NetworkFileSharesPreconfiguredShares" : {
+  "NetworkFileSharesPreconfiguredShares": {
     "os": ["chromeos"],
-    "test_policy": {
-      "NetworkFileSharesPreconfiguredShares": [{
-        "share_url": "smb://server/share",
-        "mode": "drop_down"
-      }, {
-        "share_url": "\\\\server\\share",
-        "mode": "drop_down"
-      }, {
-        "share_url": "smb://server/share",
-        "mode": "pre_mount"
-      }, {
-        "share_url": "\\\\server\\share",
-        "mode": "pre_mount"
-      }]
-    },
-    "pref_mappings": [
-      { "pref": "network_file_shares.preconfigured_shares" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "NetworkFileSharesPreconfiguredShares": [
+            {
+              "share_url": "smb://server/share",
+              "mode": "drop_down"
+            },
+            {
+              "share_url": "\\\\server\\share",
+              "mode": "drop_down"
+            },
+            {
+              "share_url": "smb://server/share",
+              "mode": "pre_mount"
+            },
+            {
+              "share_url": "\\\\server\\share",
+              "mode": "pre_mount"
+            }
+          ]
+        },
+        "prefs": { "network_file_shares.preconfigured_shares": {} }
+      }
     ]
   },
 
   "ScreenBrightnessPercent": {
     "os": ["chromeos"],
-    "test_policy": {
-      "ScreenBrightnessPercent": {
-        "BrightnessAC": 100,
-        "BrightnessBattery": 90
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ScreenBrightnessPercent": {
+            "BrightnessAC": 100,
+            "BrightnessBattery": 90
+          }
+        },
+        "prefs": {
+          "power.ac_screen_brightness_percent": {},
+          "power.battery_screen_brightness_percent": {}
+        }
       }
-    },
-    "pref_mappings": [
-      { "pref": "power.ac_screen_brightness_percent" },
-      { "pref": "power.battery_screen_brightness_percent" }
     ]
   },
 
   "PluginVmImage": {
     "os": ["chromeos"],
-    "test_policy": {
-      "PluginVmImage": {
-        "url": "http://localhost/",
-        "hash": "842841a4c75a55ad050d686f4ea5f77e83ae059877fe9b6946aa63d3d057ed32"
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PluginVmImage": {
+            "url": "http://localhost/",
+            "hash": "842841a4c75a55ad050d686f4ea5f77e83ae059877fe9b6946aa63d3d057ed32"
+          }
+        },
+        "prefs": { "plugin_vm.image": {} }
       }
-    },
-    "pref_mappings": [
-      { "pref": "plugin_vm.image" }
     ]
   },
 
   "VoiceInteractionHotwordEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "VoiceInteractionHotwordEnabled": true },
-    "pref_mappings": [{ "pref": "settings.voice_interaction.hotword.enabled" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "VoiceInteractionHotwordEnabled": true
+        },
+        "prefs": { "settings.voice_interaction.hotword.enabled": {} }
+      }
+    ]
   },
 
   "SignedHTTPExchangeEnabled": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "SignedHTTPExchangeEnabled": false },
-    "pref_mappings": [{ "pref": "web_package.signed_exchange.enabled" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SignedHTTPExchangeEnabled": false
+        },
+        "prefs": { "web_package.signed_exchange.enabled": {} }
+      }
+    ]
   },
 
   "SamlInSessionPasswordChangeEnabled": {
     "os": [],
-    "test_policy": { "SamlInSessionPasswordChangeEnabled": true },
-    "pref_mappings": [
-      { "pref": "saml.in_session_password_change_enabled"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SamlInSessionPasswordChangeEnabled": true
+        },
+        "prefs": { "saml.in_session_password_change_enabled": {} }
+      }
     ]
   },
 
   "SamlPasswordExpirationAdvanceWarningDays": {
     "os": [],
-    "test_policy": { "SamlPasswordExpirationAdvanceWarningDays": 7 },
-    "pref_mappings": [
-      { "pref": "saml.password_expiration_advance_warning_days"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SamlPasswordExpirationAdvanceWarningDays": 7
+        },
+        "prefs": { "saml.password_expiration_advance_warning_days": {} }
+      }
     ]
   },
 
   "VmManagementCliAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "VmManagementCliAllowed": false },
-    "pref_mappings": [
-      { "pref": "crostini.vm_management_cli_allowed_by_policy" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "VmManagementCliAllowed": false
+        },
+        "prefs": { "crostini.vm_management_cli_allowed_by_policy": {} }
+      }
     ]
   },
 
-  "SharedClipboardEnabled" : {
+  "SharedClipboardEnabled": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "SharedClipboardEnabled": false },
-    "pref_mappings": [{ "pref": "browser.shared_clipboard_enabled" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SharedClipboardEnabled": false
+        },
+        "prefs": { "browser.shared_clipboard_enabled": {} }
+      }
+    ]
   },
 
   "ClickToCallEnabled" : {
     "os": ["win", "linux", "mac", "chromeos"],
     "test_policy": { "ClickToCallEnabled": false },
-    "pref_mappings": [{ "pref": "browser.click_to_call_enabled" }]
+    "policy_pref_mapping_tests": { "browser.click_to_call_enabled": {} }
   },
 
   "AudioSandboxEnabled": {
@@ -3990,147 +4635,131 @@
 
   "DevicePolicyRefreshRate": {
     "os": ["chromeos"],
-    "test_policy": { "DevicePolicyRefreshRate": 300000 },
-    "pref_mappings": [
-      { "pref": "policy.device_refresh_rate",
-        "local_state": true }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DevicePolicyRefreshRate": 300000
+        },
+        "prefs": { "policy.device_refresh_rate": { "local_state": true } }
+      }
     ]
   },
 
   "DisplayRotationDefault": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "DisplayRotationDefault": 1 }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DisplayRotationDefault": 1
+        }
+      }
+    ]
   },
 
   "DeviceDisplayResolution": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": {
-      "DeviceDisplayResolution": {
-        "external_width": 1920,
-        "external_height": 1080,
-        "external_scale_percentage": 100,
-        "internal_scale_percentage": 50,
-        "recommended": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceDisplayResolution": {
+            "external_width": 1920,
+            "external_height": 1080,
+            "external_scale_percentage": 100,
+            "internal_scale_percentage": 50,
+            "recommended": true
+          }
+        }
       }
-    }
+    ]
   },
 
-  "ChromeOsReleaseChannel": {
-  },
+  "ChromeOsReleaseChannel": {},
 
-  "ChromeOsReleaseChannelDelegated": {
-  },
+  "ChromeOsReleaseChannelDelegated": {},
 
-  "DeviceOpenNetworkConfiguration": {
-  },
+  "DeviceOpenNetworkConfiguration": {},
 
-  "HeartbeatEnabled": {
-  },
+  "HeartbeatEnabled": {},
 
-  "HeartbeatFrequency": {
-  },
+  "HeartbeatFrequency": {},
 
-  "LogUploadEnabled": {
-  },
+  "LogUploadEnabled": {},
 
-  "ReportDeviceVersionInfo": {
-  },
+  "ReportDeviceVersionInfo": {},
 
-  "ReportDeviceActivityTimes": {
-  },
+  "ReportDeviceActivityTimes": {},
 
-  "ReportDeviceBootMode": {
-  },
+  "ReportDeviceBootMode": {},
 
-  "ReportDeviceNetworkInterfaces": {
-  },
+  "ReportDeviceNetworkInterfaces": {},
 
-  "ReportDeviceUsers": {
-  },
+  "ReportDeviceUsers": {},
 
-  "ReportDeviceHardwareStatus": {
-  },
+  "ReportDeviceHardwareStatus": {},
 
-  "ReportDeviceOsUpdateStatus": {
-  },
+  "ReportDeviceOsUpdateStatus": {},
 
-  "ReportDeviceSessionStatus": {
-  },
+  "ReportDeviceSessionStatus": {},
 
-  "ReportDevicePowerStatus": {
-  },
+  "ReportDevicePowerStatus": {},
 
-  "ReportDeviceStorageStatus": {
-  },
+  "ReportDeviceStorageStatus": {},
 
-  "ReportDeviceBoardStatus": {
-  },
+  "ReportDeviceBoardStatus": {},
 
-  "ReportUploadFrequency": {
-  },
+  "ReportUploadFrequency": {},
 
-  "DeviceAllowNewUsers": {
-  },
+  "DeviceAllowNewUsers": {},
 
-  "DeviceUserWhitelist": {
-  },
+  "DeviceUserWhitelist": {},
 
-  "DeviceGuestModeEnabled": {
-  },
+  "DeviceGuestModeEnabled": {},
 
-  "DeviceShowUserNamesOnSignin": {
-  },
+  "DeviceShowUserNamesOnSignin": {},
 
-  "DeviceDataRoamingEnabled": {
-  },
+  "DeviceDataRoamingEnabled": {},
 
   "DeviceMetricsReportingEnabled": {
     "os": ["chromeos"],
     "official_only": true,
-    "test_policy": { "DeviceMetricsReportingEnabled": true },
-    "pref_mappings": [
-      { "pref": "cros.metrics.reportingEnabled",
-        "indicator_test_setup_js": "var controllingPref = 'spellcheck.use_spelling_service'; var testedPref = 'cros.metrics.reportingEnabled'; Preferences.prefsChangedCallback([testedPref, Preferences.getInstance().registeredPreferences_[controllingPref].orig]); Preferences.getInstance().addEventListener(controllingPref, function(event) {Preferences.prefsChangedCallback([testedPref, {value: event.value.value, controlledBy: event.value.controlledBy, disabled: event.value.disabled}]);});",
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceMetricsReportingEnabled": true
+        },
+        "prefs": { "cros.metrics.reportingEnabled": {} },
         "indicator_tests": [
-          { "policy": { "SpellCheckServiceEnabled": true } }
-        ]
+          {
+            "pref": "cros.metrics.reportingEnabled",
+            "test_setup_js": "var controllingPref = 'spellcheck.use_spelling_service'; var testedPref = 'cros.metrics.reportingEnabled'; Preferences.prefsChangedCallback([testedPref, Preferences.getInstance().registeredPreferences_[controllingPref].orig]); Preferences.getInstance().addEventListener(controllingPref, function(event) {Preferences.prefsChangedCallback([testedPref, {value: event.value.value, controlledBy: event.value.controlledBy, disabled: event.value.disabled}]);});"
+          }
+        ],
+        "note": "TODO(bartfab): The |test_setup_js| above is a hack that makes |cros.metrics.reportingEnabled| track the status of the entirely unrelated |spellcheck.use_spelling_service| pref. This is because cros settings cannot currently be made policy-controlled in browser tests. Remove this hack once that restriction is lifted."
       }
-    ],
-
-    "note": "TODO(bartfab): The |indicator_test_setup_js| above is a hack that makes |cros.metrics.reportingEnabled| track the status of the entirely unrelated |spellcheck.use_spelling_service| pref. This is because cros settings cannot currently be made policy-controlled in browser tests. Remove this hack once that restriction is lifted."
+    ]
   },
 
-  "DeviceEphemeralUsersEnabled": {
-  },
+  "DeviceEphemeralUsersEnabled": {},
 
-  "DeviceRebootOnShutdown": {
-  },
+  "DeviceRebootOnShutdown": {},
 
-  "DeviceIdleLogoutTimeout": {
-  },
+  "DeviceIdleLogoutTimeout": {},
 
-  "DeviceIdleLogoutWarningDuration": {
-  },
+  "DeviceIdleLogoutWarningDuration": {},
 
-  "DeviceLoginScreenSaverId": {
-  },
+  "DeviceLoginScreenSaverId": {},
 
-  "DeviceLoginScreenSaverTimeout": {
-  },
+  "DeviceLoginScreenSaverTimeout": {},
 
-  "DeviceStartUpUrls": {
-  },
+  "DeviceStartUpUrls": {},
 
-  "DeviceAppPack": {
-  },
+  "DeviceAppPack": {},
 
-  "DeviceAutoUpdateDisabled": {
-  },
+  "DeviceAutoUpdateDisabled": {},
 
-  "DeviceAutoUpdateP2PEnabled": {
-  },
+  "DeviceAutoUpdateP2PEnabled": {},
 
   "DeviceRollbackAllowedMilestones": {
     "os": ["chromeos"],
@@ -4142,276 +4771,266 @@
     "note": "Chrome OS device policy used by update_engine only, not used in Chrome."
   },
 
-  "DeviceTargetVersionPrefix": {
-  },
+  "DeviceTargetVersionPrefix": {},
 
-  "DeviceUpdateScatterFactor": {
-  },
+  "DeviceUpdateScatterFactor": {},
 
-  "DeviceUpdateAllowedConnectionTypes": {
-  },
+  "DeviceUpdateAllowedConnectionTypes": {},
 
-  "DeviceUpdateHttpDownloadsEnabled": {
-  },
+  "DeviceUpdateHttpDownloadsEnabled": {},
 
-  "ReportDeviceLocation": {
-  },
+  "ReportDeviceLocation": {},
 
-  "SystemTimezone": {
-  },
+  "SystemTimezone": {},
 
-  "SystemUse24HourClock": {
-  },
+  "SystemUse24HourClock": {},
 
-  "DeviceLocalAccounts": {
-  },
+  "DeviceLocalAccounts": {},
 
-  "DeviceLocalAccountAutoLoginId": {
-  },
+  "DeviceLocalAccountAutoLoginId": {},
 
-  "DeviceLocalAccountAutoLoginDelay": {
-  },
+  "DeviceLocalAccountAutoLoginDelay": {},
 
-  "DeviceLocalAccountAutoLoginBailoutEnabled": {
-  },
+  "DeviceLocalAccountAutoLoginBailoutEnabled": {},
 
-  "DeviceLocalAccountManagedSessionEnabled": {
-  },
+  "DeviceLocalAccountManagedSessionEnabled": {},
 
-  "DeviceLocalAccountPromptForNetworkWhenOffline": {
-  },
+  "DeviceLocalAccountPromptForNetworkWhenOffline": {},
 
-  "DeviceBlockDevmode": {
-  },
+  "DeviceBlockDevmode": {},
 
-  "DeviceLoginScreenPowerManagement": {
-  },
+  "DeviceLoginScreenPowerManagement": {},
 
-  "DeviceAllowRedeemChromeOsRegistrationOffers": {
-  },
+  "DeviceAllowRedeemChromeOsRegistrationOffers": {},
 
-  "DeviceStartUpFlags": {
-  },
+  "DeviceStartUpFlags": {},
 
-  "DeviceVariationsRestrictParameter" : {
-  },
+  "DeviceVariationsRestrictParameter": {},
 
-  "DeviceLoginScreenDefaultLargeCursorEnabled" : {
-  },
+  "DeviceLoginScreenDefaultLargeCursorEnabled": {},
 
-  "DeviceLoginScreenLargeCursorEnabled" : {
-  },
+  "DeviceLoginScreenLargeCursorEnabled": {},
 
-  "DeviceLoginScreenShowOptionsInSystemTrayMenu" : {
-  },
+  "DeviceLoginScreenShowOptionsInSystemTrayMenu" : {},
 
-  "DeviceLoginScreenDefaultSpokenFeedbackEnabled" : {
-  },
+  "DeviceLoginScreenDefaultSpokenFeedbackEnabled" : {},
 
-  "DeviceLoginScreenSpokenFeedbackEnabled" : {
-  },
+  "DeviceLoginScreenSpokenFeedbackEnabled": {},
 
-  "DeviceLoginScreenDefaultHighContrastEnabled" : {
-  },
+  "DeviceLoginScreenDefaultHighContrastEnabled": {},
 
-  "DeviceLoginScreenHighContrastEnabled" : {
-  },
+  "DeviceLoginScreenHighContrastEnabled": {},
 
-  "DeviceLoginScreenDefaultScreenMagnifierType" : {
-  },
+  "DeviceLoginScreenDefaultScreenMagnifierType": {},
 
-  "DeviceLoginScreenScreenMagnifierType" : {
-  },
+  "DeviceLoginScreenScreenMagnifierType": {},
 
-  "DeviceLoginScreenDefaultVirtualKeyboardEnabled" : {
-  },
+  "DeviceLoginScreenDefaultVirtualKeyboardEnabled": {},
 
-  "DeviceLoginScreenVirtualKeyboardEnabled" : {
-  },
+  "DeviceLoginScreenVirtualKeyboardEnabled": {},
 
-  "DeviceLoginScreenDictationEnabled" : {
-  },
+  "DeviceLoginScreenDictationEnabled": {},
 
-  "DeviceLoginScreenSelectToSpeakEnabled" : {
-  },
+  "DeviceLoginScreenSelectToSpeakEnabled": {},
 
-  "DeviceLoginScreenCursorHighlightEnabled" : {
-  },
+  "DeviceLoginScreenCursorHighlightEnabled": {},
 
-  "DeviceLoginScreenCaretHighlightEnabled" : {
-  },
+  "DeviceLoginScreenCaretHighlightEnabled": {},
 
-  "DeviceLoginScreenMonoAudioEnabled" : {
-  },
+  "DeviceLoginScreenMonoAudioEnabled": {},
 
-  "DeviceLoginScreenAutoclickEnabled" : {
-  },
+  "DeviceLoginScreenAutoclickEnabled": {},
 
-  "DeviceLoginScreenStickyKeysEnabled" : {
-  },
+  "DeviceLoginScreenStickyKeysEnabled": {},
 
-  "DeviceLoginScreenKeyboardFocusHighlightEnabled" : {
-  },
+  "DeviceLoginScreenKeyboardFocusHighlightEnabled": {},
 
-  "DeviceLoginScreenLocales" : {
-  },
+  "DeviceLoginScreenLocales": {},
 
-  "DeviceLoginScreenInputMethods" : {
-  },
+  "DeviceLoginScreenInputMethods": {},
 
-  "DeviceLoginScreenSystemInfoEnforced" : {
-  },
+  "DeviceLoginScreenSystemInfoEnforced": {},
 
-  "DeviceShowNumericKeyboardForPassword" : {
-  },
+  "DeviceShowNumericKeyboardForPassword": {},
 
-  "UptimeLimit": {
-  },
+  "UptimeLimit": {},
 
-  "RebootAfterUpdate": {
-  },
+  "RebootAfterUpdate": {},
 
-  "AttestationEnabledForDevice": {
-  },
+  "AttestationEnabledForDevice": {},
 
-  "AttestationForContentProtectionEnabled": {
-  },
+  "AttestationForContentProtectionEnabled": {},
 
-  "SupervisedUsersEnabled": {
-  },
+  "SupervisedUsersEnabled": {},
 
-  "SupervisedUserCreationEnabled": {
-  },
+  "SupervisedUserCreationEnabled": {},
 
-  "SupervisedUserContentProviderEnabled": {
-  },
+  "SupervisedUserContentProviderEnabled": {},
 
-  "AutoCleanUpStrategy": {
-  },
+  "AutoCleanUpStrategy": {},
 
-  "DeviceTransferSAMLCookies": {
-  },
+  "DeviceTransferSAMLCookies": {},
 
-  "ExtensionCacheSize": {
-  },
+  "ExtensionCacheSize": {},
 
-  "DeviceLoginScreenDomainAutoComplete": {
-  },
+  "DeviceLoginScreenDomainAutoComplete": {},
 
-  "AllowKioskAppControlChromeVersion": {
-  },
+  "AllowKioskAppControlChromeVersion": {},
 
   "LoginAuthenticationBehavior": {
     "os": ["chromeos"],
-    "test_policy": { "LoginAuthenticationBehavior": 1 },
-    "pref_mappings": [
-      { "pref": "cros.device.login_authentication_behavior" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "LoginAuthenticationBehavior": 1
+        },
+        "prefs": { "cros.device.login_authentication_behavior": {} }
+      }
     ]
   },
 
-  "UsbDetachableWhitelist": {
-  },
+  "UsbDetachableWhitelist": {},
 
-  "DeviceAllowBluetooth": {
-  },
+  "DeviceAllowBluetooth": {},
 
   "DeviceWiFiAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceWiFiAllowed": false},
-    "pref_mappings": [
-      { "pref": "cros.device.device_wifi_allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceWiFiAllowed": false
+        },
+        "prefs": { "cros.device.device_wifi_allowed": {} }
+      }
     ]
   },
 
   "DeviceQuirksDownloadEnabled": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "DeviceQuirksDownloadEnabled": true },
-    "pref_mappings": [
-      { "pref": "cros.device.quirks_download_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceQuirksDownloadEnabled": true
+        },
+        "prefs": { "cros.device.quirks_download_enabled": {} }
+      }
     ]
   },
 
   "SystemTimezoneAutomaticDetection": {
     "os": ["chromeos"],
-    "test_policy": { "SystemTimezoneAutomaticDetection": 1 },
-    "pref_mappings": [
-      { "pref": "cros.device.system_timezone_automatic_detection" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SystemTimezoneAutomaticDetection": 1
+        },
+        "prefs": { "cros.device.system_timezone_automatic_detection": {} }
+      }
     ]
   },
 
-  "WebRestrictionsAuthority": {
-  },
+  "WebRestrictionsAuthority": {},
 
   "TaskManagerEndProcessEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "TaskManagerEndProcessEnabled": false },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "task_manager.end_process_enabled",
-        "local_state": true
+        "policies": {
+          "TaskManagerEndProcessEnabled": false
+        },
+        "prefs": { "task_manager.end_process_enabled": { "local_state": true } }
       }
     ]
   },
 
   "NetworkThrottlingEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "NetworkThrottlingEnabled": {"enabled": "true", "upload_rate_kbits": "5600", "download_rate_kbits": "5600"} },
-    "pref_mappings": [
-      { "pref": "net.throttling_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "NetworkThrottlingEnabled": {
+            "enabled": "true",
+            "upload_rate_kbits": "5600",
+            "download_rate_kbits": "5600"
+          }
+        },
+        "prefs": { "net.throttling_enabled": { "local_state": true } }
       }
     ]
   },
 
   "DeviceWiFiFastTransitionEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceWiFiFastTransitionEnabled": true },
-    "pref_mappings": [
-      { "pref": "net.device_wifi_fast_transition_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceWiFiFastTransitionEnabled": true
+        },
+        "prefs": { "net.device_wifi_fast_transition_enabled": { "local_state": true } }
       }
     ]
   },
 
   "LoginVideoCaptureAllowedUrls": {
     "os": ["chromeos"],
-    "test_policy": { "LoginVideoCaptureAllowedUrls": [ "https://example.com" ] },
-    "pref_mappings": [
-      { "pref": "cros.device.login_video_capture_allowed_urls" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "LoginVideoCaptureAllowedUrls": [
+            "https://example.com"
+          ]
+        },
+        "prefs": { "cros.device.login_video_capture_allowed_urls": {} }
+      }
     ]
   },
 
   "DeviceLoginScreenExtensions": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceLoginScreenExtensions": [ "abcdefghijklmnopabcdefghijklmnop" ] },
-    "pref_mappings": [
-      { "pref": "cros.device.login_screen_extensions" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceLoginScreenExtensions": [
+            "abcdefghijklmnopabcdefghijklmnop"
+          ]
+        },
+        "prefs": { "cros.device.login_screen_extensions": {} }
+      }
     ]
   },
 
   "DeviceWallpaperImage": {
     "os": ["chromeos"],
-    "test_policy": {
-      "DeviceWallpaperImage": {
-        "url": "http://localhost/",
-        "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceWallpaperImage": {
+            "url": "http://localhost/",
+            "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+          }
+        },
+        "prefs": { "cros.device_wallpaper_image": {} }
       }
-    }
+    ]
   },
 
   "BrowserNetworkTimeQueriesEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "BrowserNetworkTimeQueriesEnabled": true },
-    "pref_mappings": [
-      { "pref": "network_time.network_time_queries_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserNetworkTimeQueriesEnabled": true
+        },
+        "prefs": { "network_time.network_time_queries_enabled": { "local_state": true } }
       }
     ]
   },
 
   "DeviceSecondFactorAuthentication": {
-    "os": ["chromeos"]
+    "os": [
+      "chromeos"
+    ]
   },
 
   "DeviceEcryptfsMigrationStrategy": {
@@ -4420,143 +5039,187 @@
 
   "NoteTakingAppsLockScreenWhitelist": {
     "os": ["chromeos"],
-    "test_policy": { "NoteTakingAppsLockScreenWhitelist": [] },
-    "pref_mappings": [
-      {"pref": "settings.note_taking_apps_lock_screen_whitelist"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "NoteTakingAppsLockScreenWhitelist": []
+        },
+        "prefs": { "settings.note_taking_apps_lock_screen_whitelist": {} }
+      }
     ]
   },
 
   "CastReceiverEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "CastReceiverEnabled": true },
-    "pref_mappings": [
-      { "pref": "cast_receiver.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CastReceiverEnabled": true
+        },
+        "prefs": { "cast_receiver.enabled": {} }
+      }
     ]
   },
 
   "CastReceiverName": {
     "os": ["chromeos"],
-    "test_policy": { "CastReceiverName": "Hallway" },
-    "pref_mappings": [
-      { "pref": "cros.device.cast_receiver.name",
-        "local_state": true }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CastReceiverName": "Hallway"
+        },
+        "prefs": { "cros.device.cast_receiver.name": { "local_state": true } }
+      }
     ]
   },
 
   "DeviceNativePrinters": {
     "os": ["chromeos"],
-    "test_policy": {
-      "DeviceNativePrinters": {
-        "url": "https://example.com/policyfile",
-        "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceNativePrinters": {
+            "url": "https://example.com/policyfile",
+            "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+          }
+        }
       }
-    }
+    ]
   },
 
   "DeviceNativePrintersAccessMode": {
     "os": ["chromeos"],
-    "test_policy": {
-      "DeviceNativePrintersAccessMode": 1
-    },
-    "pref_mappings": [
-      { "pref": "cros.device.native_printers_access_mode" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceNativePrintersAccessMode": 1
+        },
+        "prefs": { "cros.device.native_printers_access_mode": {} }
+      }
     ]
   },
 
   "DeviceNativePrintersBlacklist": {
     "os": ["chromeos"],
-    "test_policy": {
-      "DeviceNativePrintersBlacklist": ["id4", "id7", "id10"]
-    },
-    "pref_mappings": [
-      { "pref": "cros.device.native_printers_blacklist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceNativePrintersBlacklist": ["id4", "id7", "id10"]
+        },
+        "prefs": { "cros.device.native_printers_blacklist": {} }
+      }
     ]
   },
+
   "DeviceNativePrintersWhitelist": {
     "os": ["chromeos"],
-    "test_policy": {
-      "DeviceNativePrintersWhitelist":  ["id4", "id7", "id10"]
-    },
-    "pref_mappings": [
-      { "pref": "cros.device.native_printers_whitelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceNativePrintersWhitelist": ["id4", "id7", "id10"]
+        },
+        "prefs": { "cros.device.native_printers_whitelist": {} }
+      }
     ]
   },
 
   "TPMFirmwareUpdateSettings": {
     "os": ["chromeos"],
-    "test_policy": {
-      "allow-user-initiated-powerwash": true,
-      "auto-update-mode": 2
-    },
-    "pref_mappings": [
-      { "pref": "cros.tpm_firmware_update_settings" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "allow-user-initiated-powerwash": true,
+          "auto-update-mode": 2
+        },
+        "prefs": { "cros.tpm_firmware_update_settings": {} }
+      }
     ]
   },
 
-
   "MinimumRequiredChromeVersion": {
     "os": ["chromeos"],
-    "test_policy": {
-      "MinimumRequiredChromeVersion": "61.0.3163.120"
-    },
-    "pref_mappings": [
-      { "pref": "cros.min_version.chrome"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "MinimumRequiredChromeVersion": "61.0.3163.120"
+        },
+        "prefs": { "cros.min_version.chrome": {} }
+      }
     ]
   },
 
-  "DeviceLoginScreenAutoSelectCertificateForUrls": {
-  },
+  "DeviceLoginScreenAutoSelectCertificateForUrls": {},
 
   "UnaffiliatedArcAllowed": {
     "os": ["chromeos"],
-    "test_policy": {
-      "unaffiliated_arc_allowed": false
-    },
-    "pref_mappings": [
-      { "pref": "cros.unaffiliated_arc_allowed"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "unaffiliated_arc_allowed": false
+        },
+        "prefs": { "cros.unaffiliated_arc_allowed": {} }
+      }
     ]
   },
 
   "DeviceHostnameTemplate": {
     "os": ["chromeos"],
-    "test_policy": {
-      "DeviceHostnameTemplate": "chromebook-${ASSET_ID}"
-    },
-    "pref_mappings": [
-      { "pref": "cros.network.hostname_template" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceHostnameTemplate": "chromebook-${ASSET_ID}"
+        },
+        "prefs": { "cros.network.hostname_template": {} }
+      }
     ]
   },
 
   "DeviceQuickFixBuildToken": {
     "os": ["chromeos"],
-    "test_policy": {
-      "DeviceQuickFixBuildToken": "sometoken"
-    }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceQuickFixBuildToken": "sometoken"
+        }
+      }
+    ]
   },
 
   "AbusiveExperienceInterventionEnforce": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "AbusiveExperienceInterventionEnforce": false
-    },
-    "pref_mappings": [
-      { "pref": "abusive_experience_intervention_enforce"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AbusiveExperienceInterventionEnforce": false
+        },
+        "prefs": { "abusive_experience_intervention_enforce": {} }
+      }
     ]
   },
 
   "AdsSettingForIntrusiveAdsSites": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "AdsSettingForIntrusiveAdsSites": 1
-    },
-    "pref_mappings": [
-      { "pref": "profile.managed_default_content_settings.ads",
-        "indicator_selector": "[content-setting=ads]",
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "AdsSettingForIntrusiveAdsSites": 1 },
+        "prefs": { "profile.managed_default_content_settings.ads": { "value": 1 }},
         "indicator_tests": [
-          { "policy": { "DefaultAdsSetting": 1 },
-            "value": "allow"},
-          { "policy": { "DefaultAdsSetting": 2 },
-            "value": "block"}
+          {
+            "pref": "profile.managed_default_content_settings.ads",
+            "selector": "[content-setting=ads]",
+            "value": "allow"
+          }
+        ]
+      },
+      {
+        "policies": { "AdsSettingForIntrusiveAdsSites": 2 },
+        "prefs": { "profile.managed_default_content_settings.ads":{ "value": 2 }},
+        "indicator_tests": [
+          {
+            "pref": "profile.managed_default_content_settings.ads",
+            "selector": "[content-setting=ads]",
+            "value": "block"
+          }
         ]
       }
     ]
@@ -4564,22 +5227,47 @@
 
   "SafeBrowsingExtendedReportingEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "SafeBrowsingExtendedReportingEnabled": true },
-    "pref_mappings": [
-      { "pref": "safebrowsing.scout_reporting_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SafeBrowsingExtendedReportingEnabled": true
+        },
+        "prefs": { "safebrowsing.scout_reporting_enabled": {} }
+      }
     ]
   },
 
+  "RestrictAccountsToPatterns": {},
 
-  "RestrictAccountsToPatterns": {
-  },
-
-  "PasswordProtectionWarningTrigger": {
-  },
+  "PasswordProtectionWarningTrigger": {},
 
   "DeviceOffHours": {
     "os": ["chromeos"],
-    "test_policy": {"DeviceOffHours": {"intervals": [{"start" : {"day_of_week" : 1, "time": 12840000}, "end": {"day_of_week" : 1, "time": 21720000}}], "timezone" : "GMT", "ignored_policy_proto_tags": [8, 3]}}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceOffHours": {
+            "intervals": [
+              {
+                "start": {
+                  "day_of_week": 1,
+                  "time": 12840000
+                },
+                "end": {
+                  "day_of_week": 1,
+                  "time": 21720000
+                }
+              }
+            ],
+            "timezone": "GMT",
+            "ignored_policy_proto_tags": [
+              8,
+              3
+            ]
+          }
+        }
+      }
+    ]
   },
 
   "DeviceKerberosEncryptionTypes": {
@@ -4618,110 +5306,150 @@
 
   "VirtualMachinesAllowed": {
     "os": ["chromeos"],
-    "test_policy": {
-      "virtual_machines_allowed": true
-    },
-    "pref_mappings": [
-      { "pref": "cros.device.virtual_machines_allowed"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "virtual_machines_allowed": true
+        },
+        "prefs": { "cros.device.virtual_machines_allowed": {} }
+      }
     ]
   },
 
   "CrostiniAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "CrostiniAllowed": false },
-    "pref_mappings": [
-      { "pref": "crostini.user_allowed_by_policy" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CrostiniAllowed": false
+        },
+        "prefs": { "crostini.user_allowed_by_policy": {} }
+      }
     ]
   },
 
   "DeviceUnaffiliatedCrostiniAllowed": {
     "os": ["chromeos"],
-    "test_policy": {
-      "device_unaffiliated_crostini_allowed": false
-    },
-    "pref_mappings": [
-      { "pref": "cros.device.unaffiliated_crostini_allowed"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "device_unaffiliated_crostini_allowed": false
+        },
+        "prefs": { "cros.device.unaffiliated_crostini_allowed": {} }
+      }
     ]
   },
 
   "CrostiniExportImportUIAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "CrostiniExportImportUIAllowed": false },
-    "pref_mappings": [
-      { "pref": "crostini.user_export_import_ui_allowed_by_policy" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CrostiniExportImportUIAllowed": false
+        },
+        "prefs": { "crostini.user_export_import_ui_allowed_by_policy": {} }
+      }
     ]
   },
 
   "CrostiniRootAccessAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "CrostiniRootAccessAllowed": false },
-    "pref_mappings": [
-      { "pref": "crostini.user_root_access_allowed_by_policy" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CrostiniRootAccessAllowed": false
+        },
+        "prefs": { "crostini.user_root_access_allowed_by_policy": {} }
+      }
     ]
   },
 
   "CrostiniAnsiblePlaybook": {
     "os": ["chromeos"],
-    "test_policy": {
-       "CrostiniAnsiblePlaybook": {
-         "url": "https://example.com/ansibleplaybook",
-         "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
-       }
+    "policy_pref_mapping_test": {
+      "policies": {
+        "CrostiniAnsiblePlaybook": {
+          "url": "https://example.com/ansibleplaybook",
+          "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+        }
+      }
     }
   },
 
-  "DeviceSamlLoginAuthenticationType": {
-  },
+  "DeviceSamlLoginAuthenticationType": {},
 
   "DeviceScheduledUpdateCheck": {
-     "os": ["chromeos"],
-     "test_policy": { "DeviceScheduledUpdateCheck": {"update_check_time" : {"hour": 23, "minute": 35}, "frequency": "WEEKLY", "day_of_week": "MONDAY" }}
-   },
+    "os": ["chromeos"],
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceScheduledUpdateCheck": {
+            "update_check_time": {
+              "hour": 23,
+              "minute": 35
+            },
+            "frequency": "WEEKLY",
+            "day_of_week": "MONDAY"
+          }
+        }
+      }
+    ]
+  },
 
   "ChromeCleanupEnabled": {
     "os": ["win"],
-    "test_policy": { "ChromeCleanupEnabled": true },
-    "pref_mappings": [
-      { "pref": "software_reporter.enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ChromeCleanupEnabled": true
+        },
+        "prefs": { "software_reporter.enabled": { "local_state": true } }
       }
     ]
   },
 
   "ChromeCleanupReportingEnabled": {
     "os": ["win"],
-    "test_policy": { "ChromeCleanupReportingEnabled": false },
-    "pref_mappings": [
-      { "pref": "software_reporter.reporting" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ChromeCleanupReportingEnabled": false
+        },
+        "prefs": { "software_reporter.reporting": {} }
+      }
     ]
   },
 
-  "SafeBrowsingWhitelistDomains": {
-  },
+  "SafeBrowsingWhitelistDomains": {},
 
-  "PasswordProtectionLoginURLs": {
-  },
+  "PasswordProtectionLoginURLs": {},
 
-  "PasswordProtectionChangePasswordURL": {
-  },
+  "PasswordProtectionChangePasswordURL": {},
 
   "AutoplayAllowed": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "AutoplayAllowed": false },
-    "pref_mappings": [
-      { "pref": "media.autoplay_allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AutoplayAllowed": false
+        },
+        "prefs": { "media.autoplay_allowed": {} }
+      }
     ]
   },
 
   "AutoplayWhitelist": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "AutoplayWhitelist": [
-        "https://mydomain.com", "https://test.mydomain.com"
-      ]
-    },
-    "pref_mappings": [
-      { "pref": "media.autoplay_whitelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AutoplayWhitelist": [
+            "https://mydomain.com",
+            "https://test.mydomain.com"
+          ]
+        },
+        "prefs": { "media.autoplay_whitelist": {} }
+      }
     ]
   },
 
@@ -4731,23 +5459,91 @@
 
   "ArcAppInstallEventLoggingEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "ArcAppInstallEventLoggingEnabled": true },
-    "pref_mappings": [
-      { "pref": "arc.app_install_event_logging_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ArcAppInstallEventLoggingEnabled": true
+        },
+        "prefs": { "arc.app_install_event_logging_enabled": {} }
+      }
     ]
   },
 
   "UsageTimeLimit": {
     "os": ["chromeos"],
-    "test_policy": { "UsageTimeLimit": { "time_window_limit": { "entries": [{ "effective_day": "WEDNESDAY", "starts_at": { "hour": 21, "minute": 0 }, "ends_at": { "hour": 7, "minute": 30 }, "last_updated_millis": "1000000" }, { "effective_day": "SATURDAY", "starts_at": { "hour": 14, "minute": 0 }, "ends_at": { "hour": 16, "minute": 15 }, "last_updated_millis": "1400000" }]}, "time_usage_limit": { "monday": { "usage_quota_mins": 120, "last_updated_millis": "1200000" }, "thursday": { "usage_quota_mins": 15, "last_updated_millis": "1600000" }, "reset_at": { "hour": 6, "minute": 0 }}, "overrides": [{ "action": "UNLOCK", "created_at_millis": "1250000", "action_specific_data": { "duration_mins": 30 }}]}},
-    "can_be_recommended": false,
-    "pref_mappings": [{ "pref": "screen_time.limit" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "UsageTimeLimit": {
+            "time_window_limit": {
+              "entries": [
+                {
+                  "effective_day": "WEDNESDAY",
+                  "starts_at": {
+                    "hour": 21,
+                    "minute": 0
+                  },
+                  "ends_at": {
+                    "hour": 7,
+                    "minute": 30
+                  },
+                  "last_updated_millis": "1000000"
+                },
+                {
+                  "effective_day": "SATURDAY",
+                  "starts_at": {
+                    "hour": 14,
+                    "minute": 0
+                  },
+                  "ends_at": {
+                    "hour": 16,
+                    "minute": 15
+                  },
+                  "last_updated_millis": "1400000"
+                }
+              ]
+            },
+            "time_usage_limit": {
+              "monday": {
+                "usage_quota_mins": 120,
+                "last_updated_millis": "1200000"
+              },
+              "thursday": {
+                "usage_quota_mins": 15,
+                "last_updated_millis": "1600000"
+              },
+              "reset_at": {
+                "hour": 6,
+                "minute": 0
+              }
+            },
+            "overrides": [
+              {
+                "action": "UNLOCK",
+                "created_at_millis": "1250000",
+                "action_specific_data": {
+                  "duration_mins": 30
+                }
+              }
+            ]
+          }
+        },
+        "can_be_recommended": false,
+        "prefs": { "screen_time.limit": {} }
+      }
+    ]
   },
 
   "EnableSyncConsent": {
     "os": ["chromeos"],
-    "test_policy": { "EnableSyncConsent": false },
-    "pref_mappings": [{ "pref": "sync_consent.enabled" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "EnableSyncConsent": false
+        },
+        "prefs": { "sync_consent.enabled": {} }
+      }
+    ]
   },
 
   "ContextualSuggestionsEnabled": {
@@ -4756,7 +5552,38 @@
 
   "DeviceAutoUpdateTimeRestrictions": {
     "os": ["chromeos"],
-    "test_policy": {"UpdateTimeRestrictions": [{"start" : {"day_of_week" : "Wednesday", "minutes": 30, "hours": 3}, "end": {"day_of_week" : "Thursday", "minutes": 20, "hours": 13}}, {"start": {"day_of_week": "Monday", "minutes": 45, "hours": 1}, "end": {"day_of_week": "Monday", "minutes": 50, "hours": 15}}]}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "UpdateTimeRestrictions": [
+            {
+              "start": {
+                "day_of_week": "Wednesday",
+                "minutes": 30,
+                "hours": 3
+              },
+              "end": {
+                "day_of_week": "Thursday",
+                "minutes": 20,
+                "hours": 13
+              }
+            },
+            {
+              "start": {
+                "day_of_week": "Monday",
+                "minutes": 45,
+                "hours": 1
+              },
+              "end": {
+                "day_of_week": "Monday",
+                "minutes": 50,
+                "hours": 15
+              }
+            }
+          ]
+        }
+      }
+    ]
   },
 
   "DeviceUpdateStagingSchedule": {
@@ -4766,90 +5593,131 @@
 
   "WebAppInstallForceList": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "WebAppInstallForceList": [{
-        "url": "https://foo.example",
-        "default_launch_container": "tab"
-      }, {
-        "url": "https://bar.example",
-        "default_launch_container": "window"
-      }, {
-        "url": "https://foobar.example"
-      }, {
-        "url": "https://xyz.example",
-        "create_desktop_shortcut": true
-      }]
-    },
-    "pref_mappings": [
-      { "pref": "profile.web_app.install.forcelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "WebAppInstallForceList": [
+            {
+              "url": "https://foo.example",
+              "default_launch_container": "tab"
+            },
+            {
+              "url": "https://bar.example",
+              "default_launch_container": "window"
+            },
+            {
+              "url": "https://foobar.example"
+            },
+            {
+              "url": "https://xyz.example",
+              "create_desktop_shortcut": true
+            }
+          ]
+        },
+        "prefs": { "profile.web_app.install.forcelist": {} }
+      }
     ]
   },
 
   "PluginVmAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "PluginVmAllowed": true },
-    "pref_mappings": [
-      { "pref": "cros.device.plugin_vm_allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PluginVmAllowed": true
+        },
+        "prefs": { "cros.device.plugin_vm_allowed": {} }
+      }
     ]
   },
 
   "PluginVmLicenseKey": {
     "os": ["chromeos"],
-    "test_policy": {
-      "PluginVmLicenseKey": "LICENSE_KEY"
-    },
-    "pref_mappings": [
-      { "pref": "cros.device.plugin_vm_license_key" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PluginVmLicenseKey": "LICENSE_KEY"
+        },
+        "prefs": { "cros.device.plugin_vm_license_key": {} }
+      }
     ]
   },
 
   "ParentAccessCodeConfig": {
     "os": ["chromeos"],
-    "test_policy": { "ParentAccessCodeConfig": {
-      "current_config": {
-        "shared_secret": "oOA9nX02LdhYdOzwMsGof+QA3wUKP4YMNlk9S/W3o+w=",
-        "access_code_ttl": 600,
-        "clock_drift_tolerance": 300
-      },
-      "future_config": {
-        "shared_secret": "KMsoIjnpvcWmiU1GHchp2blR96mNyJwS",
-        "access_code_ttl": 600,
-        "clock_drift_tolerance": 300
-      },
-      "old_configs": [{
-        "shared_secret": "sTr6jqMTJGCbLhWI5plFTQb/VsqxwX2Q",
-        "access_code_ttl": 600,
-        "clock_drift_tolerance": 300
-      }]
-    }},
-    "pref_mappings": [{ "pref": "child_user.parent_access_code.config" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ParentAccessCodeConfig": {
+            "current_config": {
+              "shared_secret": "oOA9nX02LdhYdOzwMsGof+QA3wUKP4YMNlk9S/W3o+w=",
+              "access_code_ttl": 600,
+              "clock_drift_tolerance": 300
+            },
+            "future_config": {
+              "shared_secret": "KMsoIjnpvcWmiU1GHchp2blR96mNyJwS",
+              "access_code_ttl": 600,
+              "clock_drift_tolerance": 300
+            },
+            "old_configs": [
+              {
+                "shared_secret": "sTr6jqMTJGCbLhWI5plFTQb/VsqxwX2Q",
+                "access_code_ttl": 600,
+                "clock_drift_tolerance": 300
+              }
+            ]
+          }
+        },
+        "prefs": { "child_user.parent_access_code.config": {} }
+      }
+    ]
   },
 
   "ClientCertificateManagementAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "ClientCertificateManagementAllowed": 2 },
-    "pref_mappings": [{ "pref": "client_certificate_management_allowed" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ClientCertificateManagementAllowed": 2
+        },
+        "prefs": { "client_certificate_management_allowed": {} }
+      }
+    ]
   },
 
   "CACertificateManagementAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "CACertificateManagementAllowed": 2 },
-    "pref_mappings": [{ "pref": "ca_certificate_management_allowed" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CACertificateManagementAllowed": 2
+        },
+        "prefs": { "ca_certificate_management_allowed": {} }
+      }
+    ]
   },
 
   "VoiceInteractionContextEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "VoiceInteractionContextEnabled": true },
-    "pref_mappings": [{ "pref": "settings.voice_interaction.context.enabled" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "VoiceInteractionContextEnabled": true
+        },
+        "prefs": { "settings.voice_interaction.context.enabled": {} }
+      }
+    ]
   },
 
   "DeviceRebootOnUserSignout": {
     "os": ["chromeos"],
-    "test_policy": {
-      "DeviceRebootOnUserSignout": 0
-    },
-    "pref_mappings": [
-      { "pref": "cros.device.reboot_on_user_signout"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceRebootOnUserSignout": 0
+        },
+        "prefs": { "cros.device.reboot_on_user_signout": {} }
+      }
     ]
   },
 
@@ -4859,635 +5727,790 @@
 
   "DeviceWilcoDtcAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceWilcoDtcAllowed": true },
-    "pref_mappings": [{ "pref": "cros.device.wilco_dtc_allowed" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceWilcoDtcAllowed": true
+        },
+        "prefs": { "cros.device.wilco_dtc_allowed": {} }
+      }
+    ]
   },
 
   "DeviceWilcoDtcConfiguration": {
     "os": ["chromeos"],
-    "test_policy": {
-       "DeviceWilcoDtcCOnfiguration": {
-         "url": "https://example.com/wilcodtcconfiguration",
-         "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
-       }
-    }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceWilcoDtcCOnfiguration": {
+            "url": "https://example.com/wilcodtcconfiguration",
+            "hash": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+          }
+        }
+      }
+    ]
   },
 
   "DevicePowerPeakShiftEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "DevicePowerPeakShiftEnabled": true },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "ash.power.peak_shift_enabled",
-        "local_state": true
+        "policies": {
+          "DevicePowerPeakShiftEnabled": true
+        },
+        "prefs": { "ash.power.peak_shift_enabled": { "local_state": true } }
       }
     ]
   },
 
   "DevicePowerPeakShiftBatteryThreshold": {
     "os": ["chromeos"],
-    "test_policy": { "DevicePowerPeakShiftBatteryThreshold": 20 },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "ash.power.peak_shift_battery_threshold",
-        "local_state": true
+        "policies": {
+          "DevicePowerPeakShiftBatteryThreshold": 20
+        },
+        "prefs": { "ash.power.peak_shift_battery_threshold": { "local_state": true } }
       }
     ]
   },
 
   "DevicePowerPeakShiftDayConfig": {
     "os": ["chromeos"],
-    "test_policy": {
-      "DevicePowerPeakShiftDayConfig": {
-        "entries": [
-           {
-              "charge_start_time": {
-                 "hour": 20,
-                 "minute": 0
-              },
-              "day": "MONDAY",
-              "end_time": {
-                 "hour": 10,
-                 "minute": 15
-              },
-              "start_time": {
-                 "hour": 7,
-                 "minute": 30
-              }
-           },
-           {
-              "charge_start_time": {
-                 "hour": 22,
-                 "minute": 30
-              },
-              "day": "FRIDAY",
-              "end_time": {
-                 "hour": 9,
-                 "minute": 45
-              },
-              "start_time": {
-                 "hour": 4,
-                 "minute": 0
-              }
-           }
-        ]
-      }
-    },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "ash.power.peak_shift_day_config",
-        "local_state": true
+        "policies": {
+          "DevicePowerPeakShiftDayConfig": {
+            "entries": [
+              {
+                "charge_start_time": {
+                  "hour": 20,
+                  "minute": 0
+                },
+                "day": "MONDAY",
+                "end_time": {
+                  "hour": 10,
+                  "minute": 15
+                },
+                "start_time": {
+                  "hour": 7,
+                  "minute": 30
+                }
+              },
+              {
+                "charge_start_time": {
+                  "hour": 22,
+                  "minute": 30
+                },
+                "day": "FRIDAY",
+                "end_time": {
+                  "hour": 9,
+                  "minute": 45
+                },
+                "start_time": {
+                  "hour": 4,
+                  "minute": 0
+                }
+              }
+            ]
+          }
+        },
+        "prefs": { "ash.power.peak_shift_day_config": { "local_state": true } }
       }
     ]
   },
 
   "DeviceBootOnAcEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceBootOnAcEnabled": true },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "ash.power.boot_on_ac_enabled",
-        "local_state": true
+        "policies": {
+          "DeviceBootOnAcEnabled": true
+        },
+        "prefs": { "ash.power.boot_on_ac_enabled": { "local_state": true } }
       }
     ]
   },
 
   "DeviceDockMacAddressSource": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceDockMacAddressSource": 2},
-    "pref_mappings": [
-      { "pref": "cros.device.device_dock_mac_address_source" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DeviceDockMacAddressSource": 2
+        },
+        "prefs": { "cros.device.device_dock_mac_address_source": {} }
+      }
     ]
   },
 
   "DeviceAdvancedBatteryChargeModeEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceAdvancedBatteryChargeModeEnabled": true },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "ash.power.advanced_battery_charge_mode_enabled",
-        "local_state": true
+        "policies": {
+          "DeviceAdvancedBatteryChargeModeEnabled": true
+        },
+        "prefs": { "ash.power.advanced_battery_charge_mode_enabled": { "local_state": true } }
       }
     ]
   },
 
   "DeviceAdvancedBatteryChargeModeDayConfig": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceAdvancedBatteryChargeModeDayConfig":
+    "policy_pref_mapping_test": [
       {
-        "entries": [
-           {
-              "charge_start_time": {
-                 "hour": 10,
-                 "minute": 0
+        "policies": {
+          "DeviceAdvancedBatteryChargeModeDayConfig": {
+            "entries": [
+              {
+                "charge_start_time": {
+                  "hour": 10,
+                  "minute": 0
+                },
+                "day": "MONDAY",
+                "charge_end_time": {
+                  "hour": 20,
+                  "minute": 15
+                }
               },
-              "day": "MONDAY",
-              "charge_end_time": {
-                 "hour": 20,
-                 "minute": 15
+              {
+                "charge_start_time": {
+                  "hour": 8,
+                  "minute": 30
+                },
+                "day": "FRIDAY",
+                "charge_end_time": {
+                  "hour": 15,
+                  "minute": 45
+                }
               }
-           },
-           {
-              "charge_start_time": {
-                 "hour": 8,
-                 "minute": 30
-              },
-              "day": "FRIDAY",
-              "charge_end_time": {
-                 "hour": 15,
-                 "minute": 45
-              }
-           }
-        ]
-      }
-    },
-    "pref_mappings": [
-      {
-        "pref": "ash.power.advanced_battery_charge_mode_day_config",
-        "local_state": true
+            ]
+          }
+        },
+        "prefs": { "ash.power.advanced_battery_charge_mode_day_config": { "local_state": true } }
       }
     ]
   },
 
   "DeviceBatteryChargeMode": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceBatteryChargeMode": 5 },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "ash.power.battery_charge_mode",
-        "local_state": true
+        "policies": {
+          "DeviceBatteryChargeMode": 5
+        },
+        "prefs": { "ash.power.battery_charge_mode": { "local_state": true } }
       }
     ]
   },
 
   "DeviceBatteryChargeCustomStartCharging": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceBatteryChargeCustomStartCharging": 60 },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "ash.power.battery_charge_custom_start_charging",
-        "local_state": true
+        "policies": {
+          "DeviceBatteryChargeCustomStartCharging": 60
+        },
+        "prefs": { "ash.power.battery_charge_custom_start_charging": { "local_state": true } }
       }
     ]
   },
 
   "DeviceBatteryChargeCustomStopCharging": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceBatteryChargeCustomStopCharging": 91 },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "ash.power.battery_charge_custom_stop_charging",
-        "local_state": true
+        "policies": {
+          "DeviceBatteryChargeCustomStopCharging": 91
+        },
+        "prefs": { "ash.power.battery_charge_custom_stop_charging": { "local_state": true } }
       }
     ]
   },
 
   "DeviceUsbPowerShareEnabled": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceUsbPowerShareEnabled": true },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "ash.power.usb_power_share_enabled",
-        "local_state": true
+        "policies": {
+          "DeviceUsbPowerShareEnabled": true
+        },
+        "prefs": { "ash.power.usb_power_share_enabled": { "local_state": true } }
       }
     ]
   },
 
   "DevicePowerwashAllowed": {
     "os": ["chromeos"],
-    "test_policy": { "DevicePowerwashAllowed": true },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "cros.device.device_powerwash_allowed",
-        "local_state": true
+        "policies": {
+          "DevicePowerwashAllowed": true
+        },
+        "prefs": { "cros.device.device_powerwash_allowed": { "local_state": true } }
       }
     ]
   },
 
   "DeviceWebBasedAttestationAllowedUrls": {
     "os": ["chromeos"],
-    "test_policy": { "DeviceWebBasedAttestationAllowedUrls": ["[*.]example.com"] },
-    "pref_mappings": [
+    "policy_pref_mapping_test": [
       {
-        "pref": "cros.device.web_based_attestation_allowed_urls"
+        "policies": {
+          "DeviceWebBasedAttestationAllowedUrls": [
+            "[*.]example.com"
+          ]
+        },
+        "prefs": { "cros.device.web_based_attestation_allowed_urls": {} }
       }
     ]
   },
 
   "PerAppTimeLimits": {
     "os": ["chromeos"],
-    "test_policy": { "PerAppTimeLimits": {
-      "app_limits": [
-        {
-          "app_info": {
-            "app_id": "com.example.myapp",
-            "app_type": "ARC"
-          },
-          "restriction": "TIME_LIMIT",
-          "daily_limit_mins": 30,
-          "last_updated_millis": "1570223060437"
-        },
-        {
-          "app_info": {
-            "app_id": "pjkljhegncpnkpknbcohdijeoejaedia",
-            "app_type": "EXTENSION"
-          },
-          "restriction": "TIME_LIMIT",
-          "daily_limit_mins": 10,
-          "last_updated_millis": "1570223000000"
-        },
-        {
-          "app_info": {
-            "app_id": "iniodglblcgmngkgdipeiclkdjjpnlbn",
-            "app_type": "BUILT-IN"
-          },
-          "restriction": "BLOCK",
-          "last_updated_millis": "1570223000000"
-        }
-      ],
-      "reset_at": {
-        "hour": 6,
-        "minute": 0
-      }
-    }
-    },
     "can_be_recommended": false,
-    "pref_mappings": [{ "pref": "child_user.per_app_time_limits.policy" }]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "PerAppTimeLimits": {
+            "app_limits": [
+              {
+                "app_info": {
+                  "app_id": "com.example.myapp",
+                  "app_type": "ARC"
+                },
+                "restriction": "TIME_LIMIT",
+                "daily_limit_mins": 30,
+                "last_updated_millis": "1570223060437"
+              },
+              {
+                "app_info": {
+                  "app_id": "pjkljhegncpnkpknbcohdijeoejaedia",
+                  "app_type": "EXTENSION"
+                },
+                "restriction": "TIME_LIMIT",
+                "daily_limit_mins": 10,
+                "last_updated_millis": "1570223000000"
+              },
+              {
+                "app_info": {
+                  "app_id": "iniodglblcgmngkgdipeiclkdjjpnlbn",
+                  "app_type": "BUILT-IN"
+                },
+                "restriction": "BLOCK",
+                "last_updated_millis": "1570223000000"
+              }
+            ],
+            "reset_at": {
+              "hour": 6,
+              "minute": 0
+            }
+          }
+        },
+        "prefs": { "child_user.per_app_time_limits.policy": {} }
+      }
+    ]
   },
 
   "----- Chrome Frame policies -------------------------------------------": {},
 
-  "ChromeFrameRendererSettings": {
-  },
+  "ChromeFrameRendererSettings": {},
 
-  "RenderInChromeFrameList": {
-  },
+  "RenderInChromeFrameList": {},
 
-  "RenderInHostList": {
-  },
+  "RenderInHostList": {},
 
-  "ChromeFrameContentTypes": {
-  },
+  "ChromeFrameContentTypes": {},
 
-  "GCFUserDataDir": {
-  },
+  "GCFUserDataDir": {},
 
-  "AdditionalLaunchParameters": {
-  },
+  "AdditionalLaunchParameters": {},
 
-  "SuppressChromeFrameTurndownPrompt": {
-  },
+  "SuppressChromeFrameTurndownPrompt": {},
 
-  "SkipMetadataCheck": {
-  },
+  "SkipMetadataCheck": {},
 
   "ArcBackupRestoreServiceEnabled": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "ArcBackupRestoreServiceEnabled": 0 },
-    "pref_mappings": [
-      { "pref": "arc.backup_restore.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ArcBackupRestoreServiceEnabled": 0
+        },
+        "prefs": { "arc.backup_restore.enabled": {} }
+      }
     ]
   },
 
   "ArcGoogleLocationServicesEnabled": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "ArcGoogleLocationServicesEnabled": 0 },
-    "pref_mappings": [
-      { "pref": "arc.location_service.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ArcGoogleLocationServicesEnabled": 0
+        },
+        "prefs": { "arc.location_service.enabled": {} }
+      }
     ]
   },
 
   "SafeSitesFilterBehavior": {
     "os": ["win", "linux", "mac", "chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "SafeSitesFilterBehavior": 0 },
-    "note": "pref_mappings cannot be tested here as the code where the pref will be registered is not yet ready."
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SafeSitesFilterBehavior": 0
+        },
+        "note": "prefs cannot be tested here as the code where the pref will be registered is not yet ready."
+      }
+    ]
   },
 
   "ReportVersionData": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "ReportVersionData": false },
-    "pref_mappings": [
-      { "pref": "enterprise_reporting.report_version_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ReportVersionData": false
+        },
+        "prefs": { "enterprise_reporting.report_version_data": {} }
+      }
     ]
   },
 
   "ReportPolicyData": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "ReportPolicyData": false },
-    "pref_mappings": [
-      { "pref": "enterprise_reporting.report_policy_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ReportPolicyData": false
+        },
+        "prefs": { "enterprise_reporting.report_policy_data": {} }
+      }
     ]
   },
 
   "ReportMachineIDData": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "ReportMachineIDData": false },
-    "pref_mappings": [
-      { "pref": "enterprise_reporting.report_machine_id_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ReportMachineIDData": false
+        },
+        "prefs": { "enterprise_reporting.report_machine_id_data": {} }
+      }
     ]
   },
 
   "ReportUserIDData": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "ReportUserIDData": false },
-    "pref_mappings": [
-      { "pref": "enterprise_reporting.report_user_id_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ReportUserIDData": false
+        },
+        "prefs": { "enterprise_reporting.report_user_id_data": {} }
+      }
     ]
   },
 
   "ReportExtensionsAndPluginsData": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "ReportExtensionsAndPluginsData": false },
-    "pref_mappings": [
-      { "pref": "enterprise_reporting.report_extensions_and_plugins_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ReportExtensionsAndPluginsData": false
+        },
+        "prefs": { "enterprise_reporting.report_extensions_and_plugins_data": {} }
+      }
     ]
   },
 
   "ReportSafeBrowsingData": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "ReportSafeBrowsingData": false },
-    "pref_mappings": [
-      { "pref": "enterprise_reporting.report_safe_browsing_data" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "ReportSafeBrowsingData": false
+        },
+        "prefs": { "enterprise_reporting.report_safe_browsing_data": {} }
+      }
     ]
   },
 
   "CloudExtensionRequestEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": { "CloudExtensionRequestEnabled": false },
-    "pref_mappings": [
-      { "pref": "enterprise_reporting.extension_request.enabled"}
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CloudExtensionRequestEnabled": false
+        },
+        "prefs": { "enterprise_reporting.extension_request.enabled": {} }
+      }
     ]
   },
 
   "CloudReportingEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "CloudReportingEnabled": true },
-    "pref_mappings": [
-      { "pref": "enterprise_reporting.chrome_cloud_reporting" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CloudReportingEnabled": true
+        },
+        "prefs": { "enterprise_reporting.chrome_cloud_reporting": {} }
+      }
     ]
   },
 
   "VpnConfigAllowed": {
     "os": ["chromeos"],
     "can_be_recommended": false,
-    "test_policy": { "VpnConfigAllowed": false },
-    "pref_mappings": [
-      { "pref": "vpn_config_allowed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "VpnConfigAllowed": false
+        },
+        "prefs": { "vpn_config_allowed": {} }
+      }
     ]
   },
 
   "AlternativeBrowserPath": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "AlternativeBrowserPath": "foobarbaz" },
-    "pref_mappings": [
-      { "pref": "browser_switcher.alternative_browser_path" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AlternativeBrowserPath": "foobarbaz"
+        },
+        "prefs": { "browser_switcher.alternative_browser_path": {} }
+      }
     ]
   },
 
   "AlternativeBrowserParameters": {
     "os": ["win", "linux", "mac"],
-    "test_policy": {
-      "AlternativeBrowserParameters": ["-foreground", "-new-window"]
-    },
-    "pref_mappings": [
-      { "pref": "browser_switcher.alternative_browser_parameters" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AlternativeBrowserParameters": [
+            "-foreground",
+            "-new-window"
+          ]
+        },
+        "prefs": { "browser_switcher.alternative_browser_parameters": {} }
+      }
     ]
   },
 
   "BrowserSwitcherChromePath": {
     "os": ["win"],
-    "test_policy": { "BrowserSwitcherChromePath": "chrome.exe" },
-    "pref_mappings": [
-      { "pref": "browser_switcher.chrome_path" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherChromePath": "chrome.exe"
+        },
+        "prefs": { "browser_switcher.chrome_path": {} }
+      }
     ]
   },
 
   "BrowserSwitcherChromeParameters": {
     "os": ["win"],
-    "test_policy": { "BrowserSwitcherChromeParameters": ["--force-dark-mode"] },
-    "pref_mappings": [
-      { "pref": "browser_switcher.chrome_parameters" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherChromeParameters": [
+            "--force-dark-mode"
+          ]
+        },
+        "prefs": { "browser_switcher.chrome_parameters": {} }
+      }
     ]
   },
 
-
   "BrowserSwitcherUrlList": {
     "os": ["win", "linux", "mac"],
-    "test_policy": {
-      "BrowserSwitcherUrlList": ["example.com", "google.com"]
-    },
-    "pref_mappings": [
-      { "pref": "browser_switcher.url_list" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherUrlList": [
+            "example.com",
+            "google.com"
+          ]
+        },
+        "prefs": { "browser_switcher.url_list": {} }
+      }
     ]
   },
 
   "BrowserSwitcherUrlGreylist": {
     "os": ["win", "linux", "mac"],
-    "test_policy": {
-      "BrowserSwitcherUrlGreylist": ["example.com", "google.com"]
-    },
-    "pref_mappings": [
-      { "pref": "browser_switcher.url_greylist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherUrlGreylist": [
+            "example.com",
+            "google.com"
+          ]
+        },
+        "prefs": { "browser_switcher.url_greylist": {} }
+      }
     ]
   },
 
   "BrowserSwitcherUseIeSitelist": {
     "os": ["win"],
-    "test_policy": {
-      "BrowserSwitcherUseIeSitelist": true
-    },
-    "pref_mappings": [
-      { "pref": "browser_switcher.use_ie_sitelist" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherUseIeSitelist": true
+        },
+        "prefs": { "browser_switcher.use_ie_sitelist": {} }
+      }
     ]
   },
 
   "BrowserSwitcherExternalSitelistUrl": {
     "os": ["win", "linux", "mac"],
-    "test_policy": {
-      "BrowserSwitcherExternalSitelistUrl": "file:///C:/src/sitelist.xml"
-    },
-    "pref_mappings": [
-      { "pref": "browser_switcher.external_sitelist_url" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherExternalSitelistUrl": "file:///C:/src/sitelist.xml"
+        },
+        "prefs": { "browser_switcher.external_sitelist_url": {} }
+      }
     ]
   },
 
   "BrowserSwitcherExternalGreylistUrl": {
     "os": [],
-    "test_policy": {
-      "BrowserSwitcherExternalGreylistUrl": "file:///C:/src/greylist.xml"
-    },
-    "pref_mappings": [
-      { "pref": "browser_switcher.external_greylist_url" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherExternalGreylistUrl": "file:///C:/src/greylist.xml"
+        },
+        "prefs": { "browser_switcher.external_greylist_url": {} }
+      }
     ]
   },
 
   "BrowserSwitcherDelay": {
     "os": ["win", "linux", "mac"],
-    "test_policy": {
-      "BrowserSwitcherDelay": 10000
-    },
-    "pref_mappings": [
-      { "pref": "browser_switcher.delay" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherDelay": 10000
+        },
+        "prefs": { "browser_switcher.delay": {} }
+      }
     ]
   },
 
   "BrowserSwitcherEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": {
-      "BrowserSwitcherEnabled": true
-    },
-    "pref_mappings": [
-      { "pref": "browser_switcher.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherEnabled": true
+        },
+        "prefs": { "browser_switcher.enabled": {} }
+      }
     ]
   },
 
   "BrowserSwitcherKeepLastChromeTab": {
     "os": ["win", "linux", "mac"],
-    "test_policy": {
-      "BrowserSwitcherKeepLastChromeTab": false
-    },
-    "pref_mappings": [
-      { "pref": "browser_switcher.keep_last_tab" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BrowserSwitcherKeepLastChromeTab": false
+        },
+        "prefs": { "browser_switcher.keep_last_tab": {} }
+      }
     ]
   },
 
   "StartupBrowserWindowLaunchSuppressed": {
     "os": ["chromeos"],
-    "test_policy": {
-      "StartupBrowserWindowLaunchSuppressed": false
-    },
-    "pref_mappings": [
-      { "pref": "startup_browser_window_launch_suppressed" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "StartupBrowserWindowLaunchSuppressed": false
+        },
+        "prefs": { "startup_browser_window_launch_suppressed": {} }
+      }
     ]
   },
 
   "SafeBrowsingRealTimeLookupEnabled": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "SafeBrowsingRealTimeLookupEnabled": true
-    },
-    "pref_mappings": [
-      { "pref": "safebrowsing.real_time_lookup_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SafeBrowsingRealTimeLookupEnabled": true
+        },
+        "prefs": { "safebrowsing.real_time_lookup_enabled": {} }
+      }
     ]
   },
 
   "LockScreenMediaPlaybackEnabled": {
     "os": ["chromeos"],
-    "test_policy": {
-      "LockScreenMediaPlaybackEnabled": true
-    },
-    "pref_mappings": [
-      { "pref": "ash.lock_screen_media_controls_enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "LockScreenMediaPlaybackEnabled": true
+        },
+        "prefs": { "ash.lock_screen_media_controls_enabled": {} }
+      }
     ]
   },
 
   "SendFilesForMalwareCheck": {
     "os": ["win", "linux", "mac", "chromeos"],
-    "test_policy": {
-      "SendFilesForMalwareCheck": 2
-    },
-    "pref_mappings": [
-      { "pref": "safebrowsing.send_files_for_malware_check" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "SendFilesForMalwareCheck": 2
+        },
+        "prefs": { "safebrowsing.send_files_for_malware_check": {} }
+      }
     ]
   },
 
   "UnsafeEventsReportingEnabled": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "UnsafeEventsReportingEnabled": false },
-    "pref_mappings": [ { "pref": "safebrowsing.unsafe_events_reporting",
-                         "local_state": true } ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "UnsafeEventsReportingEnabled": false
+        },
+        "prefs": { "safebrowsing.unsafe_events_reporting": { "local_state": true } }
+      }
+    ]
   },
 
   "BlockLargeFileTransfer": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "BlockLargeFileTransfer": 0 },
-    "pref_mappings": [ { "pref": "safebrowsing.block_large_file_transfers",
-                         "local_state": true } ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "BlockLargeFileTransfer": 0
+        },
+        "prefs": { "safebrowsing.block_large_file_transfers": { "local_state": true } }
+      }
+    ]
   },
 
   "DelayDeliveryUntilVerdict": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "DelayDeliveryUntilVerdict": 0 },
-    "pref_mappings": [ { "pref": "safebrowsing.delay_delivery_until_verdict",
-                         "local_state": true } ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "DelayDeliveryUntilVerdict": 0
+        },
+        "prefs": { "safebrowsing.delay_delivery_until_verdict": { "local_state": true } }
+      }
+    ]
   },
 
   "AllowPasswordProtectedFiles": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "AllowPasswordProtectedFiles": 0 },
-    "pref_mappings": [ { "pref": "safebrowsing.allow_password_protected_files",
-                         "local_state": true } ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "AllowPasswordProtectedFiles": 0
+        },
+        "prefs": { "safebrowsing.allow_password_protected_files": { "local_state": true } }
+      }
+    ]
   },
 
   "CheckContentCompliance": {
     "os": ["win", "linux", "mac"],
-    "test_policy": { "CheckContentCompliance": 0 },
-    "pref_mappings": [ { "pref": "safebrowsing.check_content_compliance",
-                         "local_state": true } ]
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CheckContentCompliance": 0 },
+        "prefs": { "safebrowsing.check_content_compliance": { "local_state": true } }
+      }
+    ]
   },
 
-  "URLsToCheckComplianceOfDownloadedContent": {
-  },
+  "URLsToCheckComplianceOfDownloadedContent": {},
 
-  "URLsToNotCheckComplianceOfUploadedContent": {
-  },
+  "URLsToNotCheckComplianceOfUploadedContent": {},
 
-  "URLsToCheckForMalwareOfUploadedContent": {
-  },
+  "URLsToCheckForMalwareOfUploadedContent": {},
 
   "RendererCodeIntegrityEnabled": {
     "os": ["win"],
-    "test_policy": { "RendererCodeIntegrityEnabled": true },
-    "pref_mappings": [
-      { "pref": "renderer_code_integrity_enabled",
-        "local_state": true
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "RendererCodeIntegrityEnabled": true },
+        "prefs": { "renderer_code_integrity_enabled": { "local_state": true } }
       }
     ]
   },
 
   "HSTSPolicyBypassList": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "HSTSPolicyBypassList": ["example"] },
-    "pref_mappings": [
-      { "pref": "hsts.policy.upgrade_bypass_list", "local_state": true }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "HSTSPolicyBypassList": ["example"]},
+        "prefs": { "hsts.policy.upgrade_bypass_list": { "local_state": true } }
+      }
     ]
   },
 
   "TotalMemoryLimitMb": {
-    "os": ["win", "mac"],
-    "test_policy": { "TotalMemoryLimitMb": 2048 },
-    "pref_mappings": [
+    "os": [
+      "win",
+      "mac"
+    ],
+    "policy_pref_mapping_test": [
       {
-        "pref": "total_memory_limit_mb",
-        "local_state":  true
+        "policies": { "TotalMemoryLimitMb": 2048 },
+        "prefs": { "total_memory_limit_mb": { "local_state": true } }
       }
     ]
   },
 
   "TLS13HardeningForLocalAnchorsEnabled": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "TLS13HardeningForLocalAnchorsEnabled": true },
-    "pref_mappings": [
-      { "pref": "ssl.tls13_hardening_for_local_anchors", "local_state": true }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "TLS13HardeningForLocalAnchorsEnabled": true },
+        "prefs": { "ssl.tls13_hardening_for_local_anchors": { "local_state": true } }
+      }
     ]
   },
 
   "CorsMitigationList": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "CorsMitigationList": ["x-googapps-allowed-domains", "youtube-restrict"] },
-    "pref_mappings": [
-      { "pref": "cors.mitigation.list" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": {
+          "CorsMitigationList": [
+            "x-googapps-allowed-domains",
+            "youtube-restrict"
+          ]
+        },
+        "prefs": { "cors.mitigation.list": {} }
+      }
     ]
   },
 
   "CorsLegacyModeEnabled": {
     "os": ["win", "linux", "mac", "chromeos", "android"],
-    "test_policy": { "CorsLegacyModeEnabled": true },
-    "pref_mappings": [
-      { "pref": "cors.legacy_mode.enabled" }
+    "policy_pref_mapping_test": [
+      {
+        "policies": { "CorsLegacyModeEnabled": true },
+        "prefs": { "cors.legacy_mode.enabled": {} }
+      }
     ]
   }
-}
+}
\ No newline at end of file
diff --git a/chrome/test/data/webrtc/peerconnection_getstats.js b/chrome/test/data/webrtc/peerconnection_getstats.js
index 824aa87..32cb2a1 100644
--- a/chrome/test/data/webrtc/peerconnection_getstats.js
+++ b/chrome/test/data/webrtc/peerconnection_getstats.js
@@ -119,6 +119,8 @@
   keyFramesDecoded: 'number',
   qpSum: 'number',
   totalDecodeTime: 'number',
+  totalInterFrameDelay: 'number',
+  totalSquaredInterFrameDelay: 'number',
   lastPacketReceivedTimestamp: 'number',
   averageRtcpInterval: 'number',
   fecPacketsReceived: 'number',
diff --git a/chromecast/browser/webview/web_content_controller.cc b/chromecast/browser/webview/web_content_controller.cc
index 12cddaaa..fc610b685 100644
--- a/chromecast/browser/webview/web_content_controller.cc
+++ b/chromecast/browser/webview/web_content_controller.cc
@@ -234,6 +234,10 @@
             base::TimeTicks() +
                 base::TimeDelta::FromMicroseconds(ev.timestamp()),
             ev.flags(), mouse.changed_button_flags());
+        if (contents->GetAccessibilityMode().has_mode(
+                ui::AXMode::kWebContents)) {
+          evt.set_flags(evt.flags() | ui::EF_TOUCH_ACCESSIBILITY);
+        }
         handler->OnMouseEvent(&evt);
       } else {
         client_->OnError("mouse() not supplied for mouse event");
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 67eff7d..e2c1348 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-12660.0.0
\ No newline at end of file
+12699.0.0
\ No newline at end of file
diff --git a/chromeos/dbus/dlcservice/dlcservice_client.cc b/chromeos/dbus/dlcservice/dlcservice_client.cc
index f1f3a6ab..5aaa9992 100644
--- a/chromeos/dbus/dlcservice/dlcservice_client.cc
+++ b/chromeos/dbus/dlcservice/dlcservice_client.cc
@@ -37,8 +37,6 @@
 
 namespace {
 
-const char kOnInstallStatus[] = "OnInstallStatus";
-
 DlcserviceClient* g_instance = nullptr;
 
 class DlcserviceErrorResponseHandler {
@@ -181,13 +179,10 @@
     dlcservice_proxy_ = bus->GetObjectProxy(
         dlcservice::kDlcServiceServiceName,
         dbus::ObjectPath(dlcservice::kDlcServiceServicePath));
-    // TODO(kimjae): Use from cros_system_api the const once sync'ed for
-    // kOnInstallStatus as dlcservice::kOnInstallStatus and delete the
-    // definition of kOnInstallStatus.
     dlcservice_proxy_->ConnectToSignal(
-        dlcservice::kDlcServiceInterface, kOnInstallStatus,
-        base::Bind(&DlcserviceClientImpl::OnInstallStatus,
-                   weak_ptr_factory_.GetWeakPtr()),
+        dlcservice::kDlcServiceInterface, dlcservice::kOnInstallStatusSignal,
+        base::BindRepeating(&DlcserviceClientImpl::OnInstallStatus,
+                            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&DlcserviceClientImpl::OnInstallStatusConnected,
                        weak_ptr_factory_.GetWeakPtr()));
   }
@@ -239,10 +234,6 @@
         SendCompleted(install_status);
         break;
       case dlcservice::Status::RUNNING: {
-        // TODO(kimjae): Currently, update_engine delegate's two actions.
-        // Specifically the Download action and Post Install Runner action,
-        // which means that progress will go from 0 -> 1 two complete cycles.
-        // This can be easily handled either here or inside dlcservice daemon.
         SendProgress(install_status);
         // Need to return here since we don't want to try starting another
         // pending install from the queue (would waste time checking).
diff --git a/chromeos/dbus/media_analytics/media_analytics_client.cc b/chromeos/dbus/media_analytics/media_analytics_client.cc
index 3898507..80ee93d 100644
--- a/chromeos/dbus/media_analytics/media_analytics_client.cc
+++ b/chromeos/dbus/media_analytics/media_analytics_client.cc
@@ -99,8 +99,9 @@
     dbus_proxy_->ConnectToSignal(
         media_perception::kMediaPerceptionInterface,
         media_perception::kDetectionSignal,
-        base::Bind(&MediaAnalyticsClientImpl::OnDetectionSignalReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(
+            &MediaAnalyticsClientImpl::OnDetectionSignalReceived,
+            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&MediaAnalyticsClientImpl::OnSignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
   }
diff --git a/chromeos/dbus/power/power_manager_client.cc b/chromeos/dbus/power/power_manager_client.cc
index 4427040..4782a09 100644
--- a/chromeos/dbus/power/power_manager_client.cc
+++ b/chromeos/dbus/power/power_manager_client.cc
@@ -166,8 +166,8 @@
         dbus::ObjectPath(power_manager::kPowerManagerServicePath));
 
     power_manager_proxy_->SetNameOwnerChangedCallback(
-        base::Bind(&PowerManagerClientImpl::NameOwnerChangedReceived,
-                   weak_ptr_factory_.GetWeakPtr()));
+        base::BindRepeating(&PowerManagerClientImpl::NameOwnerChangedReceived,
+                            weak_ptr_factory_.GetWeakPtr()));
 
     power_manager_proxy_->WaitForServiceToBeAvailable(
         base::BindOnce(&PowerManagerClientImpl::NotifyServiceBecameAvailable,
diff --git a/chromeos/dbus/services/cros_dbus_service.cc b/chromeos/dbus/services/cros_dbus_service.cc
index ea15dd5..9eaf4a77 100644
--- a/chromeos/dbus/services/cros_dbus_service.cc
+++ b/chromeos/dbus/services/cros_dbus_service.cc
@@ -59,9 +59,10 @@
     // situation if we don't allow the new browser instance to replace the old
     // as the owner of |service_name_| as seen in http://crbug.com/234382.
     // Hence, REQUIRE_PRIMARY_ALLOW_REPLACEMENT.
-    bus_->RequestOwnership(
-        service_name_, dbus::Bus::REQUIRE_PRIMARY_ALLOW_REPLACEMENT,
-        base::Bind(&CrosDBusServiceImpl::OnOwnership, base::Unretained(this)));
+    bus_->RequestOwnership(service_name_,
+                           dbus::Bus::REQUIRE_PRIMARY_ALLOW_REPLACEMENT,
+                           base::BindOnce(&CrosDBusServiceImpl::OnOwnership,
+                                          base::Unretained(this)));
 
     service_started_ = true;
   }
diff --git a/chromeos/dbus/services/service_provider_test_helper.cc b/chromeos/dbus/services/service_provider_test_helper.cc
index 723eaf4d..c3a26280 100644
--- a/chromeos/dbus/services/service_provider_test_helper.cc
+++ b/chromeos/dbus/services/service_provider_test_helper.cc
@@ -132,8 +132,8 @@
   // will be received by |on_signal_callback_|.
   std::unique_ptr<dbus::Response> response;
   method_callback_.Run(method_call,
-                       base::Bind(&ServiceProviderTestHelper::OnResponse,
-                                  base::Unretained(this), &response));
+                       base::BindOnce(&ServiceProviderTestHelper::OnResponse,
+                                      base::Unretained(this), &response));
   // Check for a response.
   if (!response)
     base::RunLoop().Run();
diff --git a/chromeos/dbus/session_manager/session_manager_client.cc b/chromeos/dbus/session_manager/session_manager_client.cc
index a5d1faa..a556183 100644
--- a/chromeos/dbus/session_manager/session_manager_client.cc
+++ b/chromeos/dbus/session_manager/session_manager_client.cc
@@ -656,36 +656,38 @@
     session_manager_proxy_->ConnectToSignal(
         login_manager::kSessionManagerInterface,
         login_manager::kOwnerKeySetSignal,
-        base::Bind(&SessionManagerClientImpl::OwnerKeySetReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&SessionManagerClientImpl::OwnerKeySetReceived,
+                            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&SessionManagerClientImpl::SignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
     session_manager_proxy_->ConnectToSignal(
         login_manager::kSessionManagerInterface,
         login_manager::kPropertyChangeCompleteSignal,
-        base::Bind(&SessionManagerClientImpl::PropertyChangeCompleteReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(
+            &SessionManagerClientImpl::PropertyChangeCompleteReceived,
+            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&SessionManagerClientImpl::SignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
     session_manager_proxy_->ConnectToSignal(
         login_manager::kSessionManagerInterface,
         login_manager::kScreenIsLockedSignal,
-        base::Bind(&SessionManagerClientImpl::ScreenIsLockedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&SessionManagerClientImpl::ScreenIsLockedReceived,
+                            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&SessionManagerClientImpl::SignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
     session_manager_proxy_->ConnectToSignal(
         login_manager::kSessionManagerInterface,
         login_manager::kScreenIsUnlockedSignal,
-        base::Bind(&SessionManagerClientImpl::ScreenIsUnlockedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&SessionManagerClientImpl::ScreenIsUnlockedReceived,
+                            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&SessionManagerClientImpl::SignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
     session_manager_proxy_->ConnectToSignal(
         login_manager::kSessionManagerInterface,
         login_manager::kArcInstanceStopped,
-        base::Bind(&SessionManagerClientImpl::ArcInstanceStoppedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(
+            &SessionManagerClientImpl::ArcInstanceStoppedReceived,
+            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&SessionManagerClientImpl::SignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
   }
diff --git a/chromeos/dbus/shill/fake_shill_device_client.cc b/chromeos/dbus/shill/fake_shill_device_client.cc
index ba42511..182c0f3 100644
--- a/chromeos/dbus/shill/fake_shill_device_client.cc
+++ b/chromeos/dbus/shill/fake_shill_device_client.cc
@@ -42,13 +42,14 @@
 }
 
 void PostError(const std::string& error,
-               const ShillDeviceClient::ErrorCallback& error_callback) {
+               ShillDeviceClient::ErrorCallback error_callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(error_callback, error, kFailedMessage));
+      FROM_HERE,
+      base::BindOnce(std::move(error_callback), error, kFailedMessage));
 }
 
-void PostNotFoundError(const ShillDeviceClient::ErrorCallback& error_callback) {
-  PostError(shill::kErrorResultNotFound, error_callback);
+void PostNotFoundError(ShillDeviceClient::ErrorCallback error_callback) {
+  PostError(shill::kErrorResultNotFound, std::move(error_callback));
 }
 
 }  // namespace
@@ -78,21 +79,22 @@
   GetObserverList(device_path).RemoveObserver(observer);
 }
 
-void FakeShillDeviceClient::GetProperties(
-    const dbus::ObjectPath& device_path,
-    const DictionaryValueCallback& callback) {
+void FakeShillDeviceClient::GetProperties(const dbus::ObjectPath& device_path,
+                                          DictionaryValueCallback callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
       base::BindOnce(&FakeShillDeviceClient::PassStubDeviceProperties,
-                     weak_ptr_factory_.GetWeakPtr(), device_path, callback));
+                     weak_ptr_factory_.GetWeakPtr(), device_path,
+                     std::move(callback)));
 }
 
 void FakeShillDeviceClient::SetProperty(const dbus::ObjectPath& device_path,
                                         const std::string& name,
                                         const base::Value& value,
-                                        const base::Closure& callback,
-                                        const ErrorCallback& error_callback) {
-  SetPropertyInternal(device_path, name, value, callback, error_callback,
+                                        base::OnceClosure callback,
+                                        ErrorCallback error_callback) {
+  SetPropertyInternal(device_path, name, value, std::move(callback),
+                      std::move(error_callback),
                       /*notify_changed=*/true);
 }
 
@@ -100,13 +102,13 @@
     const dbus::ObjectPath& device_path,
     const std::string& name,
     const base::Value& value,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback,
+    base::OnceClosure callback,
+    ErrorCallback error_callback,
     bool notify_changed) {
   base::DictionaryValue* device_properties = nullptr;
   if (!stub_devices_.GetDictionaryWithoutPathExpansion(device_path.value(),
                                                        &device_properties)) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
   device_properties->SetKey(name, value.Clone());
@@ -116,7 +118,7 @@
         base::BindOnce(&FakeShillDeviceClient::NotifyObserversPropertyChanged,
                        weak_ptr_factory_.GetWeakPtr(), device_path, name));
   }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::ClearProperty(const dbus::ObjectPath& device_path,
@@ -135,114 +137,114 @@
 void FakeShillDeviceClient::RequirePin(const dbus::ObjectPath& device_path,
                                        const std::string& pin,
                                        bool require,
-                                       const base::Closure& callback,
-                                       const ErrorCallback& error_callback) {
+                                       base::OnceClosure callback,
+                                       ErrorCallback error_callback) {
   VLOG(1) << "RequirePin: " << device_path.value();
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
   if (!SimTryPin(device_path.value(), pin)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(error_callback, shill::kErrorResultIncorrectPin, ""));
+        FROM_HERE, base::BindOnce(std::move(error_callback),
+                                  shill::kErrorResultIncorrectPin, ""));
     return;
   }
   SimLockStatus status = GetSimLockStatus(device_path.value());
   status.lock_enabled = require;
   SetSimLockStatus(device_path.value(), status);
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::EnterPin(const dbus::ObjectPath& device_path,
                                      const std::string& pin,
-                                     const base::Closure& callback,
-                                     const ErrorCallback& error_callback) {
+                                     base::OnceClosure callback,
+                                     ErrorCallback error_callback) {
   VLOG(1) << "EnterPin: " << device_path.value();
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
   if (!SimTryPin(device_path.value(), pin)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(error_callback, shill::kErrorResultIncorrectPin, ""));
+        FROM_HERE, base::BindOnce(std::move(error_callback),
+                                  shill::kErrorResultIncorrectPin, ""));
     return;
   }
   SetSimLocked(device_path.value(), false);
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::UnblockPin(const dbus::ObjectPath& device_path,
                                        const std::string& puk,
                                        const std::string& pin,
-                                       const base::Closure& callback,
-                                       const ErrorCallback& error_callback) {
+                                       base::OnceClosure callback,
+                                       ErrorCallback error_callback) {
   VLOG(1) << "UnblockPin: " << device_path.value();
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
   if (!SimTryPuk(device_path.value(), puk)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(error_callback, shill::kErrorResultIncorrectPin, ""));
+        FROM_HERE, base::BindOnce(std::move(error_callback),
+                                  shill::kErrorResultIncorrectPin, ""));
     return;
   }
   if (pin.length() < kSimPinMinLength) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(error_callback,
+        FROM_HERE, base::BindOnce(std::move(error_callback),
                                   shill::kErrorResultInvalidArguments, ""));
     return;
   }
   sim_pin_[device_path.value()] = pin;
   SetSimLocked(device_path.value(), false);
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::ChangePin(const dbus::ObjectPath& device_path,
                                       const std::string& old_pin,
                                       const std::string& new_pin,
-                                      const base::Closure& callback,
-                                      const ErrorCallback& error_callback) {
+                                      base::OnceClosure callback,
+                                      ErrorCallback error_callback) {
   VLOG(1) << "ChangePin: " << device_path.value();
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
   if (!SimTryPin(device_path.value(), old_pin)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(error_callback, shill::kErrorResultIncorrectPin, ""));
+        FROM_HERE, base::BindOnce(std::move(error_callback),
+                                  shill::kErrorResultIncorrectPin, ""));
     return;
   }
   if (new_pin.length() < kSimPinMinLength) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(error_callback,
+        FROM_HERE, base::BindOnce(std::move(error_callback),
                                   shill::kErrorResultInvalidArguments, ""));
     return;
   }
   sim_pin_[device_path.value()] = new_pin;
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::Register(const dbus::ObjectPath& device_path,
                                      const std::string& network_id,
-                                     const base::Closure& callback,
-                                     const ErrorCallback& error_callback) {
+                                     base::OnceClosure callback,
+                                     ErrorCallback error_callback) {
   base::Value* device_properties = stub_devices_.FindKey(device_path.value());
   if (!device_properties || !device_properties->is_dict()) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
   base::Value* scan_results =
       device_properties->FindKey(shill::kFoundNetworksProperty);
   if (!scan_results) {
-    PostError("No Cellular scan results", error_callback);
+    PostError("No Cellular scan results", std::move(error_callback));
     return;
   }
   for (auto& network : scan_results->GetList()) {
@@ -250,41 +252,42 @@
     std::string status = id == network_id ? "current" : "available";
     network.SetKey(shill::kStatusProperty, base::Value(status));
   }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::Reset(const dbus::ObjectPath& device_path,
-                                  const base::Closure& callback,
-                                  const ErrorCallback& error_callback) {
+                                  base::OnceClosure callback,
+                                  ErrorCallback error_callback) {
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::PerformTDLSOperation(
     const dbus::ObjectPath& device_path,
     const std::string& operation,
     const std::string& peer,
-    const StringCallback& callback,
-    const ErrorCallback& error_callback) {
+    StringCallback callback,
+    ErrorCallback error_callback) {
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
   // Use -1 to emulate a TDLS failure.
   if (tdls_busy_count_ == -1) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(error_callback, shill::kErrorDhcpFailed, "Failed"));
+        FROM_HERE, base::BindOnce(std::move(error_callback),
+                                  shill::kErrorDhcpFailed, "Failed"));
     return;
   }
   if (operation != shill::kTDLSStatusOperation && tdls_busy_count_ > 0) {
     --tdls_busy_count_;
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(error_callback, shill::kErrorResultInProgress,
-                                  "In-Progress"));
+        FROM_HERE,
+        base::BindOnce(std::move(error_callback), shill::kErrorResultInProgress,
+                       "In-Progress"));
     return;
   }
 
@@ -305,74 +308,74 @@
   }
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(callback, result));
+      FROM_HERE, base::BindOnce(std::move(callback), result));
 }
 
 void FakeShillDeviceClient::AddWakeOnPacketConnection(
     const dbus::ObjectPath& device_path,
     const net::IPEndPoint& ip_endpoint,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
 
   wake_on_packet_connections_[device_path].insert(ip_endpoint);
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::AddWakeOnPacketOfTypes(
     const dbus::ObjectPath& device_path,
     const std::vector<std::string>& types,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
 
   wake_on_packet_types_[device_path].insert(types.begin(), types.end());
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::RemoveWakeOnPacketConnection(
     const dbus::ObjectPath& device_path,
     const net::IPEndPoint& ip_endpoint,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   const auto device_iter = wake_on_packet_connections_.find(device_path);
   if (!stub_devices_.HasKey(device_path.value()) ||
       device_iter == wake_on_packet_connections_.end()) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
 
   const auto endpoint_iter = device_iter->second.find(ip_endpoint);
   if (endpoint_iter == device_iter->second.end()) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
 
   device_iter->second.erase(endpoint_iter);
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::RemoveWakeOnPacketOfTypes(
     const dbus::ObjectPath& device_path,
     const std::vector<std::string>& types,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
 
   const auto registered_types_iter = wake_on_packet_types_.find(device_path);
   if (registered_types_iter == wake_on_packet_types_.end()) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
 
@@ -380,32 +383,32 @@
   for (auto it = types.begin(); it != types.end(); it++)
     registered_types.erase(*it);
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::RemoveAllWakeOnPacketConnections(
     const dbus::ObjectPath& device_path,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   const auto iter = wake_on_packet_connections_.find(device_path);
   if (!stub_devices_.HasKey(device_path.value()) ||
       iter == wake_on_packet_connections_.end()) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
 
   wake_on_packet_connections_.erase(iter);
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillDeviceClient::SetUsbEthernetMacAddressSource(
     const dbus::ObjectPath& device_path,
     const std::string& source,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   if (!stub_devices_.HasKey(device_path.value())) {
-    PostNotFoundError(error_callback);
+    PostNotFoundError(std::move(error_callback));
     return;
   }
 
@@ -415,7 +418,7 @@
   if (error_name_iter !=
           set_usb_ethernet_mac_address_source_error_names_.end() &&
       !error_name_iter->second.empty()) {
-    PostError(error_name_iter->second, error_callback);
+    PostError(error_name_iter->second, std::move(error_callback));
     return;
   }
 
@@ -423,7 +426,7 @@
                     shill::kUsbEthernetMacAddressSourceProperty,
                     base::Value(source), /*notify_changed=*/true);
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 ShillDeviceClient::TestInterface* FakeShillDeviceClient::GetTestInterface() {
@@ -465,9 +468,9 @@
                                               bool notify_changed) {
   VLOG(1) << "SetDeviceProperty: " << device_path << ": " << name << " = "
           << value;
-  SetPropertyInternal(dbus::ObjectPath(device_path), name, value,
-                      base::DoNothing(),
-                      base::Bind(&ErrorFunction, device_path), notify_changed);
+  SetPropertyInternal(
+      dbus::ObjectPath(device_path), name, value, base::DoNothing(),
+      base::BindOnce(&ErrorFunction, device_path), notify_changed);
 }
 
 std::string FakeShillDeviceClient::GetDevicePathForType(
@@ -654,15 +657,15 @@
 
 void FakeShillDeviceClient::PassStubDeviceProperties(
     const dbus::ObjectPath& device_path,
-    const DictionaryValueCallback& callback) const {
+    DictionaryValueCallback callback) const {
   const base::DictionaryValue* device_properties = nullptr;
   if (!stub_devices_.GetDictionaryWithoutPathExpansion(device_path.value(),
                                                        &device_properties)) {
     base::DictionaryValue empty_dictionary;
-    callback.Run(DBUS_METHOD_CALL_FAILURE, empty_dictionary);
+    std::move(callback).Run(DBUS_METHOD_CALL_FAILURE, empty_dictionary);
     return;
   }
-  callback.Run(DBUS_METHOD_CALL_SUCCESS, *device_properties);
+  std::move(callback).Run(DBUS_METHOD_CALL_SUCCESS, *device_properties);
 }
 
 // Posts a task to run a void callback with status code |status|.
diff --git a/chromeos/dbus/shill/fake_shill_device_client.h b/chromeos/dbus/shill/fake_shill_device_client.h
index 0f557da..efc9cff 100644
--- a/chromeos/dbus/shill/fake_shill_device_client.h
+++ b/chromeos/dbus/shill/fake_shill_device_client.h
@@ -34,72 +34,69 @@
       const dbus::ObjectPath& device_path,
       ShillPropertyChangedObserver* observer) override;
   void GetProperties(const dbus::ObjectPath& device_path,
-                     const DictionaryValueCallback& callback) override;
+                     DictionaryValueCallback callback) override;
   void SetProperty(const dbus::ObjectPath& device_path,
                    const std::string& name,
                    const base::Value& value,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override;
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override;
   void ClearProperty(const dbus::ObjectPath& device_path,
                      const std::string& name,
                      VoidDBusMethodCallback callback) override;
   void RequirePin(const dbus::ObjectPath& device_path,
                   const std::string& pin,
                   bool require,
-                  const base::Closure& callback,
-                  const ErrorCallback& error_callback) override;
+                  base::OnceClosure callback,
+                  ErrorCallback error_callback) override;
   void EnterPin(const dbus::ObjectPath& device_path,
                 const std::string& pin,
-                const base::Closure& callback,
-                const ErrorCallback& error_callback) override;
+                base::OnceClosure callback,
+                ErrorCallback error_callback) override;
   void UnblockPin(const dbus::ObjectPath& device_path,
                   const std::string& puk,
                   const std::string& pin,
-                  const base::Closure& callback,
-                  const ErrorCallback& error_callback) override;
+                  base::OnceClosure callback,
+                  ErrorCallback error_callback) override;
   void ChangePin(const dbus::ObjectPath& device_path,
                  const std::string& old_pin,
                  const std::string& new_pin,
-                 const base::Closure& callback,
-                 const ErrorCallback& error_callback) override;
+                 base::OnceClosure callback,
+                 ErrorCallback error_callback) override;
   void Register(const dbus::ObjectPath& device_path,
                 const std::string& network_id,
-                const base::Closure& callback,
-                const ErrorCallback& error_callback) override;
+                base::OnceClosure callback,
+                ErrorCallback error_callback) override;
   void Reset(const dbus::ObjectPath& device_path,
-             const base::Closure& callback,
-             const ErrorCallback& error_callback) override;
+             base::OnceClosure callback,
+             ErrorCallback error_callback) override;
   void PerformTDLSOperation(const dbus::ObjectPath& device_path,
                             const std::string& operation,
                             const std::string& peer,
-                            const StringCallback& callback,
-                            const ErrorCallback& error_callback) override;
+                            StringCallback callback,
+                            ErrorCallback error_callback) override;
   void AddWakeOnPacketConnection(const dbus::ObjectPath& device_path,
                                  const net::IPEndPoint& ip_endpoint,
-                                 const base::Closure& callback,
-                                 const ErrorCallback& error_callback) override;
+                                 base::OnceClosure callback,
+                                 ErrorCallback error_callback) override;
   void AddWakeOnPacketOfTypes(const dbus::ObjectPath& device_path,
                               const std::vector<std::string>& types,
-                              const base::Closure& callback,
-                              const ErrorCallback& error_callback) override;
-  void RemoveWakeOnPacketConnection(
-      const dbus::ObjectPath& device_path,
-      const net::IPEndPoint& ip_endpoint,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) override;
+                              base::OnceClosure callback,
+                              ErrorCallback error_callback) override;
+  void RemoveWakeOnPacketConnection(const dbus::ObjectPath& device_path,
+                                    const net::IPEndPoint& ip_endpoint,
+                                    base::OnceClosure callback,
+                                    ErrorCallback error_callback) override;
   void RemoveWakeOnPacketOfTypes(const dbus::ObjectPath& device_path,
                                  const std::vector<std::string>& types,
-                                 const base::Closure& callback,
-                                 const ErrorCallback& error_callback) override;
-  void RemoveAllWakeOnPacketConnections(
-      const dbus::ObjectPath& device_path,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) override;
-  void SetUsbEthernetMacAddressSource(
-      const dbus::ObjectPath& device_path,
-      const std::string& source,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) override;
+                                 base::OnceClosure callback,
+                                 ErrorCallback error_callback) override;
+  void RemoveAllWakeOnPacketConnections(const dbus::ObjectPath& device_path,
+                                        base::OnceClosure callback,
+                                        ErrorCallback error_callback) override;
+  void SetUsbEthernetMacAddressSource(const dbus::ObjectPath& device_path,
+                                      const std::string& source,
+                                      base::OnceClosure callback,
+                                      ErrorCallback error_callback) override;
 
   ShillDeviceClient::TestInterface* GetTestInterface() override;
 
@@ -141,7 +138,7 @@
   bool SimTryPin(const std::string& device_path, const std::string& pin);
   bool SimTryPuk(const std::string& device_path, const std::string& pin);
   void PassStubDeviceProperties(const dbus::ObjectPath& device_path,
-                                const DictionaryValueCallback& callback) const;
+                                DictionaryValueCallback callback) const;
 
   // Posts a task to run a void callback with status code |result|.
   void PostVoidCallback(VoidDBusMethodCallback callback, bool result);
@@ -151,8 +148,8 @@
   void SetPropertyInternal(const dbus::ObjectPath& device_path,
                            const std::string& name,
                            const base::Value& value,
-                           const base::Closure& callback,
-                           const ErrorCallback& error_callback,
+                           base::OnceClosure callback,
+                           ErrorCallback error_callback,
                            bool notify_changed);
 
   void NotifyObserversPropertyChanged(const dbus::ObjectPath& device_path,
diff --git a/chromeos/dbus/shill/fake_shill_ipconfig_client.cc b/chromeos/dbus/shill/fake_shill_ipconfig_client.cc
index 319b284..7918e72 100644
--- a/chromeos/dbus/shill/fake_shill_ipconfig_client.cc
+++ b/chromeos/dbus/shill/fake_shill_ipconfig_client.cc
@@ -37,15 +37,15 @@
 
 void FakeShillIPConfigClient::GetProperties(
     const dbus::ObjectPath& ipconfig_path,
-    const DictionaryValueCallback& callback) {
+    DictionaryValueCallback callback) {
   const base::DictionaryValue* dict = nullptr;
   if (!ipconfigs_.GetDictionaryWithoutPathExpansion(ipconfig_path.value(),
                                                     &dict))
     return;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&FakeShillIPConfigClient::PassProperties,
-                     weak_ptr_factory_.GetWeakPtr(), dict, callback));
+      FROM_HERE, base::BindOnce(&FakeShillIPConfigClient::PassProperties,
+                                weak_ptr_factory_.GetWeakPtr(), dict,
+                                std::move(callback)));
 }
 
 void FakeShillIPConfigClient::SetProperty(const dbus::ObjectPath& ipconfig_path,
@@ -96,8 +96,8 @@
 
 void FakeShillIPConfigClient::PassProperties(
     const base::DictionaryValue* values,
-    const DictionaryValueCallback& callback) const {
-  callback.Run(DBUS_METHOD_CALL_SUCCESS, *values);
+    DictionaryValueCallback callback) const {
+  std::move(callback).Run(DBUS_METHOD_CALL_SUCCESS, *values);
 }
 
 }  // namespace chromeos
diff --git a/chromeos/dbus/shill/fake_shill_ipconfig_client.h b/chromeos/dbus/shill/fake_shill_ipconfig_client.h
index eeac643..818778d 100644
--- a/chromeos/dbus/shill/fake_shill_ipconfig_client.h
+++ b/chromeos/dbus/shill/fake_shill_ipconfig_client.h
@@ -29,7 +29,7 @@
       const dbus::ObjectPath& ipconfig_path,
       ShillPropertyChangedObserver* observer) override;
   void GetProperties(const dbus::ObjectPath& ipconfig_path,
-                     const DictionaryValueCallback& callback) override;
+                     DictionaryValueCallback callback) override;
   void SetProperty(const dbus::ObjectPath& ipconfig_path,
                    const std::string& name,
                    const base::Value& value,
@@ -48,7 +48,7 @@
  private:
   // Runs callback with |values|.
   void PassProperties(const base::DictionaryValue* values,
-                      const DictionaryValueCallback& callback) const;
+                      DictionaryValueCallback callback) const;
 
   // Dictionary of <ipconfig_path, property dictionaries>
   base::DictionaryValue ipconfigs_;
diff --git a/chromeos/dbus/shill/fake_shill_manager_client.cc b/chromeos/dbus/shill/fake_shill_manager_client.cc
index 9d72949..da5c33e 100644
--- a/chromeos/dbus/shill/fake_shill_manager_client.cc
+++ b/chromeos/dbus/shill/fake_shill_manager_client.cc
@@ -247,34 +247,35 @@
   observer_list_.RemoveObserver(observer);
 }
 
-void FakeShillManagerClient::GetProperties(
-    const DictionaryValueCallback& callback) {
+void FakeShillManagerClient::GetProperties(DictionaryValueCallback callback) {
   VLOG(1) << "Manager.GetProperties";
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&FakeShillManagerClient::PassStubProperties,
-                                weak_ptr_factory_.GetWeakPtr(), callback));
+      FROM_HERE,
+      base::BindOnce(&FakeShillManagerClient::PassStubProperties,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void FakeShillManagerClient::GetNetworksForGeolocation(
-    const DictionaryValueCallback& callback) {
+    DictionaryValueCallback callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&FakeShillManagerClient::PassStubGeoNetworks,
-                                weak_ptr_factory_.GetWeakPtr(), callback));
+      FROM_HERE,
+      base::BindOnce(&FakeShillManagerClient::PassStubGeoNetworks,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void FakeShillManagerClient::SetProperty(const std::string& name,
                                          const base::Value& value,
-                                         const base::Closure& callback,
-                                         const ErrorCallback& error_callback) {
+                                         base::OnceClosure callback,
+                                         ErrorCallback error_callback) {
   VLOG(2) << "SetProperty: " << name;
   stub_properties_.SetKey(name, value.Clone());
   CallNotifyObserversPropertyChanged(name);
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillManagerClient::RequestScan(const std::string& type,
-                                         const base::Closure& callback,
-                                         const ErrorCallback& error_callback) {
+                                         base::OnceClosure callback,
+                                         ErrorCallback error_callback) {
   VLOG(1) << "RequestScan: " << type;
   // For Stub purposes, default to a Wifi scan.
   std::string device_type = type.empty() ? shill::kTypeWifi : type;
@@ -289,7 +290,7 @@
       device_client->AddCellularFoundNetwork(device_path);
   }
   // Trigger |callback| immediately to indicate that the scan started.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&FakeShillManagerClient::ScanCompleted,
@@ -297,56 +298,57 @@
       interactive_delay_);
 }
 
-void FakeShillManagerClient::EnableTechnology(
-    const std::string& type,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+void FakeShillManagerClient::EnableTechnology(const std::string& type,
+                                              base::OnceClosure callback,
+                                              ErrorCallback error_callback) {
   base::ListValue* enabled_list = nullptr;
   if (!stub_properties_.GetListWithoutPathExpansion(
           shill::kAvailableTechnologiesProperty, &enabled_list)) {
-    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  std::move(callback));
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(error_callback, "StubError", "Property not found"));
+        FROM_HERE, base::BindOnce(std::move(error_callback), "StubError",
+                                  "Property not found"));
     return;
   }
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&FakeShillManagerClient::SetTechnologyEnabled,
-                     weak_ptr_factory_.GetWeakPtr(), type, callback, true),
+                     weak_ptr_factory_.GetWeakPtr(), type, std::move(callback),
+                     true),
       interactive_delay_);
 }
 
-void FakeShillManagerClient::DisableTechnology(
-    const std::string& type,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+void FakeShillManagerClient::DisableTechnology(const std::string& type,
+                                               base::OnceClosure callback,
+                                               ErrorCallback error_callback) {
   base::ListValue* enabled_list = nullptr;
   if (!stub_properties_.GetListWithoutPathExpansion(
           shill::kAvailableTechnologiesProperty, &enabled_list)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE,
-        base::BindOnce(error_callback, "StubError", "Property not found"));
+        FROM_HERE, base::BindOnce(std::move(error_callback), "StubError",
+                                  "Property not found"));
     return;
   }
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE,
       base::BindOnce(&FakeShillManagerClient::SetTechnologyEnabled,
-                     weak_ptr_factory_.GetWeakPtr(), type, callback, false),
+                     weak_ptr_factory_.GetWeakPtr(), type, std::move(callback),
+                     false),
       interactive_delay_);
 }
 
 void FakeShillManagerClient::ConfigureService(
     const base::DictionaryValue& properties,
     ObjectPathCallback callback,
-    const ErrorCallback& error_callback) {
+    ErrorCallback error_callback) {
   switch (simulate_configuration_result_) {
     case FakeShillSimulatedResult::kSuccess:
       break;
     case FakeShillSimulatedResult::kFailure:
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::BindOnce(error_callback, "Error", "Simulated failure"));
+          FROM_HERE, base::BindOnce(std::move(error_callback), "Error",
+                                    "Simulated failure"));
       return;
     case FakeShillSimulatedResult::kTimeout:
       // No callbacks get executed and the caller should eventually timeout.
@@ -437,31 +439,32 @@
     const dbus::ObjectPath& profile_path,
     const base::DictionaryValue& properties,
     ObjectPathCallback callback,
-    const ErrorCallback& error_callback) {
+    ErrorCallback error_callback) {
   std::string profile_property;
   properties.GetStringWithoutPathExpansion(shill::kProfileProperty,
                                            &profile_property);
   CHECK(profile_property == profile_path.value());
-  ConfigureService(properties, std::move(callback), error_callback);
+  ConfigureService(properties, std::move(callback), std::move(error_callback));
 }
 
 void FakeShillManagerClient::GetService(const base::DictionaryValue& properties,
                                         ObjectPathCallback callback,
-                                        const ErrorCallback& error_callback) {
+                                        ErrorCallback error_callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), dbus::ObjectPath()));
 }
 
 void FakeShillManagerClient::ConnectToBestServices(
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   if (best_service_.empty()) {
     VLOG(1) << "No 'best' service set.";
     return;
   }
 
-  ShillServiceClient::Get()->Connect(dbus::ObjectPath(best_service_), callback,
-                                     error_callback);
+  ShillServiceClient::Get()->Connect(dbus::ObjectPath(best_service_),
+                                     std::move(callback),
+                                     std::move(error_callback));
 }
 
 ShillManagerClient::TestInterface* FakeShillManagerClient::GetTestInterface() {
@@ -558,7 +561,7 @@
 
 void FakeShillManagerClient::SetManagerProperty(const std::string& key,
                                                 const base::Value& value) {
-  SetProperty(key, value, base::DoNothing(), base::Bind(&LogErrorCallback));
+  SetProperty(key, value, base::DoNothing(), base::BindOnce(&LogErrorCallback));
 }
 
 void FakeShillManagerClient::AddManagerService(const std::string& service_path,
@@ -690,10 +693,10 @@
 
 void FakeShillManagerClient::SetNetworkThrottlingStatus(
     const NetworkThrottlingStatus& status,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   network_throttling_status_ = status;
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 bool FakeShillManagerClient::GetFastTransitionStatus() {
@@ -847,7 +850,7 @@
                                    base::Value(shill::kSecurityNone));
       services->SetConnectBehavior(
           kPortaledWifiPath,
-          base::Bind(&UpdatePortaledWifiState, kPortaledWifiPath));
+          base::BindRepeating(&UpdatePortaledWifiState, kPortaledWifiPath));
       services->SetServiceProperty(
           kPortaledWifiPath, shill::kConnectableProperty, base::Value(true));
       profiles->AddService(shared_profile, kPortaledWifiPath);
@@ -1011,18 +1014,18 @@
 // Private methods
 
 void FakeShillManagerClient::PassStubProperties(
-    const DictionaryValueCallback& callback) const {
+    DictionaryValueCallback callback) const {
   std::unique_ptr<base::DictionaryValue> stub_properties(
       stub_properties_.DeepCopy());
   stub_properties->SetWithoutPathExpansion(
       shill::kServiceCompleteListProperty,
       GetEnabledServiceList(shill::kServiceCompleteListProperty));
-  callback.Run(DBUS_METHOD_CALL_SUCCESS, *stub_properties);
+  std::move(callback).Run(DBUS_METHOD_CALL_SUCCESS, *stub_properties);
 }
 
 void FakeShillManagerClient::PassStubGeoNetworks(
-    const DictionaryValueCallback& callback) const {
-  callback.Run(DBUS_METHOD_CALL_SUCCESS, stub_geo_networks_);
+    DictionaryValueCallback callback) const {
+  std::move(callback).Run(DBUS_METHOD_CALL_SUCCESS, stub_geo_networks_);
 }
 
 void FakeShillManagerClient::CallNotifyObserversPropertyChanged(
@@ -1083,7 +1086,7 @@
 }
 
 void FakeShillManagerClient::SetTechnologyEnabled(const std::string& type,
-                                                  const base::Closure& callback,
+                                                  base::OnceClosure callback,
                                                   bool enabled) {
   base::ListValue* enabled_list =
       GetListProperty(shill::kEnabledTechnologiesProperty);
@@ -1092,7 +1095,7 @@
   else
     enabled_list->Remove(base::Value(type), nullptr);
   CallNotifyObserversPropertyChanged(shill::kEnabledTechnologiesProperty);
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
   // May affect available services.
   SortManagerServices(true);
 }
diff --git a/chromeos/dbus/shill/fake_shill_manager_client.h b/chromeos/dbus/shill/fake_shill_manager_client.h
index 26aa5b3..0d8851a 100644
--- a/chromeos/dbus/shill/fake_shill_manager_client.h
+++ b/chromeos/dbus/shill/fake_shill_manager_client.h
@@ -32,37 +32,36 @@
       ShillPropertyChangedObserver* observer) override;
   void RemovePropertyChangedObserver(
       ShillPropertyChangedObserver* observer) override;
-  void GetProperties(const DictionaryValueCallback& callback) override;
-  void GetNetworksForGeolocation(
-      const DictionaryValueCallback& callback) override;
+  void GetProperties(DictionaryValueCallback callback) override;
+  void GetNetworksForGeolocation(DictionaryValueCallback callback) override;
   void SetProperty(const std::string& name,
                    const base::Value& value,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override;
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override;
   void RequestScan(const std::string& type,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override;
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override;
   void EnableTechnology(const std::string& type,
-                        const base::Closure& callback,
-                        const ErrorCallback& error_callback) override;
+                        base::OnceClosure callback,
+                        ErrorCallback error_callback) override;
   void DisableTechnology(const std::string& type,
-                         const base::Closure& callback,
-                         const ErrorCallback& error_callback) override;
+                         base::OnceClosure callback,
+                         ErrorCallback error_callback) override;
   void ConfigureService(const base::DictionaryValue& properties,
                         ObjectPathCallback callback,
-                        const ErrorCallback& error_callback) override;
+                        ErrorCallback error_callback) override;
   void ConfigureServiceForProfile(const dbus::ObjectPath& profile_path,
                                   const base::DictionaryValue& properties,
                                   ObjectPathCallback callback,
-                                  const ErrorCallback& error_callback) override;
+                                  ErrorCallback error_callback) override;
   void GetService(const base::DictionaryValue& properties,
                   ObjectPathCallback callback,
-                  const ErrorCallback& error_callback) override;
-  void ConnectToBestServices(const base::Closure& callback,
-                             const ErrorCallback& error_callback) override;
+                  ErrorCallback error_callback) override;
+  void ConnectToBestServices(base::OnceClosure callback,
+                             ErrorCallback error_callback) override;
   void SetNetworkThrottlingStatus(const NetworkThrottlingStatus& status,
-                                  const base::Closure& callback,
-                                  const ErrorCallback& error_callback) override;
+                                  base::OnceClosure callback,
+                                  ErrorCallback error_callback) override;
 
   ShillManagerClient::TestInterface* GetTestInterface() override;
 
@@ -101,14 +100,14 @@
 
  private:
   void SetDefaultProperties();
-  void PassStubProperties(const DictionaryValueCallback& callback) const;
-  void PassStubGeoNetworks(const DictionaryValueCallback& callback) const;
+  void PassStubProperties(DictionaryValueCallback callback) const;
+  void PassStubGeoNetworks(DictionaryValueCallback callback) const;
   void CallNotifyObserversPropertyChanged(const std::string& property);
   void NotifyObserversPropertyChanged(const std::string& property);
   base::ListValue* GetListProperty(const std::string& property);
   bool TechnologyEnabled(const std::string& type) const;
   void SetTechnologyEnabled(const std::string& type,
-                            const base::Closure& callback,
+                            base::OnceClosure callback,
                             bool enabled);
   std::unique_ptr<base::ListValue> GetEnabledServiceList(
       const std::string& property) const;
diff --git a/chromeos/dbus/shill/fake_shill_profile_client.cc b/chromeos/dbus/shill/fake_shill_profile_client.cc
index 64ef43d..5176e0e 100644
--- a/chromeos/dbus/shill/fake_shill_profile_client.cc
+++ b/chromeos/dbus/shill/fake_shill_profile_client.cc
@@ -33,9 +33,9 @@
 namespace {
 
 void PassDictionary(
-    const ShillProfileClient::DictionaryValueCallbackWithoutStatus& callback,
+    ShillProfileClient::DictionaryValueCallbackWithoutStatus callback,
     const base::DictionaryValue* dictionary) {
-  callback.Run(*dictionary);
+  std::move(callback).Run(*dictionary);
 }
 
 }  // namespace
@@ -54,11 +54,13 @@
 
 void FakeShillProfileClient::GetProperties(
     const dbus::ObjectPath& profile_path,
-    const DictionaryValueCallbackWithoutStatus& callback,
-    const ErrorCallback& error_callback) {
-  ProfileProperties* profile = GetProfile(profile_path, error_callback);
-  if (!profile)
+    DictionaryValueCallbackWithoutStatus callback,
+    ErrorCallback error_callback) {
+  ProfileProperties* profile = GetProfile(profile_path);
+  if (!profile) {
+    std::move(error_callback).Run("Error.InvalidProfile", "Invalid profile");
     return;
+  }
 
   auto entry_paths = std::make_unique<base::ListValue>();
   for (base::DictionaryValue::Iterator it(profile->entries); !it.IsAtEnd();
@@ -72,60 +74,64 @@
                                       std::move(entry_paths));
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&PassDictionary, callback,
+      FROM_HERE, base::BindOnce(&PassDictionary, std::move(callback),
                                 base::Owned(properties.release())));
 }
 
 void FakeShillProfileClient::GetEntry(
     const dbus::ObjectPath& profile_path,
     const std::string& entry_path,
-    const DictionaryValueCallbackWithoutStatus& callback,
-    const ErrorCallback& error_callback) {
-  ProfileProperties* profile = GetProfile(profile_path, error_callback);
-  if (!profile)
+    DictionaryValueCallbackWithoutStatus callback,
+    ErrorCallback error_callback) {
+  ProfileProperties* profile = GetProfile(profile_path);
+  if (!profile) {
+    std::move(error_callback).Run("Error.InvalidProfile", "Invalid profile");
     return;
+  }
 
   base::DictionaryValue* entry = nullptr;
   profile->entries.GetDictionaryWithoutPathExpansion(entry_path, &entry);
   if (!entry) {
-    error_callback.Run("Error.InvalidProfileEntry", "Invalid profile entry");
+    std::move(error_callback)
+        .Run("Error.InvalidProfileEntry", "Invalid profile entry");
     return;
   }
 
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&PassDictionary, callback,
+      FROM_HERE, base::BindOnce(&PassDictionary, std::move(callback),
                                 base::Owned(entry->DeepCopy())));
 }
 
 void FakeShillProfileClient::DeleteEntry(const dbus::ObjectPath& profile_path,
                                          const std::string& entry_path,
-                                         const base::Closure& callback,
-                                         const ErrorCallback& error_callback) {
+                                         base::OnceClosure callback,
+                                         ErrorCallback error_callback) {
   switch (simulate_delete_result_) {
     case FakeShillSimulatedResult::kSuccess:
       break;
     case FakeShillSimulatedResult::kFailure:
       base::ThreadTaskRunnerHandle::Get()->PostTask(
-          FROM_HERE,
-          base::BindOnce(error_callback, "Error", "Simulated failure"));
+          FROM_HERE, base::BindOnce(std::move(error_callback), "Error",
+                                    "Simulated failure"));
       return;
     case FakeShillSimulatedResult::kTimeout:
       // No callbacks get executed and the caller should eventually timeout.
       return;
   }
 
-  ProfileProperties* profile = GetProfile(profile_path, error_callback);
+  ProfileProperties* profile = GetProfile(profile_path);
   if (!profile) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(error_callback, "Error.InvalidProfile",
-                                  profile_path.value()));
+        FROM_HERE,
+        base::BindOnce(std::move(error_callback), "Error.InvalidProfile",
+                       profile_path.value()));
     return;
   }
 
   if (!profile->entries.RemoveWithoutPathExpansion(entry_path, nullptr)) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(error_callback, "Error.InvalidProfileEntry",
-                                  entry_path));
+        FROM_HERE, base::BindOnce(std::move(error_callback),
+                                  "Error.InvalidProfileEntry", entry_path));
     return;
   }
 
@@ -133,7 +139,7 @@
       ->GetTestInterface()
       ->ClearConfiguredServiceProperties(entry_path);
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 ShillProfileClient::TestInterface* FakeShillProfileClient::GetTestInterface() {
@@ -142,7 +148,7 @@
 
 void FakeShillProfileClient::AddProfile(const std::string& profile_path,
                                         const std::string& userhash) {
-  if (GetProfile(dbus::ObjectPath(profile_path), ErrorCallback()))
+  if (GetProfile(dbus::ObjectPath(profile_path)))
     return;
 
   // If adding a shared profile, make sure there are no user profiles currently
@@ -161,8 +167,7 @@
 void FakeShillProfileClient::AddEntry(const std::string& profile_path,
                                       const std::string& entry_path,
                                       const base::DictionaryValue& properties) {
-  ProfileProperties* profile =
-      GetProfile(dbus::ObjectPath(profile_path), ErrorCallback());
+  ProfileProperties* profile = GetProfile(dbus::ObjectPath(profile_path));
   DCHECK(profile);
   profile->entries.SetKey(entry_path, properties.Clone());
   ShillManagerClient::Get()->GetTestInterface()->AddManagerService(entry_path,
@@ -171,8 +176,7 @@
 
 bool FakeShillProfileClient::AddService(const std::string& profile_path,
                                         const std::string& service_path) {
-  ProfileProperties* profile =
-      GetProfile(dbus::ObjectPath(profile_path), ErrorCallback());
+  ProfileProperties* profile = GetProfile(dbus::ObjectPath(profile_path));
   if (!profile) {
     LOG(ERROR) << "AddService: No matching profile: " << profile_path
                << " for: " << service_path;
@@ -185,8 +189,7 @@
 
 bool FakeShillProfileClient::UpdateService(const std::string& profile_path,
                                            const std::string& service_path) {
-  ProfileProperties* profile =
-      GetProfile(dbus::ObjectPath(profile_path), ErrorCallback());
+  ProfileProperties* profile = GetProfile(dbus::ObjectPath(profile_path));
   if (!profile) {
     LOG(ERROR) << "UpdateService: No matching profile: " << profile_path
                << " for: " << service_path;
@@ -287,14 +290,11 @@
 }
 
 FakeShillProfileClient::ProfileProperties* FakeShillProfileClient::GetProfile(
-    const dbus::ObjectPath& profile_path,
-    const ErrorCallback& error_callback) {
+    const dbus::ObjectPath& profile_path) {
   for (auto& profile : profiles_) {
     if (profile->path == profile_path.value())
       return profile.get();
   }
-  if (!error_callback.is_null())
-    error_callback.Run("Error.InvalidProfile", "Invalid profile");
   return nullptr;
 }
 
diff --git a/chromeos/dbus/shill/fake_shill_profile_client.h b/chromeos/dbus/shill/fake_shill_profile_client.h
index 91c28c06..4724b63b 100644
--- a/chromeos/dbus/shill/fake_shill_profile_client.h
+++ b/chromeos/dbus/shill/fake_shill_profile_client.h
@@ -32,16 +32,16 @@
       const dbus::ObjectPath& profile_path,
       ShillPropertyChangedObserver* observer) override;
   void GetProperties(const dbus::ObjectPath& profile_path,
-                     const DictionaryValueCallbackWithoutStatus& callback,
-                     const ErrorCallback& error_callback) override;
+                     DictionaryValueCallbackWithoutStatus callback,
+                     ErrorCallback error_callback) override;
   void GetEntry(const dbus::ObjectPath& profile_path,
                 const std::string& entry_path,
-                const DictionaryValueCallbackWithoutStatus& callback,
-                const ErrorCallback& error_callback) override;
+                DictionaryValueCallbackWithoutStatus callback,
+                ErrorCallback error_callback) override;
   void DeleteEntry(const dbus::ObjectPath& profile_path,
                    const std::string& entry_path,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override;
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override;
   ShillProfileClient::TestInterface* GetTestInterface() override;
 
   // ShillProfileClient::TestInterface overrides.
@@ -72,8 +72,7 @@
                               const std::string& service_path,
                               ProfileProperties* profile);
 
-  ProfileProperties* GetProfile(const dbus::ObjectPath& profile_path,
-                                const ErrorCallback& error_callback);
+  ProfileProperties* GetProfile(const dbus::ObjectPath& profile_path);
 
   // List of profiles known to the client in order they were added, and in the
   // reverse order of priority.
diff --git a/chromeos/dbus/shill/fake_shill_service_client.cc b/chromeos/dbus/shill/fake_shill_service_client.cc
index 1723a7ad..eed2205 100644
--- a/chromeos/dbus/shill/fake_shill_service_client.cc
+++ b/chromeos/dbus/shill/fake_shill_service_client.cc
@@ -31,10 +31,10 @@
 namespace {
 
 void PassStubServiceProperties(
-    const ShillServiceClient::DictionaryValueCallback& callback,
+    ShillServiceClient::DictionaryValueCallback callback,
     DBusMethodCallStatus call_status,
     const base::DictionaryValue* properties) {
-  callback.Run(call_status, *properties);
+  std::move(callback).Run(call_status, *properties);
 }
 
 void CallSortManagerServices() {
@@ -163,9 +163,8 @@
   GetObserverList(service_path).RemoveObserver(observer);
 }
 
-void FakeShillServiceClient::GetProperties(
-    const dbus::ObjectPath& service_path,
-    const DictionaryValueCallback& callback) {
+void FakeShillServiceClient::GetProperties(const dbus::ObjectPath& service_path,
+                                           DictionaryValueCallback callback) {
   base::DictionaryValue* nested_dict = nullptr;
   std::unique_ptr<base::DictionaryValue> result_properties;
   DBusMethodCallStatus call_status;
@@ -185,8 +184,8 @@
   }
 
   base::OnceClosure property_update =
-      base::BindOnce(&PassStubServiceProperties, callback, call_status,
-                     base::Owned(result_properties.release()));
+      base::BindOnce(&PassStubServiceProperties, std::move(callback),
+                     call_status, base::Owned(result_properties.release()));
   if (hold_back_service_property_updates_)
     recorded_property_updates_.push_back(std::move(property_update));
   else
@@ -197,57 +196,56 @@
 void FakeShillServiceClient::SetProperty(const dbus::ObjectPath& service_path,
                                          const std::string& name,
                                          const base::Value& value,
-                                         const base::Closure& callback,
-                                         const ErrorCallback& error_callback) {
+                                         base::OnceClosure callback,
+                                         ErrorCallback error_callback) {
   if (!SetServiceProperty(service_path.value(), name, value)) {
     LOG(ERROR) << "Service not found: " << service_path.value();
-    error_callback.Run("Error.InvalidService", "Invalid Service");
+    std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
     return;
   }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillServiceClient::SetProperties(
     const dbus::ObjectPath& service_path,
     const base::DictionaryValue& properties,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd();
        iter.Advance()) {
     if (!SetServiceProperty(service_path.value(), iter.key(), iter.value())) {
       LOG(ERROR) << "Service not found: " << service_path.value();
-      error_callback.Run("Error.InvalidService", "Invalid Service");
+      std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
       return;
     }
   }
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
-void FakeShillServiceClient::ClearProperty(
-    const dbus::ObjectPath& service_path,
-    const std::string& name,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+void FakeShillServiceClient::ClearProperty(const dbus::ObjectPath& service_path,
+                                           const std::string& name,
+                                           base::OnceClosure callback,
+                                           ErrorCallback error_callback) {
   base::DictionaryValue* dict = nullptr;
   if (!stub_services_.GetDictionaryWithoutPathExpansion(service_path.value(),
                                                         &dict)) {
-    error_callback.Run("Error.InvalidService", "Invalid Service");
+    std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
     return;
   }
   dict->RemoveWithoutPathExpansion(name, nullptr);
   // Note: Shill does not send notifications when properties are cleared.
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillServiceClient::ClearProperties(
     const dbus::ObjectPath& service_path,
     const std::vector<std::string>& names,
-    const ListValueCallback& callback,
-    const ErrorCallback& error_callback) {
+    ListValueCallback callback,
+    ErrorCallback error_callback) {
   base::Value* dict = stub_services_.FindKeyOfType(
       service_path.value(), base::Value::Type::DICTIONARY);
   if (!dict) {
-    error_callback.Run("Error.InvalidService", "Invalid Service");
+    std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
     return;
   }
 
@@ -257,18 +255,18 @@
     result.AppendBoolean(dict->RemoveKey(name));
   }
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(callback, std::move(result)));
+      FROM_HERE, base::BindOnce(std::move(callback), std::move(result)));
 }
 
 void FakeShillServiceClient::Connect(const dbus::ObjectPath& service_path,
-                                     const base::Closure& callback,
-                                     const ErrorCallback& error_callback) {
+                                     base::OnceClosure callback,
+                                     ErrorCallback error_callback) {
   VLOG(1) << "FakeShillServiceClient::Connect: " << service_path.value();
   base::DictionaryValue* service_properties = nullptr;
   if (!stub_services_.GetDictionary(service_path.value(),
                                     &service_properties)) {
     LOG(ERROR) << "Service not found: " << service_path.value();
-    error_callback.Run("Error.InvalidService", "Invalid Service");
+    std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
     return;
   }
 
@@ -292,15 +290,15 @@
                      weak_ptr_factory_.GetWeakPtr(), service_path.value()),
       GetInteractiveDelay());
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillServiceClient::Disconnect(const dbus::ObjectPath& service_path,
-                                        const base::Closure& callback,
-                                        const ErrorCallback& error_callback) {
+                                        base::OnceClosure callback,
+                                        ErrorCallback error_callback) {
   base::Value* service;
   if (!stub_services_.Get(service_path.value(), &service)) {
-    error_callback.Run("Error.InvalidService", "Invalid Service");
+    std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
     return;
   }
   // Set Idle after a delay
@@ -309,27 +307,27 @@
       base::BindOnce(&FakeShillServiceClient::SetProperty,
                      weak_ptr_factory_.GetWeakPtr(), service_path,
                      shill::kStateProperty, base::Value(shill::kStateIdle),
-                     base::DoNothing(), error_callback),
+                     base::DoNothing::Once(), std::move(error_callback)),
       GetInteractiveDelay());
-  callback.Run();
+  std::move(callback).Run();
 }
 
 void FakeShillServiceClient::Remove(const dbus::ObjectPath& service_path,
-                                    const base::Closure& callback,
-                                    const ErrorCallback& error_callback) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+                                    base::OnceClosure callback,
+                                    ErrorCallback error_callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillServiceClient::ActivateCellularModem(
     const dbus::ObjectPath& service_path,
     const std::string& carrier,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   base::DictionaryValue* service_properties =
       GetModifiableServiceProperties(service_path.value(), false);
   if (!service_properties) {
     LOG(ERROR) << "Service not found: " << service_path.value();
-    error_callback.Run("Error.InvalidService", "Invalid Service");
+    std::move(error_callback).Run("Error.InvalidService", "Invalid Service");
   }
   SetServiceProperty(service_path.value(), shill::kActivationStateProperty,
                      base::Value(shill::kActivationStateActivating));
@@ -338,22 +336,22 @@
       FROM_HERE,
       base::BindOnce(&FakeShillServiceClient::SetCellularActivated,
                      weak_ptr_factory_.GetWeakPtr(), service_path,
-                     error_callback),
+                     std::move(error_callback)),
       GetInteractiveDelay());
 
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillServiceClient::CompleteCellularActivation(
     const dbus::ObjectPath& service_path,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillServiceClient::GetLoadableProfileEntries(
     const dbus::ObjectPath& service_path,
-    const DictionaryValueCallback& callback) {
+    DictionaryValueCallback callback) {
   ShillProfileClient::TestInterface* profile_client =
       ShillProfileClient::Get()->GetTestInterface();
   std::vector<std::string> profiles;
@@ -371,8 +369,8 @@
   DBusMethodCallStatus call_status = DBUS_METHOD_CALL_SUCCESS;
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE,
-      base::BindOnce(&PassStubServiceProperties, callback, call_status,
-                     base::Owned(result_properties.release())));
+      base::BindOnce(&PassStubServiceProperties, std::move(callback),
+                     call_status, base::Owned(result_properties.release())));
 }
 
 ShillServiceClient::TestInterface* FakeShillServiceClient::GetTestInterface() {
@@ -664,8 +662,9 @@
   connect_behavior_.clear();
 }
 
-void FakeShillServiceClient::SetConnectBehavior(const std::string& service_path,
-                                                const base::Closure& behavior) {
+void FakeShillServiceClient::SetConnectBehavior(
+    const std::string& service_path,
+    const base::RepeatingClosure& behavior) {
   connect_behavior_[service_path] = behavior;
 }
 
@@ -753,12 +752,12 @@
 
 void FakeShillServiceClient::SetCellularActivated(
     const dbus::ObjectPath& service_path,
-    const ErrorCallback& error_callback) {
+    ErrorCallback error_callback) {
   SetProperty(service_path, shill::kActivationStateProperty,
               base::Value(shill::kActivationStateActivated), base::DoNothing(),
-              error_callback);
+              std::move(error_callback));
   SetProperty(service_path, shill::kConnectableProperty, base::Value(true),
-              base::DoNothing(), error_callback);
+              base::DoNothing(), std::move(error_callback));
 }
 
 void FakeShillServiceClient::ContinueConnect(const std::string& service_path) {
@@ -770,7 +769,7 @@
   }
 
   if (base::Contains(connect_behavior_, service_path)) {
-    const base::Closure& custom_connect_behavior =
+    const base::RepeatingClosure& custom_connect_behavior =
         connect_behavior_[service_path];
     VLOG(1) << "Running custom connect behavior for " << service_path;
     custom_connect_behavior.Run();
diff --git a/chromeos/dbus/shill/fake_shill_service_client.h b/chromeos/dbus/shill/fake_shill_service_client.h
index f49f3c7..d954d1a 100644
--- a/chromeos/dbus/shill/fake_shill_service_client.h
+++ b/chromeos/dbus/shill/fake_shill_service_client.h
@@ -35,43 +35,42 @@
       const dbus::ObjectPath& service_path,
       ShillPropertyChangedObserver* observer) override;
   void GetProperties(const dbus::ObjectPath& service_path,
-                     const DictionaryValueCallback& callback) override;
+                     DictionaryValueCallback callback) override;
   void SetProperty(const dbus::ObjectPath& service_path,
                    const std::string& name,
                    const base::Value& value,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override;
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override;
   void SetProperties(const dbus::ObjectPath& service_path,
                      const base::DictionaryValue& properties,
-                     const base::Closure& callback,
-                     const ErrorCallback& error_callback) override;
+                     base::OnceClosure callback,
+                     ErrorCallback error_callback) override;
   void ClearProperty(const dbus::ObjectPath& service_path,
                      const std::string& name,
-                     const base::Closure& callback,
-                     const ErrorCallback& error_callback) override;
+                     base::OnceClosure callback,
+                     ErrorCallback error_callback) override;
   void ClearProperties(const dbus::ObjectPath& service_path,
                        const std::vector<std::string>& names,
-                       const ListValueCallback& callback,
-                       const ErrorCallback& error_callback) override;
+                       ListValueCallback callback,
+                       ErrorCallback error_callback) override;
   void Connect(const dbus::ObjectPath& service_path,
-               const base::Closure& callback,
-               const ErrorCallback& error_callback) override;
+               base::OnceClosure callback,
+               ErrorCallback error_callback) override;
   void Disconnect(const dbus::ObjectPath& service_path,
-                  const base::Closure& callback,
-                  const ErrorCallback& error_callback) override;
+                  base::OnceClosure callback,
+                  ErrorCallback error_callback) override;
   void Remove(const dbus::ObjectPath& service_path,
-              const base::Closure& callback,
-              const ErrorCallback& error_callback) override;
+              base::OnceClosure callback,
+              ErrorCallback error_callback) override;
   void ActivateCellularModem(const dbus::ObjectPath& service_path,
                              const std::string& carrier,
-                             const base::Closure& callback,
-                             const ErrorCallback& error_callback) override;
+                             base::OnceClosure callback,
+                             ErrorCallback error_callback) override;
   void CompleteCellularActivation(const dbus::ObjectPath& service_path,
-                                  const base::Closure& callback,
-                                  const ErrorCallback& error_callback) override;
-  void GetLoadableProfileEntries(
-      const dbus::ObjectPath& service_path,
-      const DictionaryValueCallback& callback) override;
+                                  base::OnceClosure callback,
+                                  ErrorCallback error_callback) override;
+  void GetLoadableProfileEntries(const dbus::ObjectPath& service_path,
+                                 DictionaryValueCallback callback) override;
   ShillServiceClient::TestInterface* GetTestInterface() override;
 
   // ShillServiceClient::TestInterface overrides.
@@ -107,7 +106,7 @@
       const base::Value& template_service_properties) override;
   void ClearServices() override;
   void SetConnectBehavior(const std::string& service_path,
-                          const base::Closure& behavior) override;
+                          const base::RepeatingClosure& behavior) override;
   void SetHoldBackServicePropertyUpdates(bool hold_back) override;
 
  private:
@@ -122,7 +121,7 @@
   PropertyObserverList& GetObserverList(const dbus::ObjectPath& device_path);
   void SetOtherServicesOffline(const std::string& service_path);
   void SetCellularActivated(const dbus::ObjectPath& service_path,
-                            const ErrorCallback& error_callback);
+                            ErrorCallback error_callback);
   void ContinueConnect(const std::string& service_path);
 
   base::DictionaryValue stub_services_;
@@ -130,7 +129,7 @@
   // Per network service, stores a closure that is executed on each connection
   // attempt. The callback can for example modify the services properties in
   // order to simulate a connection failure.
-  std::map<std::string, base::Closure> connect_behavior_;
+  std::map<std::string, base::RepeatingClosure> connect_behavior_;
 
   // Observer list for each service.
   std::map<dbus::ObjectPath, std::unique_ptr<PropertyObserverList>>
diff --git a/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.cc b/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.cc
index 944d5be..09abb8b 100644
--- a/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.cc
+++ b/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.cc
@@ -43,26 +43,26 @@
 void FakeShillThirdPartyVpnDriverClient::SetParameters(
     const std::string& object_path_value,
     const base::DictionaryValue& parameters,
-    const ShillClientHelper::StringCallback& callback,
-    const ShillClientHelper::ErrorCallback& error_callback) {
+    StringCallback callback,
+    ErrorCallback error_callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(callback, std::string()));
+      FROM_HERE, base::BindOnce(std::move(callback), std::string()));
 }
 
 void FakeShillThirdPartyVpnDriverClient::UpdateConnectionState(
     const std::string& object_path_value,
     const uint32_t connection_state,
-    const base::Closure& callback,
-    const ShillClientHelper::ErrorCallback& error_callback) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillThirdPartyVpnDriverClient::SendPacket(
     const std::string& object_path_value,
     const std::vector<char>& ip_packet,
-    const base::Closure& callback,
-    const ShillClientHelper::ErrorCallback& error_callback) {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(callback));
 }
 
 void FakeShillThirdPartyVpnDriverClient::OnPacketReceived(
diff --git a/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h b/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h
index 0a8d041..8207894 100644
--- a/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h
+++ b/chromeos/dbus/shill/fake_shill_third_party_vpn_driver_client.h
@@ -34,21 +34,18 @@
       ShillThirdPartyVpnObserver* observer) override;
   void RemoveShillThirdPartyVpnObserver(
       const std::string& object_path_value) override;
-  void SetParameters(
-      const std::string& object_path_value,
-      const base::DictionaryValue& parameters,
-      const ShillClientHelper::StringCallback& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) override;
-  void UpdateConnectionState(
-      const std::string& object_path_value,
-      const uint32_t connection_state,
-      const base::Closure& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) override;
-  void SendPacket(
-      const std::string& object_path_value,
-      const std::vector<char>& ip_packet,
-      const base::Closure& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) override;
+  void SetParameters(const std::string& object_path_value,
+                     const base::DictionaryValue& parameters,
+                     StringCallback callback,
+                     ErrorCallback error_callback) override;
+  void UpdateConnectionState(const std::string& object_path_value,
+                             const uint32_t connection_state,
+                             base::OnceClosure callback,
+                             ErrorCallback error_callback) override;
+  void SendPacket(const std::string& object_path_value,
+                  const std::vector<char>& ip_packet,
+                  base::OnceClosure callback,
+                  ErrorCallback error_callback) override;
   ShillThirdPartyVpnDriverClient::TestInterface* GetTestInterface() override;
 
   // ShillThirdPartyVpnDriverClient::TestInterface overrides
diff --git a/chromeos/dbus/shill/modem_messaging_client.cc b/chromeos/dbus/shill/modem_messaging_client.cc
index 8cf7209..ea0d2fe4 100644
--- a/chromeos/dbus/shill/modem_messaging_client.cc
+++ b/chromeos/dbus/shill/modem_messaging_client.cc
@@ -39,8 +39,8 @@
     proxy_->ConnectToSignal(
         modemmanager::kModemManager1MessagingInterface,
         modemmanager::kSMSAddedSignal,
-        base::Bind(&ModemMessagingProxy::OnSmsAdded,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&ModemMessagingProxy::OnSmsAdded,
+                            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&ModemMessagingProxy::OnSignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
   }
diff --git a/chromeos/dbus/shill/modem_messaging_client.h b/chromeos/dbus/shill/modem_messaging_client.h
index 4f1073a..83e4da78 100644
--- a/chromeos/dbus/shill/modem_messaging_client.h
+++ b/chromeos/dbus/shill/modem_messaging_client.h
@@ -26,9 +26,9 @@
 // initializes the DBusThreadManager instance.
 class COMPONENT_EXPORT(SHILL_CLIENT) ModemMessagingClient {
  public:
-  typedef base::Callback<void(const dbus::ObjectPath& message_path,
-                              bool complete)>
-      SmsReceivedHandler;
+  using SmsReceivedHandler =
+      base::RepeatingCallback<void(const dbus::ObjectPath& message_path,
+                                   bool complete)>;
 
   // Creates and initializes the global instance. |bus| must not be null.
   static void Initialize(dbus::Bus* bus);
diff --git a/chromeos/dbus/shill/modem_messaging_client_unittest.cc b/chromeos/dbus/shill/modem_messaging_client_unittest.cc
index c636661..d3665ba 100644
--- a/chromeos/dbus/shill/modem_messaging_client_unittest.cc
+++ b/chromeos/dbus/shill/modem_messaging_client_unittest.cc
@@ -156,7 +156,8 @@
   // Set handler.
   client_->SetSmsReceivedHandler(
       kServiceName, dbus::ObjectPath(kObjectPath),
-      base::Bind(&MockSmsReceivedHandler::Run, base::Unretained(&handler)));
+      base::BindRepeating(&MockSmsReceivedHandler::Run,
+                          base::Unretained(&handler)));
 
   // Run the message loop to run the signal connection result callback.
   base::RunLoop().RunUntilIdle();
diff --git a/chromeos/dbus/shill/shill_client_helper.cc b/chromeos/dbus/shill/shill_client_helper.cc
index fb92241..f378c367 100644
--- a/chromeos/dbus/shill/shill_client_helper.cc
+++ b/chromeos/dbus/shill/shill_client_helper.cc
@@ -34,11 +34,12 @@
   }
   ~RefHolder() {
     // Release the helper on the origin thread.
-    base::Closure closure = base::Bind(&ShillClientHelper::Release, helper_);
+    base::OnceClosure closure =
+        base::BindOnce(&ShillClientHelper::Release, helper_);
     if (origin_task_runner_->BelongsToCurrentThread()) {
-      closure.Run();
+      std::move(closure).Run();
     } else {
-      origin_task_runner_->PostTask(FROM_HERE, closure);
+      origin_task_runner_->PostTask(FROM_HERE, std::move(closure));
     }
   }
 
@@ -57,38 +58,42 @@
 // Callback object once completed.
 void OnBooleanMethodWithErrorCallback(
     ShillClientHelper::RefHolder* ref_holder,
-    const ShillClientHelper::BooleanCallback& callback,
-    const ShillClientHelper::ErrorCallback& error_callback,
+    ShillClientHelper::BooleanCallback callback,
+    ShillClientHelper::ErrorCallback error_callback,
     dbus::Response* response) {
   if (!response) {
-    error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
+    std::move(error_callback)
+        .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
   dbus::MessageReader reader(response);
   bool result;
   if (!reader.PopBool(&result)) {
-    error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
+    std::move(error_callback)
+        .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
-  callback.Run(result);
+  std::move(callback).Run(result);
 }
 
 void OnStringMethodWithErrorCallback(
     ShillClientHelper::RefHolder* ref_holder,
-    const ShillClientHelper::StringCallback& callback,
-    const ShillClientHelper::ErrorCallback& error_callback,
+    ShillClientHelper::StringCallback callback,
+    ShillClientHelper::ErrorCallback error_callback,
     dbus::Response* response) {
   if (!response) {
-    error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
+    std::move(error_callback)
+        .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
   dbus::MessageReader reader(response);
   std::string result;
   if (!reader.PopString(&result)) {
-    error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
+    std::move(error_callback)
+        .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
-  callback.Run(result);
+  std::move(callback).Run(result);
 }
 
 // Handles responses for methods without results.
@@ -102,16 +107,18 @@
 void OnObjectPathMethodWithoutStatus(
     ShillClientHelper::RefHolder* ref_holder,
     ObjectPathCallback callback,
-    const ShillClientHelper::ErrorCallback& error_callback,
+    ShillClientHelper::ErrorCallback error_callback,
     dbus::Response* response) {
   if (!response) {
-    error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
+    std::move(error_callback)
+        .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
   dbus::MessageReader reader(response);
   dbus::ObjectPath result;
   if (!reader.PopObjectPath(&result)) {
-    error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
+    std::move(error_callback)
+        .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
   std::move(callback).Run(result);
@@ -120,11 +127,11 @@
 // Handles responses for methods with DictionaryValue results.
 void OnDictionaryValueMethod(
     ShillClientHelper::RefHolder* ref_holder,
-    const ShillClientHelper::DictionaryValueCallback& callback,
+    ShillClientHelper::DictionaryValueCallback callback,
     dbus::Response* response) {
   if (!response) {
     base::DictionaryValue result;
-    callback.Run(DBUS_METHOD_CALL_FAILURE, result);
+    std::move(callback).Run(DBUS_METHOD_CALL_FAILURE, result);
     return;
   }
   dbus::MessageReader reader(response);
@@ -132,54 +139,56 @@
   base::DictionaryValue* result = NULL;
   if (!value.get() || !value->GetAsDictionary(&result)) {
     base::DictionaryValue result;
-    callback.Run(DBUS_METHOD_CALL_FAILURE, result);
+    std::move(callback).Run(DBUS_METHOD_CALL_FAILURE, result);
     return;
   }
-  callback.Run(DBUS_METHOD_CALL_SUCCESS, *result);
+  std::move(callback).Run(DBUS_METHOD_CALL_SUCCESS, *result);
 }
 
 // Handles responses for methods without results.
 void OnVoidMethodWithErrorCallback(ShillClientHelper::RefHolder* ref_holder,
-                                   const base::Closure& callback,
+                                   base::OnceClosure callback,
                                    dbus::Response* response) {
-  callback.Run();
+  std::move(callback).Run();
 }
 
 // Handles responses for methods with DictionaryValue results.
 // Used by CallDictionaryValueMethodWithErrorCallback().
 void OnDictionaryValueMethodWithErrorCallback(
     ShillClientHelper::RefHolder* ref_holder,
-    const ShillClientHelper::DictionaryValueCallbackWithoutStatus& callback,
-    const ShillClientHelper::ErrorCallback& error_callback,
+    ShillClientHelper::DictionaryValueCallbackWithoutStatus callback,
+    ShillClientHelper::ErrorCallback error_callback,
     dbus::Response* response) {
   dbus::MessageReader reader(response);
   std::unique_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
   base::DictionaryValue* result = NULL;
   if (!value.get() || !value->GetAsDictionary(&result)) {
-    error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
+    std::move(error_callback)
+        .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
-  callback.Run(*result);
+  std::move(callback).Run(*result);
 }
 
 // Handles responses for methods with ListValue results.
 void OnListValueMethodWithErrorCallback(
     ShillClientHelper::RefHolder* ref_holder,
-    const ShillClientHelper::ListValueCallback& callback,
-    const ShillClientHelper::ErrorCallback& error_callback,
+    ShillClientHelper::ListValueCallback callback,
+    ShillClientHelper::ErrorCallback error_callback,
     dbus::Response* response) {
   dbus::MessageReader reader(response);
   std::unique_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
   base::ListValue* result = NULL;
   if (!value.get() || !value->GetAsList(&result)) {
-    error_callback.Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
+    std::move(error_callback)
+        .Run(kInvalidResponseErrorName, kInvalidResponseErrorMessage);
     return;
   }
-  callback.Run(*result);
+  std::move(callback).Run(*result);
 }
 
 // Handles running appropriate error callbacks.
-void OnError(const ShillClientHelper::ErrorCallback& error_callback,
+void OnError(ShillClientHelper::ErrorCallback error_callback,
              dbus::ErrorResponse* response) {
   std::string error_name;
   std::string error_message;
@@ -189,7 +198,7 @@
     error_name = response->GetErrorName();
     reader.PopString(&error_message);
   }
-  error_callback.Run(error_name, error_message);
+  std::move(error_callback).Run(error_name, error_message);
 }
 
 }  // namespace
@@ -204,7 +213,7 @@
 
 void ShillClientHelper::SetReleasedCallback(ReleasedCallback callback) {
   CHECK(released_callback_.is_null());
-  released_callback_ = callback;
+  released_callback_ = std::move(callback);
 }
 
 void ShillClientHelper::AddPropertyChangedObserver(
@@ -244,11 +253,12 @@
     const std::string& interface_name) {
   // We are not using dbus::PropertySet to monitor PropertyChanged signal
   // because the interface is not "org.freedesktop.DBus.Properties".
-  proxy_->ConnectToSignal(interface_name, shill::kMonitorPropertyChanged,
-                          base::Bind(&ShillClientHelper::OnPropertyChanged,
-                                     weak_ptr_factory_.GetWeakPtr()),
-                          base::BindOnce(&ShillClientHelper::OnSignalConnected,
-                                         weak_ptr_factory_.GetWeakPtr()));
+  proxy_->ConnectToSignal(
+      interface_name, shill::kMonitorPropertyChanged,
+      base::BindRepeating(&ShillClientHelper::OnPropertyChanged,
+                          weak_ptr_factory_.GetWeakPtr()),
+      base::BindOnce(&ShillClientHelper::OnSignalConnected,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 void ShillClientHelper::CallVoidMethod(dbus::MethodCall* method_call,
@@ -264,96 +274,106 @@
 void ShillClientHelper::CallObjectPathMethodWithErrorCallback(
     dbus::MethodCall* method_call,
     ObjectPathCallback callback,
-    const ErrorCallback& error_callback) {
+    ErrorCallback error_callback) {
   DCHECK(!callback.is_null());
   DCHECK(!error_callback.is_null());
+  auto error_callback_adapted =
+      base::AdaptCallbackForRepeating(std::move(error_callback));
   proxy_->CallMethodWithErrorCallback(
       method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
       base::BindOnce(&OnObjectPathMethodWithoutStatus,
                      base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
-                     std::move(callback), error_callback),
-      base::BindOnce(&OnError, error_callback));
+                     std::move(callback), error_callback_adapted),
+      base::BindOnce(&OnError, error_callback_adapted));
 }
 
 void ShillClientHelper::CallDictionaryValueMethod(
     dbus::MethodCall* method_call,
-    const DictionaryValueCallback& callback) {
+    DictionaryValueCallback callback) {
   DCHECK(!callback.is_null());
   proxy_->CallMethod(
       method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
       base::BindOnce(&OnDictionaryValueMethod,
                      base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
-                     callback));
+                     std::move(callback)));
 }
 
 void ShillClientHelper::CallVoidMethodWithErrorCallback(
     dbus::MethodCall* method_call,
-    const base::Closure& callback,
-    const ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   DCHECK(!callback.is_null());
   DCHECK(!error_callback.is_null());
   proxy_->CallMethodWithErrorCallback(
       method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
       base::BindOnce(&OnVoidMethodWithErrorCallback,
                      base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
-                     callback),
-      base::BindOnce(&OnError, error_callback));
+                     std::move(callback)),
+      base::BindOnce(&OnError, std::move(error_callback)));
 }
 
 void ShillClientHelper::CallBooleanMethodWithErrorCallback(
     dbus::MethodCall* method_call,
-    const BooleanCallback& callback,
-    const ErrorCallback& error_callback) {
+    BooleanCallback callback,
+    ErrorCallback error_callback) {
   DCHECK(!callback.is_null());
   DCHECK(!error_callback.is_null());
+  auto error_callback_adapted =
+      base::AdaptCallbackForRepeating(std::move(error_callback));
   proxy_->CallMethodWithErrorCallback(
       method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
       base::BindOnce(&OnBooleanMethodWithErrorCallback,
                      base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
-                     callback, error_callback),
-      base::BindOnce(&OnError, error_callback));
+                     std::move(callback), error_callback_adapted),
+      base::BindOnce(&OnError, error_callback_adapted));
 }
 
 void ShillClientHelper::CallStringMethodWithErrorCallback(
     dbus::MethodCall* method_call,
-    const StringCallback& callback,
-    const ErrorCallback& error_callback) {
+    StringCallback callback,
+    ErrorCallback error_callback) {
   DCHECK(!callback.is_null());
   DCHECK(!error_callback.is_null());
+  auto error_callback_adapted =
+      base::AdaptCallbackForRepeating(std::move(error_callback));
   proxy_->CallMethodWithErrorCallback(
       method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
       base::BindOnce(&OnStringMethodWithErrorCallback,
                      base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
-                     callback, error_callback),
-      base::BindOnce(&OnError, error_callback));
+                     std::move(callback), error_callback_adapted),
+      base::BindOnce(&OnError, error_callback_adapted));
 }
 
 void ShillClientHelper::CallDictionaryValueMethodWithErrorCallback(
     dbus::MethodCall* method_call,
-    const DictionaryValueCallbackWithoutStatus& callback,
-    const ErrorCallback& error_callback) {
+    DictionaryValueCallbackWithoutStatus callback,
+    ErrorCallback error_callback) {
   DCHECK(!callback.is_null());
   DCHECK(!error_callback.is_null());
+  auto error_callback_adapted =
+      base::AdaptCallbackForRepeating(std::move(error_callback));
   proxy_->CallMethodWithErrorCallback(
       method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
       base::BindOnce(&OnDictionaryValueMethodWithErrorCallback,
                      base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
-                     callback, error_callback),
-      base::BindOnce(&OnError, error_callback));
+                     std::move(callback), error_callback_adapted),
+      base::BindOnce(&OnError, error_callback_adapted));
 }
 
 void ShillClientHelper::CallListValueMethodWithErrorCallback(
     dbus::MethodCall* method_call,
-    const ListValueCallback& callback,
-    const ErrorCallback& error_callback) {
+    ListValueCallback callback,
+    ErrorCallback error_callback) {
   DCHECK(!callback.is_null());
   DCHECK(!error_callback.is_null());
+  auto error_callback_adapted =
+      base::AdaptCallbackForRepeating(std::move(error_callback));
   proxy_->CallMethodWithErrorCallback(
       method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
       base::BindOnce(&OnListValueMethodWithErrorCallback,
                      base::Owned(new RefHolder(weak_ptr_factory_.GetWeakPtr())),
-                     callback, error_callback),
-      base::BindOnce(&OnError, error_callback));
+                     std::move(callback), error_callback_adapted),
+      base::BindOnce(&OnError, error_callback_adapted));
 }
 
 namespace {
diff --git a/chromeos/dbus/shill/shill_client_helper.h b/chromeos/dbus/shill/shill_client_helper.h
index b8ddbea6d..ef96829 100644
--- a/chromeos/dbus/shill/shill_client_helper.h
+++ b/chromeos/dbus/shill/shill_client_helper.h
@@ -40,37 +40,33 @@
  public:
   class RefHolder;
 
-  // A callback to handle PropertyChanged signals.
-  typedef base::Callback<void(const std::string& name,
-                              const base::Value& value)>
-      PropertyChangedHandler;
-
   // A callback to handle responses for methods with DictionaryValue results.
-  typedef base::Callback<void(DBusMethodCallStatus call_status,
-                              const base::DictionaryValue& result)>
-      DictionaryValueCallback;
+  using DictionaryValueCallback =
+      base::OnceCallback<void(DBusMethodCallStatus call_status,
+                              const base::DictionaryValue& result)>;
 
   // A callback to handle responses for methods with DictionaryValue results.
   // This is used by CallDictionaryValueMethodWithErrorCallback.
-  typedef base::Callback<void(const base::DictionaryValue& result)>
-      DictionaryValueCallbackWithoutStatus;
+  using DictionaryValueCallbackWithoutStatus =
+      base::OnceCallback<void(const base::DictionaryValue& result)>;
 
   // A callback to handle responses of methods returning a ListValue.
-  typedef base::Callback<void(const base::ListValue& result)> ListValueCallback;
+  using ListValueCallback =
+      base::OnceCallback<void(const base::ListValue& result)>;
 
   // A callback to handle errors for method call.
-  typedef base::Callback<void(const std::string& error_name,
-                              const std::string& error_message)>
-      ErrorCallback;
+  using ErrorCallback =
+      base::OnceCallback<void(const std::string& error_name,
+                              const std::string& error_message)>;
 
   // A callback that handles responses for methods with string results.
-  typedef base::Callback<void(const std::string& result)> StringCallback;
+  using StringCallback = base::OnceCallback<void(const std::string& result)>;
 
   // A callback that handles responses for methods with boolean results.
-  typedef base::Callback<void(bool result)> BooleanCallback;
+  using BooleanCallback = base::OnceCallback<void(bool result)>;
 
   // Callback used to notify owner when this can be safely released.
-  typedef base::Callback<void(ShillClientHelper* helper)> ReleasedCallback;
+  using ReleasedCallback = base::OnceCallback<void(ShillClientHelper* helper)>;
 
   explicit ShillClientHelper(dbus::ObjectProxy* proxy);
 
@@ -96,41 +92,39 @@
                       VoidDBusMethodCallback callback);
 
   // Calls a method with an object path result where there is an error callback.
-  void CallObjectPathMethodWithErrorCallback(
-      dbus::MethodCall* method_call,
-      ObjectPathCallback callback,
-      const ErrorCallback& error_callback);
+  void CallObjectPathMethodWithErrorCallback(dbus::MethodCall* method_call,
+                                             ObjectPathCallback callback,
+                                             ErrorCallback error_callback);
 
   // Calls a method with a dictionary value result.
   void CallDictionaryValueMethod(dbus::MethodCall* method_call,
-                                 const DictionaryValueCallback& callback);
+                                 DictionaryValueCallback callback);
 
   // Calls a method without results with error callback.
   void CallVoidMethodWithErrorCallback(dbus::MethodCall* method_call,
-                                       const base::Closure& callback,
-                                       const ErrorCallback& error_callback);
+                                       base::OnceClosure callback,
+                                       ErrorCallback error_callback);
 
   // Calls a method with a boolean result with error callback.
   void CallBooleanMethodWithErrorCallback(dbus::MethodCall* method_call,
-                                          const BooleanCallback& callback,
-                                          const ErrorCallback& error_callback);
+                                          BooleanCallback callback,
+                                          ErrorCallback error_callback);
 
   // Calls a method with a string result with error callback.
   void CallStringMethodWithErrorCallback(dbus::MethodCall* method_call,
-                                         const StringCallback& callback,
-                                         const ErrorCallback& error_callback);
+                                         StringCallback callback,
+                                         ErrorCallback error_callback);
 
   // Calls a method with a dictionary value result with error callback.
   void CallDictionaryValueMethodWithErrorCallback(
       dbus::MethodCall* method_call,
-      const DictionaryValueCallbackWithoutStatus& callback,
-      const ErrorCallback& error_callback);
+      DictionaryValueCallbackWithoutStatus callback,
+      ErrorCallback error_callback);
 
   // Calls a method with a boolean array result with error callback.
-  void CallListValueMethodWithErrorCallback(
-      dbus::MethodCall* method_call,
-      const ListValueCallback& callback,
-      const ErrorCallback& error_callback);
+  void CallListValueMethodWithErrorCallback(dbus::MethodCall* method_call,
+                                            ListValueCallback callback,
+                                            ErrorCallback error_callback);
 
   const dbus::ObjectProxy* object_proxy() const { return proxy_; }
 
@@ -167,7 +161,6 @@
   dbus::ObjectProxy* proxy_;
   ReleasedCallback released_callback_;
   int active_refs_;
-  PropertyChangedHandler property_changed_handler_;
   base::ObserverList<ShillPropertyChangedObserver,
                      true /* check_empty */>::Unchecked observer_list_;
   std::vector<std::string> interfaces_to_be_monitored_;
diff --git a/chromeos/dbus/shill/shill_client_unittest_base.h b/chromeos/dbus/shill/shill_client_unittest_base.h
index f6f813c..2f199f7 100644
--- a/chromeos/dbus/shill/shill_client_unittest_base.h
+++ b/chromeos/dbus/shill/shill_client_unittest_base.h
@@ -82,8 +82,8 @@
 
  protected:
   // A callback to intercept and check the method call arguments.
-  typedef base::Callback<void(dbus::MessageReader* reader)>
-      ArgumentCheckCallback;
+  using ArgumentCheckCallback =
+      base::RepeatingCallback<void(dbus::MessageReader* reader)>;
 
   // Sets expectations for called method name and arguments, and sets response.
   void PrepareForMethodCall(const std::string& method_name,
diff --git a/chromeos/dbus/shill/shill_device_client.cc b/chromeos/dbus/shill/shill_device_client.cc
index f836c87b..be12287 100644
--- a/chromeos/dbus/shill/shill_device_client.cc
+++ b/chromeos/dbus/shill/shill_device_client.cc
@@ -60,25 +60,26 @@
   }
 
   void GetProperties(const dbus::ObjectPath& device_path,
-                     const DictionaryValueCallback& callback) override {
+                     DictionaryValueCallback callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kGetPropertiesFunction);
-    GetHelper(device_path)->CallDictionaryValueMethod(&method_call, callback);
+    GetHelper(device_path)
+        ->CallDictionaryValueMethod(&method_call, std::move(callback));
   }
 
   void SetProperty(const dbus::ObjectPath& device_path,
                    const std::string& name,
                    const base::Value& value,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override {
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kSetPropertyFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(name);
     ShillClientHelper::AppendValueDataAsVariant(&writer, value);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void ClearProperty(const dbus::ObjectPath& device_path,
@@ -94,103 +95,103 @@
   void RequirePin(const dbus::ObjectPath& device_path,
                   const std::string& pin,
                   bool require,
-                  const base::Closure& callback,
-                  const ErrorCallback& error_callback) override {
+                  base::OnceClosure callback,
+                  ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kRequirePinFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(pin);
     writer.AppendBool(require);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void EnterPin(const dbus::ObjectPath& device_path,
                 const std::string& pin,
-                const base::Closure& callback,
-                const ErrorCallback& error_callback) override {
+                base::OnceClosure callback,
+                ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kEnterPinFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(pin);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void UnblockPin(const dbus::ObjectPath& device_path,
                   const std::string& puk,
                   const std::string& pin,
-                  const base::Closure& callback,
-                  const ErrorCallback& error_callback) override {
+                  base::OnceClosure callback,
+                  ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kUnblockPinFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(puk);
     writer.AppendString(pin);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void ChangePin(const dbus::ObjectPath& device_path,
                  const std::string& old_pin,
                  const std::string& new_pin,
-                 const base::Closure& callback,
-                 const ErrorCallback& error_callback) override {
+                 base::OnceClosure callback,
+                 ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kChangePinFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(old_pin);
     writer.AppendString(new_pin);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void Register(const dbus::ObjectPath& device_path,
                 const std::string& network_id,
-                const base::Closure& callback,
-                const ErrorCallback& error_callback) override {
+                base::OnceClosure callback,
+                ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kRegisterFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(network_id);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void Reset(const dbus::ObjectPath& device_path,
-             const base::Closure& callback,
-             const ErrorCallback& error_callback) override {
+             base::OnceClosure callback,
+             ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kResetFunction);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void PerformTDLSOperation(const dbus::ObjectPath& device_path,
                             const std::string& operation,
                             const std::string& peer,
-                            const StringCallback& callback,
-                            const ErrorCallback& error_callback) override {
+                            StringCallback callback,
+                            ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kPerformTDLSOperationFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(operation);
     writer.AppendString(peer);
     GetHelper(device_path)
-        ->CallStringMethodWithErrorCallback(&method_call, callback,
-                                            error_callback);
+        ->CallStringMethodWithErrorCallback(&method_call, std::move(callback),
+                                            std::move(error_callback));
   }
 
   void AddWakeOnPacketConnection(const dbus::ObjectPath& device_path,
                                  const net::IPEndPoint& ip_endpoint,
-                                 const base::Closure& callback,
-                                 const ErrorCallback& error_callback) override {
+                                 base::OnceClosure callback,
+                                 ErrorCallback error_callback) override {
     if (ip_endpoint.address().empty()) {
       LOG(ERROR) << "AddWakeOnPacketConnection: null address";
       return;
@@ -200,28 +201,27 @@
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(ip_endpoint.ToStringWithoutPort());
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void AddWakeOnPacketOfTypes(const dbus::ObjectPath& device_path,
                               const std::vector<std::string>& types,
-                              const base::Closure& callback,
-                              const ErrorCallback& error_callback) override {
+                              base::OnceClosure callback,
+                              ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kAddWakeOnPacketOfTypesFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendArrayOfStrings(types);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
-  void RemoveWakeOnPacketConnection(
-      const dbus::ObjectPath& device_path,
-      const net::IPEndPoint& ip_endpoint,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) override {
+  void RemoveWakeOnPacketConnection(const dbus::ObjectPath& device_path,
+                                    const net::IPEndPoint& ip_endpoint,
+                                    base::OnceClosure callback,
+                                    ErrorCallback error_callback) override {
     if (ip_endpoint.address().empty()) {
       LOG(ERROR) << "RemoveWakeOnPacketConnection: null address";
       return;
@@ -231,48 +231,46 @@
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(ip_endpoint.ToStringWithoutPort());
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void RemoveWakeOnPacketOfTypes(const dbus::ObjectPath& device_path,
                                  const std::vector<std::string>& types,
-                                 const base::Closure& callback,
-                                 const ErrorCallback& error_callback) override {
+                                 base::OnceClosure callback,
+                                 ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamDeviceInterface,
                                  shill::kRemoveWakeOnPacketOfTypesFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendArrayOfStrings(types);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
-  void RemoveAllWakeOnPacketConnections(
-      const dbus::ObjectPath& device_path,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) override {
+  void RemoveAllWakeOnPacketConnections(const dbus::ObjectPath& device_path,
+                                        base::OnceClosure callback,
+                                        ErrorCallback error_callback) override {
     dbus::MethodCall method_call(
         shill::kFlimflamDeviceInterface,
         shill::kRemoveAllWakeOnPacketConnectionsFunction);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
-  void SetUsbEthernetMacAddressSource(
-      const dbus::ObjectPath& device_path,
-      const std::string& source,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) override {
+  void SetUsbEthernetMacAddressSource(const dbus::ObjectPath& device_path,
+                                      const std::string& source,
+                                      base::OnceClosure callback,
+                                      ErrorCallback error_callback) override {
     dbus::MethodCall method_call(
         shill::kFlimflamDeviceInterface,
         shill::kSetUsbEthernetMacAddressSourceFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(source);
     GetHelper(device_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   TestInterface* GetTestInterface() override { return nullptr; }
diff --git a/chromeos/dbus/shill/shill_device_client.h b/chromeos/dbus/shill/shill_device_client.h
index fcf5339..b6b2956 100644
--- a/chromeos/dbus/shill/shill_device_client.h
+++ b/chromeos/dbus/shill/shill_device_client.h
@@ -35,7 +35,6 @@
 // DBusThreadManager instance.
 class COMPONENT_EXPORT(SHILL_CLIENT) ShillDeviceClient {
  public:
-  typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
   typedef ShillClientHelper::StringCallback StringCallback;
   typedef ShillClientHelper::ErrorCallback ErrorCallback;
@@ -97,15 +96,15 @@
   // Calls GetProperties method.
   // |callback| is called after the method call finishes.
   virtual void GetProperties(const dbus::ObjectPath& device_path,
-                             const DictionaryValueCallback& callback) = 0;
+                             DictionaryValueCallback callback) = 0;
 
   // Calls SetProperty method.
   // |callback| is called after the method call finishes.
   virtual void SetProperty(const dbus::ObjectPath& device_path,
                            const std::string& name,
                            const base::Value& value,
-                           const base::Closure& callback,
-                           const ErrorCallback& error_callback) = 0;
+                           base::OnceClosure callback,
+                           ErrorCallback error_callback) = 0;
 
   // Calls ClearProperty method.
   // |callback| is called after the method call finishes.
@@ -118,60 +117,59 @@
   virtual void RequirePin(const dbus::ObjectPath& device_path,
                           const std::string& pin,
                           bool require,
-                          const base::Closure& callback,
-                          const ErrorCallback& error_callback) = 0;
+                          base::OnceClosure callback,
+                          ErrorCallback error_callback) = 0;
 
   // Calls the EnterPin method.
   // |callback| is called after the method call finishes.
   virtual void EnterPin(const dbus::ObjectPath& device_path,
                         const std::string& pin,
-                        const base::Closure& callback,
-                        const ErrorCallback& error_callback) = 0;
+                        base::OnceClosure callback,
+                        ErrorCallback error_callback) = 0;
 
   // Calls the UnblockPin method.
   // |callback| is called after the method call finishes.
   virtual void UnblockPin(const dbus::ObjectPath& device_path,
                           const std::string& puk,
                           const std::string& pin,
-                          const base::Closure& callback,
-                          const ErrorCallback& error_callback) = 0;
+                          base::OnceClosure callback,
+                          ErrorCallback error_callback) = 0;
 
   // Calls the ChangePin method.
   // |callback| is called after the method call finishes.
   virtual void ChangePin(const dbus::ObjectPath& device_path,
                          const std::string& old_pin,
                          const std::string& new_pin,
-                         const base::Closure& callback,
-                         const ErrorCallback& error_callback) = 0;
+                         base::OnceClosure callback,
+                         ErrorCallback error_callback) = 0;
 
   // Calls the Register method.
   // |callback| is called after the method call finishes.
   virtual void Register(const dbus::ObjectPath& device_path,
                         const std::string& network_id,
-                        const base::Closure& callback,
-                        const ErrorCallback& error_callback) = 0;
+                        base::OnceClosure callback,
+                        ErrorCallback error_callback) = 0;
 
   // Calls the Reset method.
   // |callback| is called after the method call finishes.
   virtual void Reset(const dbus::ObjectPath& device_path,
-                     const base::Closure& callback,
-                     const ErrorCallback& error_callback) = 0;
+                     base::OnceClosure callback,
+                     ErrorCallback error_callback) = 0;
 
   // Calls the PerformTDLSOperation method.
   // |callback| is called after the method call finishes.
   virtual void PerformTDLSOperation(const dbus::ObjectPath& device_path,
                                     const std::string& operation,
                                     const std::string& peer,
-                                    const StringCallback& callback,
-                                    const ErrorCallback& error_callback) = 0;
+                                    StringCallback callback,
+                                    ErrorCallback error_callback) = 0;
 
   // Adds |ip_endpoint| to the list of tcp connections that the device should
   // monitor to wake the system from suspend.
-  virtual void AddWakeOnPacketConnection(
-      const dbus::ObjectPath& device_path,
-      const net::IPEndPoint& ip_endpoint,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) = 0;
+  virtual void AddWakeOnPacketConnection(const dbus::ObjectPath& device_path,
+                                         const net::IPEndPoint& ip_endpoint,
+                                         base::OnceClosure callback,
+                                         ErrorCallback error_callback) = 0;
 
   // Adds |types| to the list of packet types that the device should monitor to
   // wake the system from suspend. |types| corresponds to "Wake on WiFi Packet
@@ -179,33 +177,31 @@
   // third_party/cros_system_api/dbus/shill/dbus-constants.h.
   virtual void AddWakeOnPacketOfTypes(const dbus::ObjectPath& device_path,
                                       const std::vector<std::string>& types,
-                                      const base::Closure& callback,
-                                      const ErrorCallback& error_callback) = 0;
+                                      base::OnceClosure callback,
+                                      ErrorCallback error_callback) = 0;
 
   // Removes |ip_endpoint| from the list of tcp connections that the device
   // should monitor to wake the system from suspend.
-  virtual void RemoveWakeOnPacketConnection(
-      const dbus::ObjectPath& device_path,
-      const net::IPEndPoint& ip_endpoint,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) = 0;
+  virtual void RemoveWakeOnPacketConnection(const dbus::ObjectPath& device_path,
+                                            const net::IPEndPoint& ip_endpoint,
+                                            base::OnceClosure callback,
+                                            ErrorCallback error_callback) = 0;
 
   // Removes |types| from the list of packet types that the device should
   // monitor to wake the system from suspend. |types| corresponds to "Wake on
   // WiFi Packet Type Constants." in
   // third_party/cros_system_api/dbus/shill/dbus-constants.h.
-  virtual void RemoveWakeOnPacketOfTypes(
-      const dbus::ObjectPath& device_path,
-      const std::vector<std::string>& types,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) = 0;
+  virtual void RemoveWakeOnPacketOfTypes(const dbus::ObjectPath& device_path,
+                                         const std::vector<std::string>& types,
+                                         base::OnceClosure callback,
+                                         ErrorCallback error_callback) = 0;
 
   // Clears the list of tcp connections that the device should monitor to wake
   // the system from suspend.
   virtual void RemoveAllWakeOnPacketConnections(
       const dbus::ObjectPath& device_path,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) = 0;
+      base::OnceClosure callback,
+      ErrorCallback error_callback) = 0;
 
   // Set MAC address source for USB Ethernet adapter. |source| corresponds to
   // "USB Ethernet MAC address sources." in
@@ -213,8 +209,8 @@
   virtual void SetUsbEthernetMacAddressSource(
       const dbus::ObjectPath& device_path,
       const std::string& source,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) = 0;
+      base::OnceClosure callback,
+      ErrorCallback error_callback) = 0;
 
   // Returns an interface for testing (stub only), or returns null.
   virtual TestInterface* GetTestInterface() = 0;
diff --git a/chromeos/dbus/shill/shill_device_client_unittest.cc b/chromeos/dbus/shill/shill_device_client_unittest.cc
index ea9d3e5..6ebde2d 100644
--- a/chromeos/dbus/shill/shill_device_client_unittest.cc
+++ b/chromeos/dbus/shill/shill_device_client_unittest.cc
@@ -130,10 +130,10 @@
   base::DictionaryValue value;
   value.SetKey(shill::kCellularAllowRoamingProperty, base::Value(kValue));
   PrepareForMethodCall(shill::kGetPropertiesFunction,
-                       base::Bind(&ExpectNoArgument), response.get());
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   // Call method.
   client_->GetProperties(dbus::ObjectPath(kExampleDevicePath),
-                         base::Bind(&ExpectDictionaryValueResult, &value));
+                         base::BindOnce(&ExpectDictionaryValueResult, &value));
   // Run the message loop.
   base::RunLoop().RunUntilIdle();
 }
@@ -145,12 +145,13 @@
 
   // Set expectations.
   const base::Value value(kValue);
-  PrepareForMethodCall(shill::kSetPropertyFunction,
-                       base::Bind(&ExpectStringAndValueArguments,
-                                  shill::kCellularAllowRoamingProperty, &value),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kSetPropertyFunction,
+      base::BindRepeating(&ExpectStringAndValueArguments,
+                          shill::kCellularAllowRoamingProperty, &value),
+      response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillDeviceClient::ErrorCallback> mock_error_callback;
   client_->SetProperty(dbus::ObjectPath(kExampleDevicePath),
                        shill::kCellularAllowRoamingProperty, value,
@@ -169,7 +170,8 @@
   // Set expectations.
   PrepareForMethodCall(
       shill::kClearPropertyFunction,
-      base::Bind(&ExpectStringArgument, shill::kCellularAllowRoamingProperty),
+      base::BindRepeating(&ExpectStringArgument,
+                          shill::kCellularAllowRoamingProperty),
       response.get());
   // Call method.
   client_->ClearProperty(dbus::ObjectPath(kExampleDevicePath),
@@ -186,11 +188,11 @@
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
 
   // Set expectations.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillDeviceClient::ErrorCallback> mock_error_callback;
   PrepareForMethodCall(
       shill::kRequirePinFunction,
-      base::Bind(&ExpectStringAndBoolArguments, kPin, kRequired),
+      base::BindRepeating(&ExpectStringAndBoolArguments, kPin, kRequired),
       response.get());
   EXPECT_CALL(mock_closure, Run()).Times(1);
   EXPECT_CALL(mock_error_callback, Run(_, _)).Times(0);
@@ -207,10 +209,11 @@
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
 
   // Set expectations.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillDeviceClient::ErrorCallback> mock_error_callback;
   PrepareForMethodCall(shill::kEnterPinFunction,
-                       base::Bind(&ExpectStringArgument, kPin), response.get());
+                       base::BindRepeating(&ExpectStringArgument, kPin),
+                       response.get());
   EXPECT_CALL(mock_closure, Run()).Times(1);
   EXPECT_CALL(mock_error_callback, Run(_, _)).Times(0);
 
@@ -228,11 +231,12 @@
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
 
   // Set expectations.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillDeviceClient::ErrorCallback> mock_error_callback;
-  PrepareForMethodCall(shill::kUnblockPinFunction,
-                       base::Bind(&ExpectTwoStringArguments, kPuk, kPin),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kUnblockPinFunction,
+      base::BindRepeating(&ExpectTwoStringArguments, kPuk, kPin),
+      response.get());
   EXPECT_CALL(mock_closure, Run()).Times(1);
   EXPECT_CALL(mock_error_callback, Run(_, _)).Times(0);
 
@@ -250,11 +254,12 @@
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
 
   // Set expectations.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillDeviceClient::ErrorCallback> mock_error_callback;
-  PrepareForMethodCall(shill::kChangePinFunction,
-                       base::Bind(&ExpectTwoStringArguments, kOldPin, kNewPin),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kChangePinFunction,
+      base::BindRepeating(&ExpectTwoStringArguments, kOldPin, kNewPin),
+      response.get());
   EXPECT_CALL(mock_closure, Run()).Times(1);
   EXPECT_CALL(mock_error_callback, Run(_, _)).Times(0);
 
@@ -271,10 +276,10 @@
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
 
   // Set expectations.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillDeviceClient::ErrorCallback> mock_error_callback;
   PrepareForMethodCall(shill::kRegisterFunction,
-                       base::Bind(&ExpectStringArgument, kNetworkId),
+                       base::BindRepeating(&ExpectStringArgument, kNetworkId),
                        response.get());
   EXPECT_CALL(mock_closure, Run()).Times(1);
   EXPECT_CALL(mock_error_callback, Run(_, _)).Times(0);
@@ -291,10 +296,10 @@
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
 
   // Set expectations.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillDeviceClient::ErrorCallback> mock_error_callback;
-  PrepareForMethodCall(shill::kResetFunction, base::Bind(&ExpectNoArgument),
-                       response.get());
+  PrepareForMethodCall(shill::kResetFunction,
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   EXPECT_CALL(mock_closure, Run()).Times(1);
   // Call method.
   client_->Reset(dbus::ObjectPath(kExampleDevicePath), mock_closure.Get(),
diff --git a/chromeos/dbus/shill/shill_ipconfig_client.cc b/chromeos/dbus/shill/shill_ipconfig_client.cc
index 0c5c648..a41c398 100644
--- a/chromeos/dbus/shill/shill_ipconfig_client.cc
+++ b/chromeos/dbus/shill/shill_ipconfig_client.cc
@@ -46,7 +46,7 @@
     GetHelper(ipconfig_path)->RemovePropertyChangedObserver(observer);
   }
   void GetProperties(const dbus::ObjectPath& ipconfig_path,
-                     const DictionaryValueCallback& callback) override;
+                     DictionaryValueCallback callback) override;
   void SetProperty(const dbus::ObjectPath& ipconfig_path,
                    const std::string& name,
                    const base::Value& value,
@@ -86,10 +86,11 @@
 
 void ShillIPConfigClientImpl::GetProperties(
     const dbus::ObjectPath& ipconfig_path,
-    const DictionaryValueCallback& callback) {
+    DictionaryValueCallback callback) {
   dbus::MethodCall method_call(shill::kFlimflamIPConfigInterface,
                                shill::kGetPropertiesFunction);
-  GetHelper(ipconfig_path)->CallDictionaryValueMethod(&method_call, callback);
+  GetHelper(ipconfig_path)
+      ->CallDictionaryValueMethod(&method_call, std::move(callback));
 }
 
 void ShillIPConfigClientImpl::SetProperty(const dbus::ObjectPath& ipconfig_path,
diff --git a/chromeos/dbus/shill/shill_ipconfig_client.h b/chromeos/dbus/shill/shill_ipconfig_client.h
index fd18e21..9ab372d 100644
--- a/chromeos/dbus/shill/shill_ipconfig_client.h
+++ b/chromeos/dbus/shill/shill_ipconfig_client.h
@@ -31,7 +31,6 @@
 // initializes the DBusThreadManager instance.
 class COMPONENT_EXPORT(SHILL_CLIENT) ShillIPConfigClient {
  public:
-  typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
 
   class TestInterface {
@@ -73,7 +72,7 @@
   // Calls GetProperties method.
   // |callback| is called after the method call succeeds.
   virtual void GetProperties(const dbus::ObjectPath& ipconfig_path,
-                             const DictionaryValueCallback& callback) = 0;
+                             DictionaryValueCallback callback) = 0;
 
   // Calls SetProperty method.
   // |callback| is called after the method call succeeds.
diff --git a/chromeos/dbus/shill/shill_ipconfig_client_unittest.cc b/chromeos/dbus/shill/shill_ipconfig_client_unittest.cc
index 274c4d0..0ee26b1 100644
--- a/chromeos/dbus/shill/shill_ipconfig_client_unittest.cc
+++ b/chromeos/dbus/shill/shill_ipconfig_client_unittest.cc
@@ -112,10 +112,10 @@
 
   // Set expectations.
   PrepareForMethodCall(shill::kGetPropertiesFunction,
-                       base::Bind(&ExpectNoArgument), response.get());
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   // Call method.
   client_->GetProperties(dbus::ObjectPath(kExampleIPConfigPath),
-                         base::Bind(&ExpectDictionaryValueResult, &value));
+                         base::BindOnce(&ExpectDictionaryValueResult, &value));
   // Run the message loop.
   base::RunLoop().RunUntilIdle();
 }
@@ -129,8 +129,8 @@
   // Set expectations.
   base::Value value(kAddress);
   PrepareForMethodCall(shill::kSetPropertyFunction,
-                       base::Bind(&ExpectStringAndValueArguments,
-                                  shill::kAddressProperty, &value),
+                       base::BindRepeating(&ExpectStringAndValueArguments,
+                                           shill::kAddressProperty, &value),
                        response.get());
   // Call method.
   client_->SetProperty(dbus::ObjectPath(kExampleIPConfigPath),
@@ -147,7 +147,7 @@
   // Set expectations.
   PrepareForMethodCall(
       shill::kClearPropertyFunction,
-      base::Bind(&ExpectStringArgument, shill::kAddressProperty),
+      base::BindRepeating(&ExpectStringArgument, shill::kAddressProperty),
       response.get());
   // Call method.
   client_->ClearProperty(dbus::ObjectPath(kExampleIPConfigPath),
@@ -163,7 +163,7 @@
 
   // Set expectations.
   PrepareForMethodCall(shill::kRemoveConfigFunction,
-                       base::Bind(&ExpectNoArgument), response.get());
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   // Call method.
   client_->Remove(dbus::ObjectPath(kExampleIPConfigPath),
                   base::BindOnce(&ExpectNoResultValue));
diff --git a/chromeos/dbus/shill/shill_manager_client.cc b/chromeos/dbus/shill/shill_manager_client.cc
index af95e48..ad22c45 100644
--- a/chromeos/dbus/shill/shill_manager_client.cc
+++ b/chromeos/dbus/shill/shill_manager_client.cc
@@ -45,121 +45,118 @@
     helper_->RemovePropertyChangedObserver(observer);
   }
 
-  void GetProperties(const DictionaryValueCallback& callback) override {
+  void GetProperties(DictionaryValueCallback callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kGetPropertiesFunction);
-    helper_->CallDictionaryValueMethod(&method_call, callback);
+    helper_->CallDictionaryValueMethod(&method_call, std::move(callback));
   }
 
-  void GetNetworksForGeolocation(
-      const DictionaryValueCallback& callback) override {
+  void GetNetworksForGeolocation(DictionaryValueCallback callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kGetNetworksForGeolocation);
-    helper_->CallDictionaryValueMethod(&method_call, callback);
+    helper_->CallDictionaryValueMethod(&method_call, std::move(callback));
   }
 
   void SetProperty(const std::string& name,
                    const base::Value& value,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override {
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kSetPropertyFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(name);
     ShillClientHelper::AppendValueDataAsVariant(&writer, value);
-    helper_->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                             error_callback);
+    helper_->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                             std::move(error_callback));
   }
 
   void RequestScan(const std::string& type,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override {
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kRequestScanFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(type);
-    helper_->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                             error_callback);
+    helper_->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                             std::move(error_callback));
   }
 
   void EnableTechnology(const std::string& type,
-                        const base::Closure& callback,
-                        const ErrorCallback& error_callback) override {
+                        base::OnceClosure callback,
+                        ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kEnableTechnologyFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(type);
-    helper_->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                             error_callback);
+    helper_->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                             std::move(error_callback));
   }
 
-  void SetNetworkThrottlingStatus(
-      const NetworkThrottlingStatus& status,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) override {
+  void SetNetworkThrottlingStatus(const NetworkThrottlingStatus& status,
+                                  base::OnceClosure callback,
+                                  ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kSetNetworkThrottlingFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendBool(status.enabled);
     writer.AppendUint32(status.upload_rate_kbits);
     writer.AppendUint32(status.download_rate_kbits);
-    helper_->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                             error_callback);
+    helper_->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                             std::move(error_callback));
   }
 
   void DisableTechnology(const std::string& type,
-                         const base::Closure& callback,
-                         const ErrorCallback& error_callback) override {
+                         base::OnceClosure callback,
+                         ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kDisableTechnologyFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(type);
-    helper_->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                             error_callback);
+    helper_->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                             std::move(error_callback));
   }
 
   void ConfigureService(const base::DictionaryValue& properties,
                         ObjectPathCallback callback,
-                        const ErrorCallback& error_callback) override {
+                        ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kConfigureServiceFunction);
     dbus::MessageWriter writer(&method_call);
     ShillClientHelper::AppendServicePropertiesDictionary(&writer, properties);
     helper_->CallObjectPathMethodWithErrorCallback(
-        &method_call, std::move(callback), error_callback);
+        &method_call, std::move(callback), std::move(error_callback));
   }
 
-  void ConfigureServiceForProfile(
-      const dbus::ObjectPath& profile_path,
-      const base::DictionaryValue& properties,
-      ObjectPathCallback callback,
-      const ErrorCallback& error_callback) override {
+  void ConfigureServiceForProfile(const dbus::ObjectPath& profile_path,
+                                  const base::DictionaryValue& properties,
+                                  ObjectPathCallback callback,
+                                  ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kConfigureServiceForProfileFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendObjectPath(dbus::ObjectPath(profile_path));
     ShillClientHelper::AppendServicePropertiesDictionary(&writer, properties);
     helper_->CallObjectPathMethodWithErrorCallback(
-        &method_call, std::move(callback), error_callback);
+        &method_call, std::move(callback), std::move(error_callback));
   }
 
   void GetService(const base::DictionaryValue& properties,
                   ObjectPathCallback callback,
-                  const ErrorCallback& error_callback) override {
+                  ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kGetServiceFunction);
     dbus::MessageWriter writer(&method_call);
     ShillClientHelper::AppendServicePropertiesDictionary(&writer, properties);
     helper_->CallObjectPathMethodWithErrorCallback(
-        &method_call, std::move(callback), error_callback);
+        &method_call, std::move(callback), std::move(error_callback));
   }
 
-  void ConnectToBestServices(const base::Closure& callback,
-                             const ErrorCallback& error_callback) override {
+  void ConnectToBestServices(base::OnceClosure callback,
+                             ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamManagerInterface,
                                  shill::kConnectToBestServicesFunction);
-    helper_->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                             error_callback);
+    helper_->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                             std::move(error_callback));
   }
 
   TestInterface* GetTestInterface() override { return nullptr; }
diff --git a/chromeos/dbus/shill/shill_manager_client.h b/chromeos/dbus/shill/shill_manager_client.h
index f1fc6fa..08a1b14 100644
--- a/chromeos/dbus/shill/shill_manager_client.h
+++ b/chromeos/dbus/shill/shill_manager_client.h
@@ -28,7 +28,6 @@
 // initializes the DBusThreadManager instance.
 class COMPONENT_EXPORT(SHILL_CLIENT) ShillManagerClient {
  public:
-  typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
   typedef ShillClientHelper::ErrorCallback ErrorCallback;
 
@@ -138,43 +137,42 @@
 
   // Calls GetProperties method.
   // |callback| is called after the method call succeeds.
-  virtual void GetProperties(const DictionaryValueCallback& callback) = 0;
+  virtual void GetProperties(DictionaryValueCallback callback) = 0;
 
   // Calls GetNetworksForGeolocation method.
   // |callback| is called after the method call succeeds.
-  virtual void GetNetworksForGeolocation(
-      const DictionaryValueCallback& callback) = 0;
+  virtual void GetNetworksForGeolocation(DictionaryValueCallback callback) = 0;
 
   // Calls SetProperty method.
   // |callback| is called after the method call succeeds.
   virtual void SetProperty(const std::string& name,
                            const base::Value& value,
-                           const base::Closure& callback,
-                           const ErrorCallback& error_callback) = 0;
+                           base::OnceClosure callback,
+                           ErrorCallback error_callback) = 0;
 
   // Calls RequestScan method.
   // |callback| is called after the method call succeeds.
   virtual void RequestScan(const std::string& type,
-                           const base::Closure& callback,
-                           const ErrorCallback& error_callback) = 0;
+                           base::OnceClosure callback,
+                           ErrorCallback error_callback) = 0;
 
   // Calls EnableTechnology method.
   // |callback| is called after the method call succeeds.
   virtual void EnableTechnology(const std::string& type,
-                                const base::Closure& callback,
-                                const ErrorCallback& error_callback) = 0;
+                                base::OnceClosure callback,
+                                ErrorCallback error_callback) = 0;
 
   // Calls DisableTechnology method.
   // |callback| is called after the method call succeeds.
   virtual void DisableTechnology(const std::string& type,
-                                 const base::Closure& callback,
-                                 const ErrorCallback& error_callback) = 0;
+                                 base::OnceClosure callback,
+                                 ErrorCallback error_callback) = 0;
 
   // Calls ConfigureService method.
   // |callback| is called after the method call succeeds.
   virtual void ConfigureService(const base::DictionaryValue& properties,
                                 ObjectPathCallback callback,
-                                const ErrorCallback& error_callback) = 0;
+                                ErrorCallback error_callback) = 0;
 
   // Calls ConfigureServiceForProfile method.
   // |callback| is called with the created service if the method call succeeds.
@@ -182,25 +180,24 @@
       const dbus::ObjectPath& profile_path,
       const base::DictionaryValue& properties,
       ObjectPathCallback callback,
-      const ErrorCallback& error_callback) = 0;
+      ErrorCallback error_callback) = 0;
 
   // Calls GetService method.
   // |callback| is called after the method call succeeds.
   virtual void GetService(const base::DictionaryValue& properties,
                           ObjectPathCallback callback,
-                          const ErrorCallback& error_callback) = 0;
+                          ErrorCallback error_callback) = 0;
 
   // For each technology present, connects to the "best" service available.
   // Called once the user is logged in and certificates are loaded.
-  virtual void ConnectToBestServices(const base::Closure& callback,
-                                     const ErrorCallback& error_callback) = 0;
+  virtual void ConnectToBestServices(base::OnceClosure callback,
+                                     ErrorCallback error_callback) = 0;
 
   // Enable or disable network bandwidth throttling, on all interfaces on the
   // system.
-  virtual void SetNetworkThrottlingStatus(
-      const NetworkThrottlingStatus& status,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) = 0;
+  virtual void SetNetworkThrottlingStatus(const NetworkThrottlingStatus& status,
+                                          base::OnceClosure callback,
+                                          ErrorCallback error_callback) = 0;
 
   // Returns an interface for testing (stub only), or returns null.
   virtual TestInterface* GetTestInterface() = 0;
diff --git a/chromeos/dbus/shill/shill_manager_client_unittest.cc b/chromeos/dbus/shill/shill_manager_client_unittest.cc
index eefd987..f0a9504 100644
--- a/chromeos/dbus/shill/shill_manager_client_unittest.cc
+++ b/chromeos/dbus/shill/shill_manager_client_unittest.cc
@@ -118,9 +118,9 @@
   value.SetKey(shill::kOfflineModeProperty, base::Value(true));
   // Set expectations.
   PrepareForMethodCall(shill::kGetPropertiesFunction,
-                       base::Bind(&ExpectNoArgument), response.get());
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   // Call method.
-  client_->GetProperties(base::Bind(&ExpectDictionaryValueResult, &value));
+  client_->GetProperties(base::BindOnce(&ExpectDictionaryValueResult, &value));
   // Run the message loop.
   base::RunLoop().RunUntilIdle();
 }
@@ -163,10 +163,10 @@
 
   // Set expectations.
   PrepareForMethodCall(shill::kGetNetworksForGeolocation,
-                       base::Bind(&ExpectNoArgument), response.get());
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   // Call method.
   client_->GetNetworksForGeolocation(
-      base::Bind(&ExpectDictionaryValueResult, &type_dict_value));
+      base::BindOnce(&ExpectDictionaryValueResult, &type_dict_value));
 
   // Run the message loop.
   base::RunLoop().RunUntilIdle();
@@ -177,12 +177,13 @@
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
   // Set expectations.
   base::Value value("portal list");
-  PrepareForMethodCall(shill::kSetPropertyFunction,
-                       base::Bind(ExpectStringAndValueArguments,
-                                  shill::kCheckPortalListProperty, &value),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kSetPropertyFunction,
+      base::BindRepeating(ExpectStringAndValueArguments,
+                          shill::kCheckPortalListProperty, &value),
+      response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillManagerClient::ErrorCallback> mock_error_callback;
   client_->SetProperty(shill::kCheckPortalListProperty, value,
                        mock_closure.Get(), mock_error_callback.Get());
@@ -197,11 +198,12 @@
   // Create response.
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
   // Set expectations.
-  PrepareForMethodCall(shill::kRequestScanFunction,
-                       base::Bind(&ExpectStringArgument, shill::kTypeWifi),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kRequestScanFunction,
+      base::BindRepeating(&ExpectStringArgument, shill::kTypeWifi),
+      response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillManagerClient::ErrorCallback> mock_error_callback;
   client_->RequestScan(shill::kTypeWifi, mock_closure.Get(),
                        mock_error_callback.Get());
@@ -216,11 +218,12 @@
   // Create response.
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
   // Set expectations.
-  PrepareForMethodCall(shill::kEnableTechnologyFunction,
-                       base::Bind(&ExpectStringArgument, shill::kTypeWifi),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kEnableTechnologyFunction,
+      base::BindRepeating(&ExpectStringArgument, shill::kTypeWifi),
+      response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillManagerClient::ErrorCallback> mock_error_callback;
   client_->EnableTechnology(shill::kTypeWifi, mock_closure.Get(),
                             mock_error_callback.Get());
@@ -239,11 +242,11 @@
   const uint32_t upload_rate = 1200;
   const uint32_t download_rate = 2000;
   PrepareForMethodCall(shill::kSetNetworkThrottlingFunction,
-                       base::Bind(&ExpectThrottlingArguments, enabled,
-                                  upload_rate, download_rate),
+                       base::BindRepeating(&ExpectThrottlingArguments, enabled,
+                                           upload_rate, download_rate),
                        response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillManagerClient::ErrorCallback> mock_error_callback;
   EXPECT_CALL(mock_closure, Run()).Times(1);
   EXPECT_CALL(mock_error_callback, Run(_, _)).Times(0);
@@ -259,11 +262,12 @@
   // Create response.
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
   // Set expectations.
-  PrepareForMethodCall(shill::kDisableTechnologyFunction,
-                       base::Bind(&ExpectStringArgument, shill::kTypeWifi),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kDisableTechnologyFunction,
+      base::BindRepeating(&ExpectStringArgument, shill::kTypeWifi),
+      response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillManagerClient::ErrorCallback> mock_error_callback;
   client_->DisableTechnology(shill::kTypeWifi, mock_closure.Get(),
                              mock_error_callback.Get());
@@ -285,14 +289,14 @@
   // Use a variant valued dictionary rather than a string valued one.
   const bool string_valued = false;
   // Set expectations.
-  PrepareForMethodCall(
-      shill::kConfigureServiceFunction,
-      base::Bind(&ExpectDictionaryValueArgument, arg.get(), string_valued),
-      response.get());
+  PrepareForMethodCall(shill::kConfigureServiceFunction,
+                       base::BindRepeating(&ExpectDictionaryValueArgument,
+                                           arg.get(), string_valued),
+                       response.get());
   // Call method.
   base::MockCallback<ShillManagerClient::ErrorCallback> mock_error_callback;
   client_->ConfigureService(
-      *arg, base::Bind(&ExpectObjectPathResultWithoutStatus, object_path),
+      *arg, base::BindOnce(&ExpectObjectPathResultWithoutStatus, object_path),
       mock_error_callback.Get());
   EXPECT_CALL(mock_error_callback, Run(_, _)).Times(0);
 
@@ -311,14 +315,14 @@
   // Use a variant valued dictionary rather than a string valued one.
   const bool string_valued = false;
   // Set expectations.
-  PrepareForMethodCall(
-      shill::kGetServiceFunction,
-      base::Bind(&ExpectDictionaryValueArgument, arg.get(), string_valued),
-      response.get());
+  PrepareForMethodCall(shill::kGetServiceFunction,
+                       base::BindRepeating(&ExpectDictionaryValueArgument,
+                                           arg.get(), string_valued),
+                       response.get());
   // Call method.
   base::MockCallback<ShillManagerClient::ErrorCallback> mock_error_callback;
   client_->GetService(
-      *arg, base::Bind(&ExpectObjectPathResultWithoutStatus, object_path),
+      *arg, base::BindOnce(&ExpectObjectPathResultWithoutStatus, object_path),
       mock_error_callback.Get());
   EXPECT_CALL(mock_error_callback, Run(_, _)).Times(0);
 
diff --git a/chromeos/dbus/shill/shill_profile_client.cc b/chromeos/dbus/shill/shill_profile_client.cc
index 0782411..2264788a7 100644
--- a/chromeos/dbus/shill/shill_profile_client.cc
+++ b/chromeos/dbus/shill/shill_profile_client.cc
@@ -45,16 +45,16 @@
   }
 
   void GetProperties(const dbus::ObjectPath& profile_path,
-                     const DictionaryValueCallbackWithoutStatus& callback,
-                     const ErrorCallback& error_callback) override;
+                     DictionaryValueCallbackWithoutStatus callback,
+                     ErrorCallback error_callback) override;
   void GetEntry(const dbus::ObjectPath& profile_path,
                 const std::string& entry_path,
-                const DictionaryValueCallbackWithoutStatus& callback,
-                const ErrorCallback& error_callback) override;
+                DictionaryValueCallbackWithoutStatus callback,
+                ErrorCallback error_callback) override;
   void DeleteEntry(const dbus::ObjectPath& profile_path,
                    const std::string& entry_path,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override;
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override;
 
   TestInterface* GetTestInterface() override { return nullptr; }
 
@@ -89,39 +89,40 @@
 
 void ShillProfileClientImpl::GetProperties(
     const dbus::ObjectPath& profile_path,
-    const DictionaryValueCallbackWithoutStatus& callback,
-    const ErrorCallback& error_callback) {
+    DictionaryValueCallbackWithoutStatus callback,
+    ErrorCallback error_callback) {
   dbus::MethodCall method_call(shill::kFlimflamProfileInterface,
                                shill::kGetPropertiesFunction);
   GetHelper(profile_path)
-      ->CallDictionaryValueMethodWithErrorCallback(&method_call, callback,
-                                                   error_callback);
+      ->CallDictionaryValueMethodWithErrorCallback(
+          &method_call, std::move(callback), std::move(error_callback));
 }
 
 void ShillProfileClientImpl::GetEntry(
     const dbus::ObjectPath& profile_path,
     const std::string& entry_path,
-    const DictionaryValueCallbackWithoutStatus& callback,
-    const ErrorCallback& error_callback) {
+    DictionaryValueCallbackWithoutStatus callback,
+    ErrorCallback error_callback) {
   dbus::MethodCall method_call(shill::kFlimflamProfileInterface,
                                shill::kGetEntryFunction);
   dbus::MessageWriter writer(&method_call);
   writer.AppendString(entry_path);
   GetHelper(profile_path)
-      ->CallDictionaryValueMethodWithErrorCallback(&method_call, callback,
-                                                   error_callback);
+      ->CallDictionaryValueMethodWithErrorCallback(
+          &method_call, std::move(callback), std::move(error_callback));
 }
 
 void ShillProfileClientImpl::DeleteEntry(const dbus::ObjectPath& profile_path,
                                          const std::string& entry_path,
-                                         const base::Closure& callback,
-                                         const ErrorCallback& error_callback) {
+                                         base::OnceClosure callback,
+                                         ErrorCallback error_callback) {
   dbus::MethodCall method_call(shill::kFlimflamProfileInterface,
                                shill::kDeleteEntryFunction);
   dbus::MessageWriter writer(&method_call);
   writer.AppendString(entry_path);
   GetHelper(profile_path)
-      ->CallVoidMethodWithErrorCallback(&method_call, callback, error_callback);
+      ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                        std::move(error_callback));
 }
 
 }  // namespace
diff --git a/chromeos/dbus/shill/shill_profile_client.h b/chromeos/dbus/shill/shill_profile_client.h
index 5094a53..2dcdb8a 100644
--- a/chromeos/dbus/shill/shill_profile_client.h
+++ b/chromeos/dbus/shill/shill_profile_client.h
@@ -32,7 +32,6 @@
 // initializes the DBusThreadManager instance.
 class COMPONENT_EXPORT(SHILL_CLIENT) ShillProfileClient {
  public:
-  typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallbackWithoutStatus
       DictionaryValueCallbackWithoutStatus;
   typedef ShillClientHelper::ErrorCallback ErrorCallback;
@@ -125,24 +124,23 @@
 
   // Calls GetProperties method.
   // |callback| is called after the method call succeeds.
-  virtual void GetProperties(
-      const dbus::ObjectPath& profile_path,
-      const DictionaryValueCallbackWithoutStatus& callback,
-      const ErrorCallback& error_callback) = 0;
+  virtual void GetProperties(const dbus::ObjectPath& profile_path,
+                             DictionaryValueCallbackWithoutStatus callback,
+                             ErrorCallback error_callback) = 0;
 
   // Calls GetEntry method.
   // |callback| is called after the method call succeeds.
   virtual void GetEntry(const dbus::ObjectPath& profile_path,
                         const std::string& entry_path,
-                        const DictionaryValueCallbackWithoutStatus& callback,
-                        const ErrorCallback& error_callback) = 0;
+                        DictionaryValueCallbackWithoutStatus callback,
+                        ErrorCallback error_callback) = 0;
 
   // Calls DeleteEntry method.
   // |callback| is called after the method call succeeds.
   virtual void DeleteEntry(const dbus::ObjectPath& profile_path,
                            const std::string& entry_path,
-                           const base::Closure& callback,
-                           const ErrorCallback& error_callback) = 0;
+                           base::OnceClosure callback,
+                           ErrorCallback error_callback) = 0;
 
   // Returns an interface for testing (stub only), or returns null.
   virtual TestInterface* GetTestInterface() = 0;
diff --git a/chromeos/dbus/shill/shill_profile_client_unittest.cc b/chromeos/dbus/shill/shill_profile_client_unittest.cc
index 8c71ca2..6d0e03fa 100644
--- a/chromeos/dbus/shill/shill_profile_client_unittest.cc
+++ b/chromeos/dbus/shill/shill_profile_client_unittest.cc
@@ -115,12 +115,12 @@
   value.SetWithoutPathExpansion(shill::kEntriesProperty, std::move(entries));
   // Set expectations.
   PrepareForMethodCall(shill::kGetPropertiesFunction,
-                       base::Bind(&ExpectNoArgument), response.get());
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   // Call method.
   base::MockCallback<ShillProfileClient::ErrorCallback> error_callback;
   client_->GetProperties(
       dbus::ObjectPath(kDefaultProfilePath),
-      base::Bind(&ExpectDictionaryValueResultWithoutStatus, &value),
+      base::BindOnce(&ExpectDictionaryValueResultWithoutStatus, &value),
       error_callback.Get());
   EXPECT_CALL(error_callback, Run(_, _)).Times(0);
 
@@ -145,14 +145,15 @@
   base::DictionaryValue value;
   value.SetKey(shill::kTypeProperty, base::Value(shill::kTypeWifi));
   // Set expectations.
-  PrepareForMethodCall(shill::kGetEntryFunction,
-                       base::Bind(&ExpectStringArgument, kExampleEntryPath),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kGetEntryFunction,
+      base::BindRepeating(&ExpectStringArgument, kExampleEntryPath),
+      response.get());
   // Call method.
   base::MockCallback<ShillProfileClient::ErrorCallback> error_callback;
   client_->GetEntry(
       dbus::ObjectPath(kDefaultProfilePath), kExampleEntryPath,
-      base::Bind(&ExpectDictionaryValueResultWithoutStatus, &value),
+      base::BindOnce(&ExpectDictionaryValueResultWithoutStatus, &value),
       error_callback.Get());
   EXPECT_CALL(error_callback, Run(_, _)).Times(0);
   // Run the message loop.
@@ -167,11 +168,12 @@
   base::DictionaryValue value;
   value.SetKey(shill::kOfflineModeProperty, base::Value(true));
   // Set expectations.
-  PrepareForMethodCall(shill::kDeleteEntryFunction,
-                       base::Bind(&ExpectStringArgument, kExampleEntryPath),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kDeleteEntryFunction,
+      base::BindRepeating(&ExpectStringArgument, kExampleEntryPath),
+      response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillProfileClient::ErrorCallback> mock_error_callback;
   client_->DeleteEntry(dbus::ObjectPath(kDefaultProfilePath), kExampleEntryPath,
                        mock_closure.Get(), mock_error_callback.Get());
diff --git a/chromeos/dbus/shill/shill_service_client.cc b/chromeos/dbus/shill/shill_service_client.cc
index 5c382d6..8a6cfdc 100644
--- a/chromeos/dbus/shill/shill_service_client.cc
+++ b/chromeos/dbus/shill/shill_service_client.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/stl_util.h"
@@ -33,12 +34,11 @@
 ShillServiceClient* g_instance = nullptr;
 
 // Error callback for GetProperties.
-void OnGetDictionaryError(
-    const std::string& method_name,
-    const dbus::ObjectPath& service_path,
-    const ShillServiceClient::DictionaryValueCallback& callback,
-    const std::string& error_name,
-    const std::string& error_message) {
+void OnGetDictionaryError(const std::string& method_name,
+                          const dbus::ObjectPath& service_path,
+                          ShillServiceClient::DictionaryValueCallback callback,
+                          const std::string& error_name,
+                          const std::string& error_message) {
   const std::string log_string = "Failed to call org.chromium.shill.Service." +
                                  method_name + " for: " + service_path.value() +
                                  ": " + error_name + ": " + error_message;
@@ -52,7 +52,7 @@
     LOG(ERROR) << log_string;
 
   base::DictionaryValue empty_dictionary;
-  callback.Run(DBUS_METHOD_CALL_FAILURE, empty_dictionary);
+  std::move(callback).Run(DBUS_METHOD_CALL_FAILURE, empty_dictionary);
 }
 
 // The ShillServiceClient implementation.
@@ -84,135 +84,139 @@
   }
 
   void GetProperties(const dbus::ObjectPath& service_path,
-                     const DictionaryValueCallback& callback) override {
+                     DictionaryValueCallback callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kGetPropertiesFunction);
+    auto callback_adapted =
+        base::AdaptCallbackForRepeating(std::move(callback));
     GetHelper(service_path)
         ->CallDictionaryValueMethodWithErrorCallback(
-            &method_call, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS),
-            base::Bind(&OnGetDictionaryError, "GetProperties", service_path,
-                       callback));
+            &method_call,
+            base::BindOnce(callback_adapted, DBUS_METHOD_CALL_SUCCESS),
+            base::BindOnce(&OnGetDictionaryError, "GetProperties", service_path,
+                           callback_adapted));
   }
 
   void SetProperty(const dbus::ObjectPath& service_path,
                    const std::string& name,
                    const base::Value& value,
-                   const base::Closure& callback,
-                   const ErrorCallback& error_callback) override {
+                   base::OnceClosure callback,
+                   ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kSetPropertyFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(name);
     ShillClientHelper::AppendValueDataAsVariant(&writer, value);
     GetHelper(service_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void SetProperties(const dbus::ObjectPath& service_path,
                      const base::DictionaryValue& properties,
-                     const base::Closure& callback,
-                     const ErrorCallback& error_callback) override {
+                     base::OnceClosure callback,
+                     ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kSetPropertiesFunction);
     dbus::MessageWriter writer(&method_call);
     ShillClientHelper::AppendServicePropertiesDictionary(&writer, properties);
     GetHelper(service_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void ClearProperty(const dbus::ObjectPath& service_path,
                      const std::string& name,
-                     const base::Closure& callback,
-                     const ErrorCallback& error_callback) override {
+                     base::OnceClosure callback,
+                     ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kClearPropertyFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(name);
     GetHelper(service_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void ClearProperties(const dbus::ObjectPath& service_path,
                        const std::vector<std::string>& names,
-                       const ListValueCallback& callback,
-                       const ErrorCallback& error_callback) override {
+                       ListValueCallback callback,
+                       ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kClearPropertiesFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendArrayOfStrings(names);
     GetHelper(service_path)
-        ->CallListValueMethodWithErrorCallback(&method_call, callback,
-                                               error_callback);
+        ->CallListValueMethodWithErrorCallback(
+            &method_call, std::move(callback), std::move(error_callback));
   }
 
   void Connect(const dbus::ObjectPath& service_path,
-               const base::Closure& callback,
-               const ErrorCallback& error_callback) override {
+               base::OnceClosure callback,
+               ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kConnectFunction);
     GetHelper(service_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void Disconnect(const dbus::ObjectPath& service_path,
-                  const base::Closure& callback,
-                  const ErrorCallback& error_callback) override {
+                  base::OnceClosure callback,
+                  ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kDisconnectFunction);
     GetHelper(service_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void Remove(const dbus::ObjectPath& service_path,
-              const base::Closure& callback,
-              const ErrorCallback& error_callback) override {
+              base::OnceClosure callback,
+              ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kRemoveServiceFunction);
     GetHelper(service_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
   void ActivateCellularModem(const dbus::ObjectPath& service_path,
                              const std::string& carrier,
-                             const base::Closure& callback,
-                             const ErrorCallback& error_callback) override {
+                             base::OnceClosure callback,
+                             ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kActivateCellularModemFunction);
     dbus::MessageWriter writer(&method_call);
     writer.AppendString(carrier);
     GetHelper(service_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
-  void CompleteCellularActivation(
-      const dbus::ObjectPath& service_path,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) override {
+  void CompleteCellularActivation(const dbus::ObjectPath& service_path,
+                                  base::OnceClosure callback,
+                                  ErrorCallback error_callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kCompleteCellularActivationFunction);
     dbus::MessageWriter writer(&method_call);
     GetHelper(service_path)
-        ->CallVoidMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+        ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
   }
 
-  void GetLoadableProfileEntries(
-      const dbus::ObjectPath& service_path,
-      const DictionaryValueCallback& callback) override {
+  void GetLoadableProfileEntries(const dbus::ObjectPath& service_path,
+                                 DictionaryValueCallback callback) override {
     dbus::MethodCall method_call(shill::kFlimflamServiceInterface,
                                  shill::kGetLoadableProfileEntriesFunction);
+    auto callback_adapted =
+        base::AdaptCallbackForRepeating(std::move(callback));
     GetHelper(service_path)
         ->CallDictionaryValueMethodWithErrorCallback(
-            &method_call, base::Bind(callback, DBUS_METHOD_CALL_SUCCESS),
-            base::Bind(&OnGetDictionaryError, "GetLoadableProfileEntries",
-                       service_path, callback));
+            &method_call,
+            base::BindOnce(callback_adapted, DBUS_METHOD_CALL_SUCCESS),
+            base::BindOnce(&OnGetDictionaryError, "GetLoadableProfileEntries",
+                           service_path, callback_adapted));
   }
 
   ShillServiceClient::TestInterface* GetTestInterface() override {
@@ -233,8 +237,8 @@
         bus_->GetObjectProxy(shill::kFlimflamServiceName, service_path);
     ShillClientHelper* helper = new ShillClientHelper(object_proxy);
     helper->SetReleasedCallback(
-        base::Bind(&ShillServiceClientImpl::NotifyReleased,
-                   weak_ptr_factory_.GetWeakPtr()));
+        base::BindOnce(&ShillServiceClientImpl::NotifyReleased,
+                       weak_ptr_factory_.GetWeakPtr()));
     helper->MonitorPropertyChanged(shill::kFlimflamServiceInterface);
     helpers_.insert(HelperMap::value_type(service_path.value(), helper));
     return helper;
diff --git a/chromeos/dbus/shill/shill_service_client.h b/chromeos/dbus/shill/shill_service_client.h
index d8c2c0a..5502dd71 100644
--- a/chromeos/dbus/shill/shill_service_client.h
+++ b/chromeos/dbus/shill/shill_service_client.h
@@ -31,7 +31,6 @@
 // DBusThreadManager instance.
 class COMPONENT_EXPORT(SHILL_CLIENT) ShillServiceClient {
  public:
-  typedef ShillClientHelper::PropertyChangedHandler PropertyChangedHandler;
   typedef ShillClientHelper::DictionaryValueCallback DictionaryValueCallback;
   typedef ShillClientHelper::ListValueCallback ListValueCallback;
   typedef ShillClientHelper::ErrorCallback ErrorCallback;
@@ -101,7 +100,7 @@
     virtual void ClearServices() = 0;
 
     virtual void SetConnectBehavior(const std::string& service_path,
-                                    const base::Closure& behavior) = 0;
+                                    const base::RepeatingClosure& behavior) = 0;
 
     // If |hold_back| is set to true, stops sending service property updates to
     // observers and records them instead. Then if this is called again with
@@ -137,74 +136,72 @@
   // Calls GetProperties method.
   // |callback| is called after the method call succeeds.
   virtual void GetProperties(const dbus::ObjectPath& service_path,
-                             const DictionaryValueCallback& callback) = 0;
+                             DictionaryValueCallback callback) = 0;
 
   // Calls SetProperty method.
   // |callback| is called after the method call succeeds.
   virtual void SetProperty(const dbus::ObjectPath& service_path,
                            const std::string& name,
                            const base::Value& value,
-                           const base::Closure& callback,
-                           const ErrorCallback& error_callback) = 0;
+                           base::OnceClosure callback,
+                           ErrorCallback error_callback) = 0;
 
   // Calls SetProperties method.
   // |callback| is called after the method call succeeds.
   virtual void SetProperties(const dbus::ObjectPath& service_path,
                              const base::DictionaryValue& properties,
-                             const base::Closure& callback,
-                             const ErrorCallback& error_callback) = 0;
+                             base::OnceClosure callback,
+                             ErrorCallback error_callback) = 0;
 
   // Calls ClearProperty method.
   // |callback| is called after the method call succeeds.
   virtual void ClearProperty(const dbus::ObjectPath& service_path,
                              const std::string& name,
-                             const base::Closure& callback,
-                             const ErrorCallback& error_callback) = 0;
+                             base::OnceClosure callback,
+                             ErrorCallback error_callback) = 0;
 
   // Calls ClearProperties method.
   // |callback| is called after the method call succeeds.
   virtual void ClearProperties(const dbus::ObjectPath& service_path,
                                const std::vector<std::string>& names,
-                               const ListValueCallback& callback,
-                               const ErrorCallback& error_callback) = 0;
+                               ListValueCallback callback,
+                               ErrorCallback error_callback) = 0;
 
   // Calls Connect method.
   // |callback| is called after the method call succeeds.
   virtual void Connect(const dbus::ObjectPath& service_path,
-                       const base::Closure& callback,
-                       const ErrorCallback& error_callback) = 0;
+                       base::OnceClosure callback,
+                       ErrorCallback error_callback) = 0;
 
   // Calls Disconnect method.
   // |callback| is called after the method call succeeds.
   virtual void Disconnect(const dbus::ObjectPath& service_path,
-                          const base::Closure& callback,
-                          const ErrorCallback& error_callback) = 0;
+                          base::OnceClosure callback,
+                          ErrorCallback error_callback) = 0;
 
   // Calls Remove method.
   // |callback| is called after the method call succeeds.
   virtual void Remove(const dbus::ObjectPath& service_path,
-                      const base::Closure& callback,
-                      const ErrorCallback& error_callback) = 0;
+                      base::OnceClosure callback,
+                      ErrorCallback error_callback) = 0;
 
   // Calls ActivateCellularModem method.
   // |callback| is called after the method call succeeds.
   virtual void ActivateCellularModem(const dbus::ObjectPath& service_path,
                                      const std::string& carrier,
-                                     const base::Closure& callback,
-                                     const ErrorCallback& error_callback) = 0;
+                                     base::OnceClosure callback,
+                                     ErrorCallback error_callback) = 0;
 
   // Calls the CompleteCellularActivation method.
   // |callback| is called after the method call succeeds.
-  virtual void CompleteCellularActivation(
-      const dbus::ObjectPath& service_path,
-      const base::Closure& callback,
-      const ErrorCallback& error_callback) = 0;
+  virtual void CompleteCellularActivation(const dbus::ObjectPath& service_path,
+                                          base::OnceClosure callback,
+                                          ErrorCallback error_callback) = 0;
 
   // Calls the GetLoadableProfileEntries method.
   // |callback| is called after the method call succeeds.
-  virtual void GetLoadableProfileEntries(
-      const dbus::ObjectPath& service_path,
-      const DictionaryValueCallback& callback) = 0;
+  virtual void GetLoadableProfileEntries(const dbus::ObjectPath& service_path,
+                                         DictionaryValueCallback callback) = 0;
 
   // Returns an interface for testing (stub only), or returns null.
   virtual TestInterface* GetTestInterface() = 0;
diff --git a/chromeos/dbus/shill/shill_service_client_unittest.cc b/chromeos/dbus/shill/shill_service_client_unittest.cc
index 5027ac0c..5df8d5c 100644
--- a/chromeos/dbus/shill/shill_service_client_unittest.cc
+++ b/chromeos/dbus/shill/shill_service_client_unittest.cc
@@ -102,10 +102,10 @@
   base::DictionaryValue value;
   value.SetKey(shill::kSignalStrengthProperty, base::Value(kValue));
   PrepareForMethodCall(shill::kGetPropertiesFunction,
-                       base::Bind(&ExpectNoArgument), response.get());
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   // Call method.
   client_->GetProperties(dbus::ObjectPath(kExampleServicePath),
-                         base::Bind(&ExpectDictionaryValueResult, &value));
+                         base::BindOnce(&ExpectDictionaryValueResult, &value));
   // Run the message loop.
   base::RunLoop().RunUntilIdle();
 }
@@ -118,11 +118,11 @@
   // Set expectations.
   const base::Value value(kValue);
   PrepareForMethodCall(shill::kSetPropertyFunction,
-                       base::Bind(&ExpectStringAndValueArguments,
-                                  shill::kPassphraseProperty, &value),
+                       base::BindRepeating(&ExpectStringAndValueArguments,
+                                           shill::kPassphraseProperty, &value),
                        response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillServiceClient::ErrorCallback> mock_error_callback;
   client_->SetProperty(dbus::ObjectPath(kExampleServicePath),
                        shill::kPassphraseProperty, value, mock_closure.Get(),
@@ -142,13 +142,13 @@
   std::unique_ptr<base::DictionaryValue> arg(CreateExampleServiceProperties());
   // Use a variant valued dictionary rather than a string valued one.
   const bool string_valued = false;
-  PrepareForMethodCall(
-      shill::kSetPropertiesFunction,
-      base::Bind(&ExpectDictionaryValueArgument, arg.get(), string_valued),
-      response.get());
+  PrepareForMethodCall(shill::kSetPropertiesFunction,
+                       base::BindRepeating(&ExpectDictionaryValueArgument,
+                                           arg.get(), string_valued),
+                       response.get());
 
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillServiceClient::ErrorCallback> mock_error_callback;
   client_->SetProperties(dbus::ObjectPath(kExampleServicePath), *arg,
                          mock_closure.Get(), mock_error_callback.Get());
@@ -166,10 +166,10 @@
   // Set expectations.
   PrepareForMethodCall(
       shill::kClearPropertyFunction,
-      base::Bind(&ExpectStringArgument, shill::kPassphraseProperty),
+      base::BindRepeating(&ExpectStringArgument, shill::kPassphraseProperty),
       response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillServiceClient::ErrorCallback> mock_error_callback;
   client_->ClearProperty(dbus::ObjectPath(kExampleServicePath),
                          shill::kPassphraseProperty, mock_closure.Get(),
@@ -196,7 +196,7 @@
   keys.push_back(shill::kPassphraseProperty);
   keys.push_back(shill::kSignalStrengthProperty);
   PrepareForMethodCall(shill::kClearPropertiesFunction,
-                       base::Bind(&ExpectArrayOfStringsArgument, keys),
+                       base::BindRepeating(&ExpectArrayOfStringsArgument, keys),
                        response.get());
   // Call method.
   base::MockCallback<ShillServiceClient::ListValueCallback>
@@ -217,10 +217,10 @@
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
 
   // Set expectations.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillServiceClient::ErrorCallback> mock_error_callback;
-  PrepareForMethodCall(shill::kConnectFunction, base::Bind(&ExpectNoArgument),
-                       response.get());
+  PrepareForMethodCall(shill::kConnectFunction,
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   EXPECT_CALL(mock_closure, Run()).Times(1);
   // Call method.
   client_->Connect(dbus::ObjectPath(kExampleServicePath), mock_closure.Get(),
@@ -236,9 +236,9 @@
 
   // Set expectations.
   PrepareForMethodCall(shill::kDisconnectFunction,
-                       base::Bind(&ExpectNoArgument), response.get());
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillServiceClient::ErrorCallback> mock_error_callback;
   client_->Disconnect(dbus::ObjectPath(kExampleServicePath), mock_closure.Get(),
                       mock_error_callback.Get());
@@ -255,9 +255,9 @@
 
   // Set expectations.
   PrepareForMethodCall(shill::kRemoveServiceFunction,
-                       base::Bind(&ExpectNoArgument), response.get());
+                       base::BindRepeating(&ExpectNoArgument), response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillServiceClient::ErrorCallback> mock_error_callback;
   client_->Remove(dbus::ObjectPath(kExampleServicePath), mock_closure.Get(),
                   mock_error_callback.Get());
@@ -275,10 +275,10 @@
 
   // Set expectations.
   PrepareForMethodCall(shill::kActivateCellularModemFunction,
-                       base::Bind(&ExpectStringArgument, kCarrier),
+                       base::BindRepeating(&ExpectStringArgument, kCarrier),
                        response.get());
   // Call method.
-  base::MockCallback<base::Closure> mock_closure;
+  base::MockCallback<base::OnceClosure> mock_closure;
   base::MockCallback<ShillServiceClient::ErrorCallback> mock_error_callback;
   client_->ActivateCellularModem(dbus::ObjectPath(kExampleServicePath),
                                  kCarrier, mock_closure.Get(),
diff --git a/chromeos/dbus/shill/shill_third_party_vpn_driver_client.cc b/chromeos/dbus/shill/shill_third_party_vpn_driver_client.cc
index dafc778..0cbb10c8 100644
--- a/chromeos/dbus/shill/shill_third_party_vpn_driver_client.cc
+++ b/chromeos/dbus/shill/shill_third_party_vpn_driver_client.cc
@@ -52,23 +52,20 @@
   void RemoveShillThirdPartyVpnObserver(
       const std::string& object_path_value) override;
 
-  void SetParameters(
-      const std::string& object_path_value,
-      const base::DictionaryValue& parameters,
-      const ShillClientHelper::StringCallback& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) override;
+  void SetParameters(const std::string& object_path_value,
+                     const base::DictionaryValue& parameters,
+                     StringCallback callback,
+                     ErrorCallback error_callback) override;
 
-  void UpdateConnectionState(
-      const std::string& object_path_value,
-      const uint32_t connection_state,
-      const base::Closure& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) override;
+  void UpdateConnectionState(const std::string& object_path_value,
+                             const uint32_t connection_state,
+                             base::OnceClosure callback,
+                             ErrorCallback error_callback) override;
 
-  void SendPacket(
-      const std::string& object_path_value,
-      const std::vector<char>& ip_packet,
-      const base::Closure& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) override;
+  void SendPacket(const std::string& object_path_value,
+                  const std::vector<char>& ip_packet,
+                  base::OnceClosure callback,
+                  ErrorCallback error_callback) override;
 
   TestInterface* GetTestInterface() override { return nullptr; }
 
@@ -165,14 +162,15 @@
 
   proxy->ConnectToSignal(
       shill::kFlimflamThirdPartyVpnInterface, shill::kOnPlatformMessageFunction,
-      base::Bind(&ShillThirdPartyVpnDriverClientImpl::OnPlatformMessage,
-                 helper_info->GetWeakPtr()),
+      base::BindRepeating(
+          &ShillThirdPartyVpnDriverClientImpl::OnPlatformMessage,
+          helper_info->GetWeakPtr()),
       base::BindOnce(&ShillThirdPartyVpnDriverClientImpl::OnSignalConnected));
 
   proxy->ConnectToSignal(
       shill::kFlimflamThirdPartyVpnInterface, shill::kOnPacketReceivedFunction,
-      base::Bind(&ShillThirdPartyVpnDriverClientImpl::OnPacketReceived,
-                 helper_info->GetWeakPtr()),
+      base::BindRepeating(&ShillThirdPartyVpnDriverClientImpl::OnPacketReceived,
+                          helper_info->GetWeakPtr()),
       base::BindOnce(&ShillThirdPartyVpnDriverClientImpl::OnSignalConnected));
 }
 
@@ -206,8 +204,8 @@
 void ShillThirdPartyVpnDriverClientImpl::SetParameters(
     const std::string& object_path_value,
     const base::DictionaryValue& parameters,
-    const ShillClientHelper::StringCallback& callback,
-    const ShillClientHelper::ErrorCallback& error_callback) {
+    StringCallback callback,
+    ErrorCallback error_callback) {
   dbus::MethodCall method_call(shill::kFlimflamThirdPartyVpnInterface,
                                shill::kSetParametersFunction);
   dbus::MessageWriter writer(&method_call);
@@ -232,28 +230,29 @@
   }
   writer.CloseContainer(&array_writer);
   GetHelper(object_path_value)
-      ->CallStringMethodWithErrorCallback(&method_call, callback,
-                                          error_callback);
+      ->CallStringMethodWithErrorCallback(&method_call, std::move(callback),
+                                          std::move(error_callback));
 }
 
 void ShillThirdPartyVpnDriverClientImpl::UpdateConnectionState(
     const std::string& object_path_value,
     const uint32_t connection_state,
-    const base::Closure& callback,
-    const ShillClientHelper::ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   dbus::MethodCall method_call(shill::kFlimflamThirdPartyVpnInterface,
                                shill::kUpdateConnectionStateFunction);
   dbus::MessageWriter writer(&method_call);
   writer.AppendUint32(connection_state);
   GetHelper(object_path_value)
-      ->CallVoidMethodWithErrorCallback(&method_call, callback, error_callback);
+      ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                        std::move(error_callback));
 }
 
 void ShillThirdPartyVpnDriverClientImpl::SendPacket(
     const std::string& object_path_value,
     const std::vector<char>& ip_packet,
-    const base::Closure& callback,
-    const ShillClientHelper::ErrorCallback& error_callback) {
+    base::OnceClosure callback,
+    ErrorCallback error_callback) {
   dbus::MethodCall method_call(shill::kFlimflamThirdPartyVpnInterface,
                                shill::kSendPacketFunction);
   dbus::MessageWriter writer(&method_call);
@@ -262,7 +261,8 @@
   writer.AppendArrayOfBytes(reinterpret_cast<const uint8_t*>(ip_packet.data()),
                             ip_packet.size());
   GetHelper(object_path_value)
-      ->CallVoidMethodWithErrorCallback(&method_call, callback, error_callback);
+      ->CallVoidMethodWithErrorCallback(&method_call, std::move(callback),
+                                        std::move(error_callback));
 }
 
 // static
diff --git a/chromeos/dbus/shill/shill_third_party_vpn_driver_client.h b/chromeos/dbus/shill/shill_third_party_vpn_driver_client.h
index 4ec6abde..81a287f 100644
--- a/chromeos/dbus/shill/shill_third_party_vpn_driver_client.h
+++ b/chromeos/dbus/shill/shill_third_party_vpn_driver_client.h
@@ -32,6 +32,9 @@
 // DBusThreadManager instance.
 class COMPONENT_EXPORT(SHILL_CLIENT) ShillThirdPartyVpnDriverClient {
  public:
+  using ErrorCallback = ShillClientHelper::ErrorCallback;
+  using StringCallback = ShillClientHelper::StringCallback;
+
   class TestInterface {
    public:
     virtual void OnPacketReceived(const std::string& object_path_value,
@@ -68,27 +71,24 @@
 
   // Calls SetParameters method.
   // |callback| is called after the method call succeeds.
-  virtual void SetParameters(
-      const std::string& object_path_value,
-      const base::DictionaryValue& parameters,
-      const ShillClientHelper::StringCallback& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) = 0;
+  virtual void SetParameters(const std::string& object_path_value,
+                             const base::DictionaryValue& parameters,
+                             StringCallback callback,
+                             ErrorCallback error_callback) = 0;
 
   // Calls UpdateConnectionState method.
   // |callback| is called after the method call succeeds.
-  virtual void UpdateConnectionState(
-      const std::string& object_path_value,
-      const uint32_t connection_state,
-      const base::Closure& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) = 0;
+  virtual void UpdateConnectionState(const std::string& object_path_value,
+                                     const uint32_t connection_state,
+                                     base::OnceClosure callback,
+                                     ErrorCallback error_callback) = 0;
 
   // Calls SendPacket method.
   // |callback| is called after the method call succeeds.
-  virtual void SendPacket(
-      const std::string& object_path_value,
-      const std::vector<char>& ip_packet,
-      const base::Closure& callback,
-      const ShillClientHelper::ErrorCallback& error_callback) = 0;
+  virtual void SendPacket(const std::string& object_path_value,
+                          const std::vector<char>& ip_packet,
+                          base::OnceClosure callback,
+                          ErrorCallback error_callback) = 0;
 
   // Returns an interface for testing (stub only), or returns nullptr.
   virtual ShillThirdPartyVpnDriverClient::TestInterface* GetTestInterface() = 0;
diff --git a/chromeos/dbus/shill/shill_third_party_vpn_driver_client_unittest.cc b/chromeos/dbus/shill/shill_third_party_vpn_driver_client_unittest.cc
index 4185581..a298d62 100644
--- a/chromeos/dbus/shill/shill_third_party_vpn_driver_client_unittest.cc
+++ b/chromeos/dbus/shill/shill_third_party_vpn_driver_client_unittest.cc
@@ -101,16 +101,17 @@
   std::unique_ptr<dbus::Response> response(dbus::Response::CreateEmpty());
   uint32_t connection_state = 2;
 
-  PrepareForMethodCall(shill::kUpdateConnectionStateFunction,
-                       base::Bind(&ExpectUint32Argument, connection_state),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kUpdateConnectionStateFunction,
+      base::BindRepeating(&ExpectUint32Argument, connection_state),
+      response.get());
 
   EXPECT_CALL(*this, MockSuccess()).Times(0);
   client_->UpdateConnectionState(
       kExampleIPConfigPath, connection_state,
-      base::Bind(&ShillThirdPartyVpnDriverClientTest::MockSuccess,
-                 base::Unretained(this)),
-      base::Bind(&Failure));
+      base::BindOnce(&ShillThirdPartyVpnDriverClientTest::MockSuccess,
+                     base::Unretained(this)),
+      base::BindOnce(&Failure));
 
   client_->RemoveShillThirdPartyVpnObserver(kExampleIPConfigPath);
   testing::Mock::VerifyAndClearExpectations(this);
@@ -144,14 +145,15 @@
 
   PrepareForMethodCall(
       shill::kSetParametersFunction,
-      base::Bind(&ExpectDictionaryValueArgument, &parameters, true),
+      base::BindRepeating(&ExpectDictionaryValueArgument, &parameters, true),
       response.get());
 
   client_->SetParameters(
       kExampleIPConfigPath, parameters,
-      base::Bind(&ShillThirdPartyVpnDriverClientTest::MockSuccessWithWarning,
-                 base::Unretained(this)),
-      base::Bind(&Failure));
+      base::BindOnce(
+          &ShillThirdPartyVpnDriverClientTest::MockSuccessWithWarning,
+          base::Unretained(this)),
+      base::BindOnce(&Failure));
 
   base::RunLoop().RunUntilIdle();
 }
@@ -162,15 +164,16 @@
 
   EXPECT_CALL(*this, MockSuccess()).Times(1);
 
-  PrepareForMethodCall(shill::kUpdateConnectionStateFunction,
-                       base::Bind(&ExpectUint32Argument, connection_state),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kUpdateConnectionStateFunction,
+      base::BindRepeating(&ExpectUint32Argument, connection_state),
+      response.get());
 
   client_->UpdateConnectionState(
       kExampleIPConfigPath, connection_state,
-      base::Bind(&ShillThirdPartyVpnDriverClientTest::MockSuccess,
-                 base::Unretained(this)),
-      base::Bind(&Failure));
+      base::BindOnce(&ShillThirdPartyVpnDriverClientTest::MockSuccess,
+                     base::Unretained(this)),
+      base::BindOnce(&Failure));
 
   base::RunLoop().RunUntilIdle();
 }
@@ -183,16 +186,17 @@
 
   EXPECT_CALL(*this, MockSuccess()).Times(1);
 
-  PrepareForMethodCall(shill::kSendPacketFunction,
-                       base::Bind(&ExpectArrayOfBytesArgument,
-                                  std::string(data.begin(), data.end())),
-                       response.get());
+  PrepareForMethodCall(
+      shill::kSendPacketFunction,
+      base::BindRepeating(&ExpectArrayOfBytesArgument,
+                          std::string(data.begin(), data.end())),
+      response.get());
 
   client_->SendPacket(
       kExampleIPConfigPath, data,
-      base::Bind(&ShillThirdPartyVpnDriverClientTest::MockSuccess,
-                 base::Unretained(this)),
-      base::Bind(&Failure));
+      base::BindOnce(&ShillThirdPartyVpnDriverClientTest::MockSuccess,
+                     base::Unretained(this)),
+      base::BindOnce(&Failure));
 
   base::RunLoop().RunUntilIdle();
 }
diff --git a/chromeos/dbus/shill/sms_client.cc b/chromeos/dbus/shill/sms_client.cc
index 006f98b..670f22ef 100644
--- a/chromeos/dbus/shill/sms_client.cc
+++ b/chromeos/dbus/shill/sms_client.cc
@@ -40,8 +40,8 @@
       : callback_(std::move(callback)), sms_received_(false) {
     property_set_ = std::make_unique<dbus::PropertySet>(
         object_proxy, modemmanager::kModemManager1SmsInterface,
-        base::Bind(&SMSReceiveHandler::OnPropertyChanged,
-                   weak_ptr_factory_.GetWeakPtr()));
+        base::BindRepeating(&SMSReceiveHandler::OnPropertyChanged,
+                            weak_ptr_factory_.GetWeakPtr()));
     property_set_->RegisterProperty(SMSClient::kSMSPropertyState, &state_);
     property_set_->ConnectSignals();
     property_set_->Get(&state_, dbus::PropertySet::GetCallback());
diff --git a/chromeos/dbus/system_clock/system_clock_client.cc b/chromeos/dbus/system_clock/system_clock_client.cc
index a3e4dbe..f788341 100644
--- a/chromeos/dbus/system_clock/system_clock_client.cc
+++ b/chromeos/dbus/system_clock/system_clock_client.cc
@@ -106,8 +106,8 @@
         dbus::ObjectPath(system_clock::kSystemClockServicePath));
     system_clock_proxy_->ConnectToSignal(
         system_clock::kSystemClockInterface, system_clock::kSystemClockUpdated,
-        base::Bind(&SystemClockClientImpl::TimeUpdatedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&SystemClockClientImpl::TimeUpdatedReceived,
+                            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&SystemClockClientImpl::TimeUpdatedConnected,
                        weak_ptr_factory_.GetWeakPtr()));
     WaitForServiceToBeAvailable(
diff --git a/chromeos/dbus/upstart/upstart_client.cc b/chromeos/dbus/upstart/upstart_client.cc
index f14eecf..49fa361 100644
--- a/chromeos/dbus/upstart/upstart_client.cc
+++ b/chromeos/dbus/upstart/upstart_client.cc
@@ -42,10 +42,10 @@
         arc::kArcServiceName, dbus::ObjectPath(arc::kArcServicePath));
     arc_proxy->ConnectToSignal(
         arc::kArcInterfaceName, arc::kArcStopped,
-        base::Bind(&UpstartClientImpl::ArcStoppedReceived,
-                   weak_ptr_factory_.GetWeakPtr()),
-        base::Bind(&UpstartClientImpl::SignalConnected,
-                   weak_ptr_factory_.GetWeakPtr()));
+        base::BindRepeating(&UpstartClientImpl::ArcStoppedReceived,
+                            weak_ptr_factory_.GetWeakPtr()),
+        base::BindOnce(&UpstartClientImpl::SignalConnected,
+                       weak_ptr_factory_.GetWeakPtr()));
   }
 
   ~UpstartClientImpl() override = default;
diff --git a/chromeos/dbus/usb/usbguard_client.cc b/chromeos/dbus/usb/usbguard_client.cc
index d941271..9832052 100644
--- a/chromeos/dbus/usb/usbguard_client.cc
+++ b/chromeos/dbus/usb/usbguard_client.cc
@@ -52,8 +52,8 @@
     usbguard_proxy_->ConnectToSignal(
         usbguard::kUsbguardDevicesInterface,
         usbguard::kDevicePolicyChangedSignalName,
-        base::Bind(&UsbguardClientImpl::DevicePolicyChanged,
-                   weak_ptr_factory_.GetWeakPtr()),
+        base::BindRepeating(&UsbguardClientImpl::DevicePolicyChanged,
+                            weak_ptr_factory_.GetWeakPtr()),
         base::BindOnce(&UsbguardClientImpl::OnSignalConnected,
                        weak_ptr_factory_.GetWeakPtr()));
   }
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index 7e245e2..3312924 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -533,9 +533,12 @@
 }
 
 void CreditCardAccessManager::OnDidCancelCardVerification() {
+  // TODO(crbug.com/949269): Add tests and logging for canceling verify pending
+  // dialog.
   GetOrCreateFIDOAuthenticator()->CancelVerification();
   unmask_details_request_in_progress_ = false;
   is_authentication_in_progress_ = false;
+  SignalCanFetchUnmaskDetails();
 }
 #endif
 
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 03984c0..be17f4a 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -1695,7 +1695,10 @@
     optional string label = 1;
 
     // Text shown below the label. Optional.
-    optional string subtext = 5;
+    optional string description_line_1 = 5;
+
+    // Text shown below |description_line_2|. Optional.
+    optional string description_line_2 = 8;
 
     // The possible values this counter can have. If empty, the possible values
     // will be all integer values between |min_value| and |max_value|.
@@ -1827,6 +1830,12 @@
     // The label of this choice.
     optional string label = 1;
 
+    // Text shown below the label. Optional.
+    optional string description_line_1 = 3;
+
+    // Text shown below |description_line_2|. Optional.
+    optional string description_line_2 = 4;
+
     // Whether this choice is selected by default. If |allow_multiple| is false,
     // then maximum 1 Choice can have this set to true, otherwise the associated
     // FormAction will fail.
diff --git a/components/optimization_guide/optimization_guide_decider.h b/components/optimization_guide/optimization_guide_decider.h
index 943362df..051a663 100644
--- a/components/optimization_guide/optimization_guide_decider.h
+++ b/components/optimization_guide/optimization_guide_decider.h
@@ -58,16 +58,6 @@
       proto::OptimizationType optimization_type,
       OptimizationMetadata* optimization_metadata) = 0;
 
-  // Returns whether the current conditions match |optimization_target| and
-  // |optimization_type| can be applied for the URL associated with
-  // |navigation_handle|.
-  virtual OptimizationGuideDecision
-  ShouldTargetNavigationAndCanApplyOptimization(
-      content::NavigationHandle* navigation_handle,
-      proto::OptimizationTarget optimization_target,
-      proto::OptimizationType optimization_type,
-      OptimizationMetadata* optimization_metadata) = 0;
-
  protected:
   OptimizationGuideDecider() {}
   virtual ~OptimizationGuideDecider() {}
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 5c0f8b9..2df8671 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -997,6 +997,7 @@
         'UserNativePrintersAllowed',
         'ExternalPrintServers',
         'ExternalPrintServersWhitelist',
+        'PrinterTypeDenyList',
       ]
     },
     {
@@ -7023,7 +7024,9 @@
 
       If this setting is enabled or not configured, users can print to <ph name="CLOUD_PRINT_NAME">Google Cloud Print</ph> from the <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> print dialog.
 
-      If this setting is disabled, users cannot print to <ph name="CLOUD_PRINT_NAME">Google Cloud Print</ph> from the <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> print dialog''',
+      If this setting is disabled, users cannot print to <ph name="CLOUD_PRINT_NAME">Google Cloud Print</ph> from the <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> print dialog.
+
+      In order to keep <ph name="CLOUD_PRINT_NAME">Google Cloud Print</ph> destinations discoverable, this policy must be set to true and 'cloud' must not be included in the 'PrinterTypeDenyList' policy.''',
     },
     {
       'name': 'EnterpriseWebStoreURL',
@@ -19047,7 +19050,72 @@
       If you set this policy, users cannot change or override it.
 
       If this policy is left unset, the Click to Call feature is enabled by default.''',
-    }
+    },
+    {
+      'name': 'PrinterTypeDenyList',
+      'owners': ['file://printing/OWNERS'],
+      'type': 'string-enum-list',
+      'schema': {
+        'type': 'array',
+        'items': {
+          'type': 'string',
+          'enum': [
+            'privet',
+            'extension',
+            'pdf',
+            'local',
+            'cloud',
+          ],
+        },
+      },
+      'items': [
+        {
+          'name': 'privet',
+          'value': 'privet',
+          'caption': '''Zeroconf-based (mDNS + DNS-SD) protocol destinations''',
+        },
+        {
+          'name': 'extension',
+          'value': 'extension',
+          'caption': '''Extension-based destinations''',
+        },
+        {
+          'name': 'pdf',
+          'value': 'pdf',
+          'caption': '''The 'Save as PDF' destination''',
+        },
+        {
+          'name': 'local',
+          'value': 'local',
+          'caption': '''Local printer destinations''',
+        },
+        {
+          'name': 'cloud',
+          'value': 'cloud',
+          'caption': '''<ph name="CLOUD_PRINT_NAME">Google Cloud Print</ph> and 'Save to Google Drive' destinations''',
+        },
+      ],
+      'supported_on': ['chrome_os:80-', 'chrome.*:80-'],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True,
+      },
+      'example_value': ['cloud', 'privet'],
+      'id': 647,
+      'caption': '''Disable printer types on the deny list''',
+      'tags': [],
+      'desc': '''The printers of types placed on the deny list will be disabled from being discovered or having their capabilities fetched.
+
+      Placing all printer types on the deny list effectively disables printing, as there would be no available destinations to send a document for printing.
+
+      Including 'cloud' on the deny list has the same effect as setting the 'CloudPrintSubmitEnabled' policy to false. In order to keep <ph name="CLOUD_PRINT_NAME">Google Cloud Print</ph> destinations discoverable, the 'CloudPrintSubmitEnabled' policy must be set to true and 'cloud' must not be on the deny list.
+
+      If the policy is not set, or is set to an empty list, all printer types will be available for discovery.
+
+      Extension printers are also known as print provider destinations, and include any destination that belongs to a <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> extension.
+
+      Local printers are also known as native printing destinations, and include destinations available to the local machine and shared network printers.'''
+    },
   ],
 
   'messages': {
@@ -19872,6 +19940,6 @@
   ],
   'placeholders': [],
   'deleted_policy_ids': [412, 546, 562, 569],
-  'highest_id_currently_used': 646,
+  'highest_id_currently_used': 647,
   'highest_atomic_group_id_currently_used': 38
 }
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc
index d566ff98..dbf69dd 100644
--- a/components/previews/content/previews_decider_impl_unittest.cc
+++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -172,15 +172,6 @@
       override {
     return optimization_guide::OptimizationGuideDecision::kFalse;
   }
-  optimization_guide::OptimizationGuideDecision
-  ShouldTargetNavigationAndCanApplyOptimization(
-      content::NavigationHandle* navigation_handle,
-      optimization_guide::proto::OptimizationTarget optimization_target,
-      optimization_guide::proto::OptimizationType optimization_type,
-      optimization_guide::OptimizationMetadata* optimization_metadata)
-      override {
-    return optimization_guide::OptimizationGuideDecision::kFalse;
-  }
 };
 
 // Stub class of PreviewsOptimizationGuide to control what is allowed when
diff --git a/components/previews/content/previews_optimization_guide_unittest.cc b/components/previews/content/previews_optimization_guide_unittest.cc
index 828867f..7fa639c 100644
--- a/components/previews/content/previews_optimization_guide_unittest.cc
+++ b/components/previews/content/previews_optimization_guide_unittest.cc
@@ -94,18 +94,6 @@
     return std::get<0>(response);
   }
 
-  optimization_guide::OptimizationGuideDecision
-  ShouldTargetNavigationAndCanApplyOptimization(
-      content::NavigationHandle* navigation_handle,
-      optimization_guide::proto::OptimizationTarget optimization_target,
-      optimization_guide::proto::OptimizationType optimization_type,
-      optimization_guide::OptimizationMetadata* optimization_metadata)
-      override {
-    // Should not be called.
-    EXPECT_TRUE(false);
-    return optimization_guide::OptimizationGuideDecision::kFalse;
-  }
-
   void SetResponses(
       std::map<std::tuple<GURL, optimization_guide::proto::OptimizationType>,
                std::tuple<optimization_guide::OptimizationGuideDecision,
diff --git a/components/viz/service/display_embedder/skia_output_device_vulkan.cc b/components/viz/service/display_embedder/skia_output_device_vulkan.cc
index d347a2e4..be4ccc0 100644
--- a/components/viz/service/display_embedder/skia_output_device_vulkan.cc
+++ b/components/viz/service/display_embedder/skia_output_device_vulkan.cc
@@ -50,7 +50,8 @@
 
   uint32_t generation = 0;
   if (!vulkan_surface_) {
-    CreateVulkanSurface();
+    if (!CreateVulkanSurface())
+      return false;
   } else {
     generation = vulkan_surface_->swap_chain_generation();
   }
@@ -147,7 +148,7 @@
   scoped_write_.reset();
 }
 
-void SkiaOutputDeviceVulkan::CreateVulkanSurface() {
+bool SkiaOutputDeviceVulkan::CreateVulkanSurface() {
   gfx::AcceleratedWidget accelerated_widget = gfx::kNullAcceleratedWidget;
 #if defined(OS_ANDROID)
   bool can_be_used_with_surface_control = false;
@@ -160,13 +161,17 @@
   auto vulkan_surface =
       context_provider_->GetVulkanImplementation()->CreateViewSurface(
           accelerated_widget);
-  if (!vulkan_surface)
-    LOG(FATAL) << "Failed to create vulkan surface.";
+  if (!vulkan_surface) {
+    LOG(ERROR) << "Failed to create vulkan surface.";
+    return false;
+  }
   if (!vulkan_surface->Initialize(context_provider_->GetDeviceQueue(),
                                   gpu::VulkanSurface::FORMAT_RGBA_32)) {
-    LOG(FATAL) << "Failed to initialize vulkan surface.";
+    LOG(ERROR) << "Failed to initialize vulkan surface.";
+    return false;
   }
   vulkan_surface_ = std::move(vulkan_surface);
+  return true;
 }
 
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/skia_output_device_vulkan.h b/components/viz/service/display_embedder/skia_output_device_vulkan.h
index 1b13f6b..129cd9f 100644
--- a/components/viz/service/display_embedder/skia_output_device_vulkan.h
+++ b/components/viz/service/display_embedder/skia_output_device_vulkan.h
@@ -42,7 +42,7 @@
   void EndPaint(const GrBackendSemaphore& semaphore) override;
 
  private:
-  void CreateVulkanSurface();
+  bool CreateVulkanSurface();
   void CreateSkSurface();
 
   VulkanContextProvider* const context_provider_;
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 28dca07..4a6bc44 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
@@ -1369,7 +1369,7 @@
     } else {
       gl_surface_ = nullptr;
       context_state_ = nullptr;
-      LOG(FATAL) << "Failed to make current during initialization.";
+      LOG(ERROR) << "Failed to make current during initialization.";
       return false;
     }
   }
diff --git a/components/viz/test/test_gpu_service_holder.cc b/components/viz/test/test_gpu_service_holder.cc
index 75216aa9..ad5b45b 100644
--- a/components/viz/test/test_gpu_service_holder.cc
+++ b/components/viz/test/test_gpu_service_holder.cc
@@ -15,6 +15,7 @@
 #include "base/no_destructor.h"
 #include "base/synchronization/lock.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/viz/service/gl/gpu_service_impl.h"
 #include "gpu/command_buffer/service/service_utils.h"
@@ -48,16 +49,43 @@
 bool g_should_register_listener = true;
 bool g_registered_listener = false;
 
-class InstanceResetter : public testing::EmptyTestEventListener {
+class InstanceResetter
+    : public testing::EmptyTestEventListener,
+      public base::test::TaskEnvironment::DestructionObserver {
  public:
-  InstanceResetter() = default;
-  ~InstanceResetter() override = default;
+  InstanceResetter() {
+    base::test::TaskEnvironment::AddDestructionObserver(this);
+  }
 
+  ~InstanceResetter() override {
+    base::test::TaskEnvironment::RemoveDestructionObserver(this);
+  }
+
+  // testing::EmptyTestEventListener:
   void OnTestEnd(const testing::TestInfo& test_info) override {
+    {
+      base::AutoLock locked(GetLock());
+      // Make sure the TestGpuServiceHolder instance is not re-created after
+      // WillDestroyCurrentTaskEnvironment().
+      // Otherwise we'll end up with GPU tasks weirdly running in a different
+      // context after the test.
+      DCHECK(!(reset_by_task_env && g_holder))
+          << "TestGpuServiceHolder was re-created after "
+             "base::test::TaskEnvironment was destroyed.";
+    }
+    reset_by_task_env = false;
+    TestGpuServiceHolder::ResetInstance();
+  }
+
+  // base::test::TaskEnvironment::DestructionObserver:
+  void WillDestroyCurrentTaskEnvironment() override {
+    reset_by_task_env = true;
     TestGpuServiceHolder::ResetInstance();
   }
 
  private:
+  bool reset_by_task_env = false;
+
   DISALLOW_COPY_AND_ASSIGN(InstanceResetter);
 };
 
@@ -74,6 +102,7 @@
     g_registered_listener = true;
     testing::TestEventListeners& listeners =
         testing::UnitTest::GetInstance()->listeners();
+    // |listeners| assumes ownership of InstanceResetter.
     listeners.Append(new InstanceResetter);
   }
 
diff --git a/content/browser/devtools/protocol/storage_handler.cc b/content/browser/devtools/protocol/storage_handler.cc
index df331b1..f789246 100644
--- a/content/browser/devtools/protocol/storage_handler.cc
+++ b/content/browser/devtools/protocol/storage_handler.cc
@@ -518,7 +518,7 @@
     return response;
   *storage_partition =
       BrowserContext::GetDefaultStoragePartition(browser_context);
-  if (!storage_partition_)
+  if (!*storage_partition)
     return Response::InternalError();
   return Response::OK();
 }
diff --git a/content/browser/indexed_db/docs/BlobStateDiagram.dot b/content/browser/indexed_db/docs/BlobStateDiagram.dot
new file mode 100644
index 0000000..1ef5a3b
--- /dev/null
+++ b/content/browser/indexed_db/docs/BlobStateDiagram.dot
@@ -0,0 +1,84 @@
+digraph IndexedDBBlobStateDiagram {
+  graph [nodesep="0.8", ranksep="0.8"];
+  node [shape=box];
+  
+  PutWithABlob [fontname="Courier New", label="os.put(blob, key)" shape=oval style=bold];
+  UnlinkedPendingInactive
+    [label=<<B>Unlinked</B>, <B>Pending</B> (Inactive)>
+     color="#000099"];
+  UnlinkedPendingActive
+    [label=<<B>Unlinked</B>, <B>Pending</B> (Active)>
+     color="#000099"];
+
+  LinkedPersistedInactive
+    [label=<<B>Linked</B>, <B>Persisted</B> (Inactive)>
+     color="#009900"];
+  LinkedPersistedActive
+    [label=<<B>Linked</B>, <B>Persisted</B> (Active)>
+     color="#009900"];
+
+  UnlinkedPersistedActive
+    [label=<<B>Unlinked</B>, <B>Persisted</B> (Active)>
+     color="#999900"];
+  UnlinkedPersistedInactive
+    [label=<<B>Unlinked</B>, <B>Persisted</B> (Inactive)<br/>(doomed, inaccessible)>
+     color="#999900"];
+
+  Deleted [label=<<B>Deleted</B>> shape=oval style=bold];
+
+  subgraph cluster_active {
+    style="invisible"
+    rank="left"
+    UnlinkedPendingActive
+    LinkedPersistedActive
+    UnlinkedPersistedActive
+  }
+
+  subgraph cluster_inactive {
+    style="invisible"
+    rank="right"
+    UnlinkedPendingInactive
+    LinkedPersistedInactive
+    UnlinkedPersistedInactive
+  }
+
+  PutWithABlob -> UnlinkedPendingInactive;
+  UnlinkedPendingInactive -> LinkedPersistedInactive
+      [label=<Transaction is committed.<br/>
+<FONT face="Courier New">txn.commit();</FONT>>];
+
+  UnlinkedPendingInactive -> UnlinkedPendingActive
+      [label=<Get is called on the transaction.<br/>
+<FONT face="courier new">let blob = os.get(key);</FONT>>];
+
+  UnlinkedPendingActive -> UnlinkedPendingInactive
+      [label=<Blob from Get is dropped.<br/>
+<FONT face="Courier New">blob = null;</FONT>>];
+      
+  UnlinkedPendingActive -> LinkedPersistedActive
+      [label=<Transaction is committed.<br/>
+<FONT face="Courier New">txn.commit()</FONT>>];
+
+  LinkedPersistedInactive -> LinkedPersistedActive
+      [label=<Get is called.<br/>
+<FONT face="courier new">let blob = os.get(key);</FONT>>];
+
+  LinkedPersistedInactive -> UnlinkedPersistedInactive
+      [label=<The object store row is deleted.<br/>
+<FONT face="courier new">os.delete(key);</FONT>>];
+      
+      
+  LinkedPersistedActive -> UnlinkedPersistedActive
+      [label=<The object store row is deleted.<br/>
+<FONT face="courier new">os.delete(key);</FONT>>];
+      
+  LinkedPersistedActive -> LinkedPersistedInactive
+      [label=<Blob from Get is dropped.<br/>
+<FONT face="Courier New">blob = null;</FONT>>];
+      
+  UnlinkedPersistedActive -> UnlinkedPersistedInactive
+      [label=<Blob from Get is dropped.<br/>
+<FONT face="Courier New">blob = null;</FONT>>];
+      
+  UnlinkedPersistedInactive -> Deleted [label=" Recovery journal cleanup."];
+}
\ No newline at end of file
diff --git a/content/browser/indexed_db/docs/BlobStateDiagram.png b/content/browser/indexed_db/docs/BlobStateDiagram.png
new file mode 100644
index 0000000..c1407b4
--- /dev/null
+++ b/content/browser/indexed_db/docs/BlobStateDiagram.png
Binary files differ
diff --git a/content/browser/indexed_db/docs/README.md b/content/browser/indexed_db/docs/README.md
new file mode 100644
index 0000000..b56f8b9
--- /dev/null
+++ b/content/browser/indexed_db/docs/README.md
@@ -0,0 +1,153 @@
+
+
+# IndexedDB
+
+## Status
+Note - this document is incomplete & under construction.
+
+Currently this terminology in NOT reflected in the codebase yet.
+
+## Common Terminology
+### (IndexedDB) Backing Store
+The backing store represents all IndexedDB data for an origin. This includes
+all IndexedDB databases for that origin. A backing store has a dedicated
+leveldb database where all data for that origin is stored.
+### LevelDB Database
+A leveldb database represents a leveldatabase database. Each backing store has
+a unique leveldb database, located at:
+`IndexedDB/<serialized_origin>.leveldb/`
+
+### IndexedDB Database
+An IndexedDB database represents a database in the IndexedDB API. The backing
+store can contain many IndexedDB  databases. So a single LevelDB database has
+multiple IndexedDB databases.
+
+IndexedDB databases are identified by a unique auto-incrementing `int64_t`
+`database_id`.
+
+## Blob Storage
+Blob are supported in IndexedDB to allow large values to be stored in IndexedDB
+without needing to save them directly into leveldb (which doesn't work well
+with large values).
+
+
+### Blob States
+Blob can be in the following states:
+
+#### _Linked_ vs _Unlinked_
+A **linked** blob is a blob that has a saved reference in the database by a
+BlobEntryKey. If a blob has been deleted from the database, or if it has not
+been written yet, it is **unlinked**.
+
+#### _Persisted_ vs _Pending_
+A **persisted** blob is stored on disk in the IndexedDB system. A **pending**
+blob is backed by an external client, and has not been stored on disk in the
+IndexedDB system.
+
+When the database is in in-memory or 'incognito' mode, blobs are never written
+to disk. This means they always stay **pending**.
+
+#### _Active_ vs _Inactive_
+An **active** blob has an active reference in some client. An **inactive** blob
+has no client references.
+
+#### State combinations, on-disk
+
+|               | **Linked** | **Unlinked** |
+| ------------: | :--------: | :----------: |
+| **Persisted** | ✓          | Active: ✓ Inactive: 🛇<br>(well, ✓ but deleted soon) |
+| **Pending**   | 🛇          | ✓            |
+
+#### State combinations, in-memory (incognito)
+Blobs are never persisted to disk in incognito mode.
+
+|               | **Linked** | **Unlinked** |
+| ------------: | :--------: | :----------: |
+| **Persisted** | 🛇          | 🛇          |
+| **Pending**   | ✓          | ✓            |
+
+#### State Flow Diagram, on-disk
+
+![Blob state diagram](BlobStateDiagram.png)
+### Blob Terminology
+#### Blob Number
+A blob number is a `int64_t` number that is unique and auto-incrementing per
+database in an origin. This is used to uniquely identify a blob in an IndexedDB
+database.
+
+#### Blob Key
+A blob key is a `int64_t` `database_id` and a unique auto-incrementing
+`int64_t` `blob_number`. The database metadata contains a
+`BlobKeyGeneratorCurrentNumber` which is used to save the next blob key number
+to use.
+
+A blob key uniquely identifies a blob in an backing store.
+
+####  Blob File
+A blob file is the physical file stored on disk that represents a **persisted**
+blob. It resides in the following directory:
+`IndexedDB/<serialized_origin>.blob/<database_id>/<blob_number>`
+Note that `database_id` + `blob_number` is a unique blob key.
+
+A blob that has been **persisted** in the database is stored in a blob file.
+This file is deleted when the blob is **unlinked** and **inactive**.
+(Technically this is done by adding the corresponding blob key is added to the
+recovery journal, which is processed every once in a while to delete unused
+files).
+
+There is a 1:1 mapping between a blob file and a blob key.
+
+#### Blob Handle
+This is-a `storage::BlobDataHandle` OR a `blink::BlobDataHandle` OR a
+`mojo::Remote<storage::mojom::Blob>`.
+
+Blob handles basically hold a reference to the given blob, which allows
+IndexedDB (or a client) to keep the blob alive and/or read the blob.
+
+#### Blob Info
+A  blob info usually is-a [IndexedDBBlobInfo](../indexed_db_blob_info.h), and
+basically contains all of the information needed to read a blob. This means it
+has a blob handle if it is a **pending** blob, and/or the file information if
+it is a **persisted** blob.
+
+If the blob handle is present, that means this holds a reference to that blob
+and keeps it alive in the blob system.
+
+#### Blob Entry
+A blob entry contains a vector of blob infos that are **persisted** in the
+database. All of these blob infos, at least initially, do not have a blob
+handle, and only contain the blob key & blob file information (like size, type,
+time modified, etc).
+
+A Blob Entry is a value that is saved into the database using a `BlobEntryKey`:
+
+#### `BlobEntryKey`
+A `BlobEntryKey` is used as a key in the leveldb database and contains the
+`database_id`, `object_store_id`, and `user_object_store_data_key`. A
+`BlobEntryKey` can be used to look up a blob entry in the database for the
+given object store data key.
+
+#### Recovery Journal
+The recovery journal is a list of blob keys that are pending deletion. These
+blob keys represent blobs that are in an **unlinked**, **inactive**, and
+**persisted** state.
+
+This is used to maintain consistency if a crash occurs or if there is an error
+while committing a transaction. The recovery journal is "processed"
+intermittently when there isn't an active transaction. Processing means that
+every blob file referenced in the journal (by the blob key) is deleted, and the
+journal is cleared.
+
+The recovery journal is where all blob keys that are to-be-deleted by a
+transaction are placed. (They are subsequently immediately deleted after the
+transaction is committed, but this can fail / crash so they are placed in the
+journal first).
+
+#### Active Journal
+This is used to keep track of all blobs that are in an  **unlinked**,
+**active**, and **persisted** state. Blobs are added to this journal during the
+second phase of an IndexedDB transaction's commit, and contains blobs that are
+referenced by clients that would have otherwise been deleted (well, added to
+the recovery journal) by the transaction. When the a client stops using the
+given blob, then the key is added to the recovery journal and removed from the
+active journal.
\ No newline at end of file
diff --git a/content/browser/resources/media/stats_rates_calculator.js b/content/browser/resources/media/stats_rates_calculator.js
index 67edddf..b3f5f4a 100644
--- a/content/browser/resources/media/stats_rates_calculator.js
+++ b/content/browser/resources/media/stats_rates_calculator.js
@@ -318,6 +318,71 @@
   }
 }
 
+// Calculates the standard deviation from a totalSquaredSum, totalSum, and
+// totalCount. If the standard deviation cannot be calculated, such as the
+// metric is missing in the current or previous report, undefined is returned.
+class StandardDeviationCalculator {
+  constructor(totalSquaredSumMetric, totalSumMetric, totalCount, label) {
+    this.totalSquaredSumMetric = totalSquaredSumMetric;
+    this.totalSumMetric = totalSumMetric;
+    this.totalCount = totalCount;
+    this.label = label;
+  }
+
+  getCalculatedMetricName() {
+    return '[' + this.label + 'StDev_in_ms]';
+  }
+
+  calculate(id, previousReport, currentReport) {
+    return StandardDeviationCalculator.calculateStandardDeviation(
+        id, previousReport, currentReport, this.totalSquaredSumMetric,
+        this.totalSumMetric, this.totalCount);
+  }
+
+  static calculateStandardDeviation(
+      id, previousReport, currentReport, totalSquaredSumMetric, totalSumMetric,
+      totalCount) {
+    if (!previousReport || !currentReport) {
+      return undefined;
+    }
+    const previousStats = previousReport.get(id);
+    const currentStats = currentReport.get(id);
+    if (!previousStats || !currentStats) {
+      return undefined;
+    }
+    const deltaCount =
+        Number(currentStats[totalCount]) - Number(previousStats[totalCount]);
+    if (deltaCount <= 0) {
+      return undefined;
+    }
+    // Try to convert whatever the values are to numbers. This gets around the
+    // fact that some types that are not supported by base::Value (e.g. uint32,
+    // int64, uint64 and double) are passed as strings.
+    const previousSquaredSumValue =
+        Number(previousStats[totalSquaredSumMetric]);
+    const currentSquaredSumValue = Number(currentStats[totalSquaredSumMetric]);
+    if (typeof previousSquaredSumValue != 'number' ||
+        typeof currentSquaredSumValue != 'number') {
+      return undefined;
+    }
+    const previousSumValue = Number(previousStats[totalSumMetric]);
+    const currentSumValue = Number(currentStats[totalSumMetric]);
+    if (typeof previousSumValue != 'number' ||
+        typeof currentSumValue != 'number') {
+      return undefined;
+    }
+
+    const deltaSquaredSum = currentSquaredSumValue - previousSquaredSumValue;
+    const deltaSum = currentSumValue - previousSumValue;
+    const variance =
+        (deltaSquaredSum - Math.pow(deltaSum, 2) / deltaCount) / deltaCount;
+    if (variance < 0) {
+      return undefined;
+    }
+    return 1000 * Math.sqrt(variance);
+  }
+}
+
 // Keeps track of previous and current stats report and calculates all
 // calculated metrics.
 class StatsRatesCalculator {
@@ -393,6 +458,12 @@
           totalDecodeTime: new RateCalculator(
               'totalDecodeTime', 'framesDecoded',
               CalculatorModifier.kMillisecondsFromSeconds),
+          totalInterFrameDelay: new RateCalculator(
+              'totalInterFrameDelay', 'framesDecoded',
+              CalculatorModifier.kMillisecondsFromSeconds),
+          totalSquaredInterFrameDelay: new StandardDeviationCalculator(
+              'totalSquaredInterFrameDelay', 'totalInterFrameDelay',
+              'framesDecoded', 'interFrameDelay'),
           qpSum: new RateCalculator('qpSum', 'framesDecoded'),
           codecId: new CodecCalculator(),
         },
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index ad0754e..4caa5362 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -427,29 +427,29 @@
 
 crbug.com/angleproject/3701 [ mac amd ] conformance2/textures/canvas/tex-2d-rg8-rg-unsigned_byte.html [ RetryOnFailure ]
 
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_interleaved_lines.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_interleaved_points.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_interleaved_triangles.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_separate_lines.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_separate_points.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_separate_triangles.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_lines.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_points.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_triangles.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_separate_lines.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_separate_points.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_separate_triangles.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/interpolation_centroid.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/interpolation_flat.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/interpolation_smooth.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/point_size.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/position.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_interleaved_lines.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_interleaved_points.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_interleaved_triangles.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_separate_lines.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_separate_points.html [ Failure ]
-crbug.com/483282 [ sierra amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_separate_triangles.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_interleaved_lines.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_interleaved_points.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_interleaved_triangles.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_separate_lines.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_separate_points.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/array_separate_triangles.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_lines.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_points.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_interleaved_triangles.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_separate_lines.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_separate_points.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/basic_types_separate_triangles.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/interpolation_centroid.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/interpolation_flat.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/interpolation_smooth.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/point_size.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/position.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_interleaved_lines.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_interleaved_points.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_interleaved_triangles.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_separate_lines.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_separate_points.html [ Failure ]
+crbug.com/1027605 [ mac amd no-passthrough ] deqp/functional/gles3/transformfeedback/random_separate_triangles.html [ Failure ]
 crbug.com/751254 [ mac amd ] deqp/functional/gles3/shaderindexing/mat_00.html [ RetryOnFailure ]
 crbug.com/636648 [ mac amd ] deqp/functional/gles3/shaderindexing/mat_01.html [ RetryOnFailure ]
 crbug.com/644360 [ mac amd ] deqp/functional/gles3/shaderindexing/mat_02.html [ RetryOnFailure ]
diff --git a/docs/testing/android_test_instructions.md b/docs/testing/android_test_instructions.md
index 2748f04..b728075 100644
--- a/docs/testing/android_test_instructions.md
+++ b/docs/testing/android_test_instructions.md
@@ -61,7 +61,7 @@
 
 ### Using Emulators
 
-Running tests on emulators is the same as on device. Refer to
+Running tests on emulators is the same as [on device](#Running-Tests). Refer to
 [android_emulator.md](../android_emulator.md) for setting up emulators.
 
 ## Building Tests
diff --git a/extensions/browser/api/messaging/extension_message_port.cc b/extensions/browser/api/messaging/extension_message_port.cc
index a74181f..006887d 100644
--- a/extensions/browser/api/messaging/extension_message_port.cc
+++ b/extensions/browser/api/messaging/extension_message_port.cc
@@ -279,13 +279,14 @@
     int guest_render_frame_routing_id,
     const MessagingEndpoint& source_endpoint,
     const std::string& target_extension_id,
-    const GURL& source_url) {
+    const GURL& source_url,
+    base::Optional<url::Origin> source_origin) {
   SendToPort(base::BindRepeating(
       &ExtensionMessagePort::BuildDispatchOnConnectIPC,
       // Called synchronously.
       base::Unretained(this), channel_name, source_tab.get(), source_frame_id,
       guest_process_id, guest_render_frame_routing_id, source_endpoint,
-      target_extension_id, source_url));
+      target_extension_id, source_url, source_origin));
 }
 
 void ExtensionMessagePort::DispatchOnDisconnect(
@@ -492,6 +493,7 @@
     const MessagingEndpoint& source_endpoint,
     const std::string& target_extension_id,
     const GURL& source_url,
+    base::Optional<url::Origin> source_origin,
     const IPCTarget& target) {
   ExtensionMsg_TabConnectionInfo source;
   if (source_tab) {
@@ -508,6 +510,7 @@
   info.target_id = target_extension_id;
   info.source_endpoint = source_endpoint;
   info.source_url = source_url;
+  info.source_origin = std::move(source_origin);
   info.guest_process_id = guest_process_id;
   info.guest_render_frame_routing_id = guest_render_frame_routing_id;
 
diff --git a/extensions/browser/api/messaging/extension_message_port.h b/extensions/browser/api/messaging/extension_message_port.h
index a747f5cf..7e1a4fd 100644
--- a/extensions/browser/api/messaging/extension_message_port.h
+++ b/extensions/browser/api/messaging/extension_message_port.h
@@ -6,15 +6,18 @@
 #define EXTENSIONS_BROWSER_API_MESSAGING_EXTENSION_MESSAGE_PORT_H_
 
 #include <map>
+#include <memory>
 #include <set>
 #include <string>
 #include <vector>
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "extensions/browser/api/messaging/message_port.h"
 #include "extensions/browser/service_worker/worker_id.h"
 #include "extensions/common/api/messaging/port_id.h"
+#include "url/origin.h"
 
 class GURL;
 
@@ -74,7 +77,8 @@
                          int guest_render_frame_routing_id,
                          const MessagingEndpoint& source_endpoint,
                          const std::string& target_extension_id,
-                         const GURL& source_url) override;
+                         const GURL& source_url,
+                         base::Optional<url::Origin> source_origin) override;
   void DispatchOnDisconnect(const std::string& error_message) override;
   void DispatchOnMessage(const Message& message) override;
   void IncrementLazyKeepaliveCount() override;
@@ -128,6 +132,7 @@
       const MessagingEndpoint& source_endpoint,
       const std::string& target_extension_id,
       const GURL& source_url,
+      base::Optional<url::Origin> source_origin,
       const IPCTarget& target);
   std::unique_ptr<IPC::Message> BuildDispatchOnDisconnectIPC(
       const std::string& error_message,
diff --git a/extensions/browser/api/messaging/message_port.cc b/extensions/browser/api/messaging/message_port.cc
index 6bacba0..0a81157 100644
--- a/extensions/browser/api/messaging/message_port.cc
+++ b/extensions/browser/api/messaging/message_port.cc
@@ -25,7 +25,8 @@
     int guest_render_frame_routing_id,
     const MessagingEndpoint& source_endpoint,
     const std::string& target_extension_id,
-    const GURL& source_url) {}
+    const GURL& source_url,
+    base::Optional<url::Origin> source_origin) {}
 
 void MessagePort::DispatchOnDisconnect(const std::string& error_message) {}
 
diff --git a/extensions/browser/api/messaging/message_port.h b/extensions/browser/api/messaging/message_port.h
index 63492674b..3ed3a28 100644
--- a/extensions/browser/api/messaging/message_port.h
+++ b/extensions/browser/api/messaging/message_port.h
@@ -9,7 +9,9 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/values.h"
+#include "url/origin.h"
 
 class GURL;
 
@@ -66,7 +68,8 @@
       int guest_render_frame_routing_id,
       const MessagingEndpoint& source_endpoint,
       const std::string& target_extension_id,
-      const GURL& source_url);
+      const GURL& source_url,
+      base::Optional<url::Origin> source_origin);
 
   // Notifies the port that the channel has been closed. If |error_message| is
   // non-empty, it indicates an error occurred while opening the connection.
diff --git a/extensions/browser/api/messaging/message_service.cc b/extensions/browser/api/messaging/message_service.cc
index 494366ca..b223bb1 100644
--- a/extensions/browser/api/messaging/message_service.cc
+++ b/extensions/browser/api/messaging/message_service.cc
@@ -123,6 +123,7 @@
   std::unique_ptr<MessagePort> opener_port;
   std::string target_extension_id;
   GURL source_url;
+  base::Optional<url::Origin> source_origin;
   std::string channel_name;
   bool include_guest_process_info;
 
@@ -136,6 +137,7 @@
                     std::unique_ptr<MessagePort> opener_port,
                     const std::string& target_extension_id,
                     const GURL& source_url,
+                    base::Optional<url::Origin> source_origin,
                     const std::string& channel_name,
                     bool include_guest_process_info)
       : source(source),
@@ -147,6 +149,7 @@
         opener_port(std::move(opener_port)),
         target_extension_id(target_extension_id),
         source_url(source_url),
+        source_origin(source_origin),
         channel_name(channel_name),
         include_guest_process_info(include_guest_process_info) {}
 
@@ -294,6 +297,10 @@
   std::unique_ptr<base::DictionaryValue> source_tab =
       messaging_delegate_->MaybeGetTabInfo(source_contents);
 
+  base::Optional<url::Origin> source_origin;
+  if (source_render_frame_host)
+    source_origin = source_render_frame_host->GetLastCommittedOrigin();
+
   if (source_tab.get()) {
     DCHECK(source_render_frame_host);
     source_frame_id =
@@ -312,8 +319,8 @@
   std::unique_ptr<OpenChannelParams> params(new OpenChannelParams(
       source, std::move(source_tab), source_frame_id, nullptr,
       source_port_id.GetOppositePortId(), source_endpoint,
-      std::move(opener_port), target_extension_id, source_url, channel_name,
-      include_guest_process_info));
+      std::move(opener_port), target_extension_id, source_url,
+      std::move(source_origin), channel_name, include_guest_process_info));
 
   pending_incognito_channels_[params->receiver_port_id.GetChannelId()] =
       PendingMessagesQueue();
@@ -503,7 +510,8 @@
       receiver.release(), receiver_port_id,
       MessagingEndpoint::ForExtension(extension_id), std::move(opener_port),
       extension_id,
-      GURL(),  // Source URL doesn't make sense for opening to tabs.
+      GURL(),         // Source URL doesn't make sense for opening to tabs.
+      url::Origin(),  // Origin URL doesn't make sense for opening to tabs.
       channel_name,
       false));  // Connections to tabs aren't webview guests.
   OpenChannelImpl(receiver_context, std::move(params), extension,
@@ -566,7 +574,8 @@
   channel->receiver->DispatchOnConnect(
       params->channel_name, std::move(params->source_tab),
       params->source_frame_id, guest_process_id, guest_render_frame_routing_id,
-      params->source_endpoint, params->target_extension_id, params->source_url);
+      params->source_endpoint, params->target_extension_id, params->source_url,
+      params->source_origin);
 
   // Report the event to the event router, if the target is an extension.
   //
diff --git a/extensions/browser/updater/extension_downloader.cc b/extensions/browser/updater/extension_downloader.cc
index c0b227e..bbf88a8 100644
--- a/extensions/browser/updater/extension_downloader.cc
+++ b/extensions/browser/updater/extension_downloader.cc
@@ -75,7 +75,7 @@
     0.1,
 
     // Maximum amount of time we are willing to delay our request in ms.
-    -1,
+    600000,  // Ten minutes.
 
     // Time to keep an entry from being discarded even when it
     // has no significant state, -1 to never discard.
diff --git a/extensions/common/api/runtime.json b/extensions/common/api/runtime.json
index f2fa63c9..052ddd08 100644
--- a/extensions/common/api/runtime.json
+++ b/extensions/common/api/runtime.json
@@ -60,8 +60,9 @@
           "id": {"type": "string", "optional": true, "description": "The ID of the extension or app that opened the connection, if any."},
           "url": {"type": "string", "optional": true, "description": "The URL of the page or frame that opened the connection. If the sender is in an iframe, it will be iframe's URL not the URL of the page which hosts it."},
           "nativeApplication": {"type": "string", "optional": true, "description": "The name of the native application that opened the connection, if any."},
-          "tlsChannelId": {"type": "string", "optional": true, "description": "The TLS channel ID of the page or frame that opened the connection, if requested by the extension or app, and if available."}
-        }
+          "tlsChannelId": {"type": "string", "optional": true, "description": "The TLS channel ID of the page or frame that opened the connection, if requested by the extension or app, and if available."},
+	  "origin": {"type": "string", "optional": true, "description": "The origin of the page or frame that opened the connection. It can vary from the url property (e.g., about:blank) or can be opaque (e.g., sandboxed iframes). This is useful for identifying if the origin can be trust if we can't immediately tell from the URL."}
+	}
       },
       {
         "id": "PlatformOs",
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index 4230a0cb..632f984 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -9,6 +9,9 @@
 
 #include <stdint.h>
 
+#include <map>
+#include <memory>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -38,6 +41,7 @@
 #include "extensions/common/view_type.h"
 #include "ipc/ipc_message_macros.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 #define IPC_MESSAGE_START ExtensionMsgStart
 
@@ -231,6 +235,9 @@
   // The URL of the frame that initiated the request.
   IPC_STRUCT_MEMBER(GURL, source_url)
 
+  // The origin of the object that initiated the request.
+  IPC_STRUCT_MEMBER(base::Optional<url::Origin>, source_origin)
+
   // The process ID of the webview that initiated the request.
   IPC_STRUCT_MEMBER(int, guest_process_id)
 
diff --git a/extensions/renderer/native_renderer_messaging_service.cc b/extensions/renderer/native_renderer_messaging_service.cc
index 3906d5e..87af2b2 100644
--- a/extensions/renderer/native_renderer_messaging_service.cc
+++ b/extensions/renderer/native_renderer_messaging_service.cc
@@ -5,7 +5,10 @@
 #include "extensions/renderer/native_renderer_messaging_service.h"
 
 #include <map>
+#include <memory>
 #include <string>
+#include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -377,6 +380,8 @@
   }
   if (!info.source_url.is_empty())
     sender_builder.Set("url", info.source_url.spec());
+  if (info.source_origin)
+    sender_builder.Set("origin", info.source_origin->Serialize());
   if (source->frame_id >= 0)
     sender_builder.Set("frameId", source->frame_id);
 
diff --git a/fuchsia/base/test_navigation_listener.cc b/fuchsia/base/test_navigation_listener.cc
index f602a3c..9f223e4 100644
--- a/fuchsia/base/test_navigation_listener.cc
+++ b/fuchsia/base/test_navigation_listener.cc
@@ -57,6 +57,13 @@
   RunUntilNavigationStateMatches(state);
 }
 
+void TestNavigationListener::RunUntilTitleEquals(
+    const base::StringPiece expected_title) {
+  fuchsia::web::NavigationState state;
+  state.set_title(expected_title.as_string());
+  RunUntilNavigationStateMatches(state);
+}
+
 void TestNavigationListener::RunUntilUrlAndTitleEquals(
     const GURL& expected_url,
     const base::StringPiece expected_title) {
diff --git a/fuchsia/base/test_navigation_listener.h b/fuchsia/base/test_navigation_listener.h
index 74d6f16cbe..99ae75a 100644
--- a/fuchsia/base/test_navigation_listener.h
+++ b/fuchsia/base/test_navigation_listener.h
@@ -30,16 +30,20 @@
   void RunUntilNavigationStateMatches(
       const fuchsia::web::NavigationState& expected_state);
 
-  // Calls RunUntilNavigationStateMatches with a NagivationState that has
+  // Calls RunUntilNavigationStateMatches with a NavigationState that has
   // |expected_url|.
   void RunUntilUrlEquals(const GURL& expected_url);
 
-  // Calls RunUntilNavigationStateMatches with a NagivationState that has
+  // Calls RunUntilNavigationStateMatches with a NavigationState that has
+  // |expected_title|.
+  void RunUntilTitleEquals(const base::StringPiece expected_title);
+
+  // Calls RunUntilNavigationStateMatches with a NavigationState that has
   // |expected_url| and |expected_title|.
   void RunUntilUrlAndTitleEquals(const GURL& expected_url,
                                  base::StringPiece expected_title);
 
-  // Calls RunUntilNavigationStateMatches with a NagivationState that has
+  // Calls RunUntilNavigationStateMatches with a NavigationState that has
   // all the expected fields.
   void RunUntilUrlTitleBackForwardEquals(const GURL& expected_url,
                                          base::StringPiece expected_title,
diff --git a/fuchsia/engine/test/data/bear-opus.webm b/fuchsia/engine/test/data/bear-opus.webm
new file mode 100644
index 0000000..c198148
--- /dev/null
+++ b/fuchsia/engine/test/data/bear-opus.webm
Binary files differ
diff --git a/fuchsia/engine/test/data/play_audio.html b/fuchsia/engine/test/data/play_audio.html
new file mode 100644
index 0000000..6aaa924
--- /dev/null
+++ b/fuchsia/engine/test/data/play_audio.html
@@ -0,0 +1,14 @@
+<html>
+  <body>
+    <video></video>
+    <script>
+      window.onload = () => {
+        var video = document.getElementsByTagName("video")[0]
+        video.onerror = function() { document.title = 'error'; }
+        video.onended = function() { document.title = 'ended'; }
+        video.src = './bear-opus.webm';
+        video.play();
+      }
+    </script>
+  </body>
+</html>
diff --git a/fuchsia/engine/web_engine_integration_test.cc b/fuchsia/engine/web_engine_integration_test.cc
index a7fdfac..3655f8e0 100644
--- a/fuchsia/engine/web_engine_integration_test.cc
+++ b/fuchsia/engine/web_engine_integration_test.cc
@@ -60,6 +60,22 @@
     return create_params;
   }
 
+  fuchsia::web::CreateContextParams DefaultContextParamsWithTestData() const {
+    fuchsia::web::CreateContextParams create_params = DefaultContextParams();
+
+    fuchsia::web::ContentDirectoryProvider provider;
+    provider.set_name("testdata");
+    base::FilePath pkg_path;
+    CHECK(base::PathService::Get(base::DIR_ASSETS, &pkg_path));
+    provider.set_directory(base::fuchsia::OpenDirectory(
+        pkg_path.AppendASCII("fuchsia/engine/test/data")));
+
+    create_params.mutable_content_directories()->emplace_back(
+        std::move(provider));
+
+    return create_params;
+  }
+
   void CreateContextAndFrame(fuchsia::web::CreateContextParams params) {
     web_context_provider_->Create(std::move(params), context_.NewRequest());
     context_.set_error_handler([](zx_status_t status) { ADD_FAILURE(); });
@@ -322,31 +338,45 @@
   const GURL kUrl("fuchsia-dir://testdata/title1.html");
   constexpr char kTitle[] = "title 1";
 
-  fuchsia::web::CreateContextParams create_params = DefaultContextParams();
-
-  fuchsia::web::ContentDirectoryProvider provider;
-  provider.set_name("testdata");
-  base::FilePath pkg_path;
-  CHECK(base::PathService::Get(base::DIR_ASSETS, &pkg_path));
-  provider.set_directory(base::fuchsia::OpenDirectory(
-      pkg_path.AppendASCII("fuchsia/engine/test/data")));
-
-  create_params.mutable_content_directories()->emplace_back(
-      std::move(provider));
+  fuchsia::web::CreateContextParams create_params =
+      DefaultContextParamsWithTestData();
 
   CreateContextAndFrame(std::move(create_params));
 
-  fuchsia::web::NavigationControllerPtr controller;
-  frame_->GetNavigationController(controller.NewRequest());
-  controller.set_error_handler([](zx_status_t status) { ADD_FAILURE(); });
-
   // Navigate to test1.html and verify that the resource was correctly
   // downloaded and interpreted by inspecting the document title.
   EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
-      controller.get(), fuchsia::web::LoadUrlParams(), kUrl.spec()));
+      navigation_controller_.get(), fuchsia::web::LoadUrlParams(),
+      kUrl.spec()));
   cr_fuchsia::TestNavigationListener navigation_listener;
   fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding(
       &navigation_listener);
   frame_->SetNavigationEventListener(listener_binding.NewBinding());
   navigation_listener.RunUntilUrlAndTitleEquals(kUrl, kTitle);
 }
+
+TEST_F(WebEngineIntegrationTest, PlayAudio) {
+  fuchsia::web::CreateContextParams create_params =
+      DefaultContextParamsWithTestData();
+  auto features = fuchsia::web::ContextFeatureFlags::AUDIO;
+  if (create_params.has_features())
+    features |= create_params.features();
+  create_params.set_features(features);
+  CreateContextAndFrame(std::move(create_params));
+
+  fuchsia::web::LoadUrlParams load_url_params;
+
+  // |was_user_activated| needs to be set to ensure the page can play audio
+  // without user gesture.
+  load_url_params.set_was_user_activated(true);
+
+  EXPECT_TRUE(cr_fuchsia::LoadUrlAndExpectResponse(
+      navigation_controller_.get(), std::move(load_url_params),
+      "fuchsia-dir://testdata/play_audio.html"));
+
+  cr_fuchsia::TestNavigationListener navigation_listener;
+  fidl::Binding<fuchsia::web::NavigationEventListener> listener_binding(
+      &navigation_listener);
+  frame_->SetNavigationEventListener(listener_binding.NewBinding());
+  navigation_listener.RunUntilTitleEquals("ended");
+}
diff --git a/fuchsia/engine/web_engine_integration_tests.cmx b/fuchsia/engine/web_engine_integration_tests.cmx
index 73be6e0..e9a7075 100644
--- a/fuchsia/engine/web_engine_integration_tests.cmx
+++ b/fuchsia/engine/web_engine_integration_tests.cmx
@@ -10,6 +10,7 @@
       "fuchsia.device.NameProvider",
       "fuchsia.fonts.Provider",
       "fuchsia.logger.LogSink",
+      "fuchsia.media.Audio",
       "fuchsia.net.NameLookup",
       "fuchsia.netstack.Netstack",
       "fuchsia.posix.socket.Provider",
diff --git a/gpu/command_buffer/service/external_vk_image_dawn_representation.cc b/gpu/command_buffer/service/external_vk_image_dawn_representation.cc
index a150bcc..f7f0427 100644
--- a/gpu/command_buffer/service/external_vk_image_dawn_representation.cc
+++ b/gpu/command_buffer/service/external_vk_image_dawn_representation.cc
@@ -81,7 +81,7 @@
     descriptor.waitFDs.push_back(handle.TakeHandle().release());
   }
 
-  texture_ = dawn_native::vulkan::WrapVulkanImageOpaqueFD(device_, &descriptor);
+  texture_ = dawn_native::vulkan::WrapVulkanImage(device_, &descriptor);
 
   if (texture_) {
     // Keep a reference to the texture so that it stays valid (its content
diff --git a/infra/config/buckets/ci.star b/infra/config/buckets/ci.star
index cdfac51..018bfe4 100644
--- a/infra/config/buckets/ci.star
+++ b/infra/config/buckets/ci.star
@@ -447,6 +447,7 @@
 
 chromiumos_builder(
     name = 'Linux ChromiumOS Full',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 chromiumos_builder(
@@ -889,6 +890,7 @@
 
 fuzz_builder(
     name = 'ChromiumOS ASAN Release',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 fuzz_builder(
@@ -963,6 +965,7 @@
 
 fuzz_libfuzzer_builder(
     name = 'Libfuzzer Upload Chrome OS ASan',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 fuzz_libfuzzer_builder(
@@ -1070,6 +1073,7 @@
 
 fyi_builder(
     name = 'Mojo ChromiumOS',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 fyi_builder(
@@ -2155,6 +2159,7 @@
 
 memory_builder(
     name = 'Linux Chromium OS ASan LSan Builder',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 memory_builder(
@@ -2163,6 +2168,7 @@
 
 memory_builder(
     name = 'Linux ChromiumOS MSan Builder',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 memory_builder(
diff --git a/infra/config/buckets/try.star b/infra/config/buckets/try.star
index 9a30595c..e2cbbcda 100644
--- a/infra/config/buckets/try.star
+++ b/infra/config/buckets/try.star
@@ -1245,11 +1245,13 @@
 
 linux_builder(
     name = 'linux_chromium_chromeos_asan_rel_ng',
+    goma_backend = goma.backend.RBE_PROD,
     goma_jobs = goma.jobs.J150,
 )
 
 linux_builder(
     name = 'linux_chromium_chromeos_msan_rel_ng',
+    goma_backend = goma.backend.RBE_PROD,
     goma_jobs = goma.jobs.J150,
 )
 
@@ -1369,6 +1371,7 @@
 
 linux_builder(
     name = 'linux_mojo_chromeos',
+    goma_backend = goma.backend.RBE_PROD,
 )
 
 linux_builder(
diff --git a/infra/config/consoles/chromium.goma.migration.star b/infra/config/consoles/chromium.goma.migration.star
index 822ee17..8cce3a8 100644
--- a/infra/config/consoles/chromium.goma.migration.star
+++ b/infra/config/consoles/chromium.goma.migration.star
@@ -830,5 +830,35 @@
             category = 'cros|week1',
             short_name = 'code',
         ),
+        luci.console_view_entry(
+            builder = 'ci/Linux ChromiumOS Full',
+            category = 'cros|week2',
+            short_name = 'full',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/ChromiumOS ASAN Release',
+            category = 'cros|week2',
+            short_name = 'asan',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/Linux Chromium OS ASan LSan Builder',
+            category = 'cros|week2',
+            short_name = 'asan lsan',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/Linux ChromiumOS MSan Builder',
+            category = 'cros|week2',
+            short_name = 'msan',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/Mojo ChromiumOS',
+            category = 'cros|week2',
+            short_name = 'mojo',
+        ),
+        luci.console_view_entry(
+            builder = 'ci/Libfuzzer Upload Chrome OS ASan',
+            category = 'cros|week2',
+            short_name = 'fuzz',
+        ),
     ],
 )
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 06fda20..e4af3c3 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -893,6 +893,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.fuzz\""
       >
@@ -2132,6 +2133,7 @@
         name: "chromium_libfuzzer"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.fuzz\""
       >
@@ -2548,7 +2550,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
-        properties_j: "$build/goma:{\"jobs\":500}"
+        properties_j: "$build/goma:{\"jobs\":500,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.memory\""
       >
@@ -2590,6 +2592,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.chromiumos\""
       >
@@ -2610,7 +2613,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
-        properties_j: "$build/goma:{\"jobs\":500}"
+        properties_j: "$build/goma:{\"jobs\":500,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.memory\""
       >
@@ -3939,6 +3942,7 @@
         name: "chromium"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"chromium.fyi\""
       >
@@ -15133,7 +15137,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
-        properties_j: "$build/goma:{\"jobs\":150}"
+        properties_j: "$build/goma:{\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.linux\""
       >
@@ -15162,7 +15166,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
-        properties_j: "$build/goma:{\"jobs\":150}"
+        properties_j: "$build/goma:{\"jobs\":150,\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.linux\""
       >
@@ -15547,6 +15551,7 @@
         name: "chromium_trybot"
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/master"
+        properties_j: "$build/goma:{\"rpc_extra_params\":\"?prod\",\"server_host\":\"goma.chromium.org\"}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
         properties_j: "mastername:\"tryserver.chromium.linux\""
       >
diff --git a/infra/config/generated/luci-milo.cfg b/infra/config/generated/luci-milo.cfg
index edba251..ed592aa 100644
--- a/infra/config/generated/luci-milo.cfg
+++ b/infra/config/generated/luci-milo.cfg
@@ -5269,6 +5269,36 @@
     category: "cros|week1"
     short_name: "code"
   >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/Linux ChromiumOS Full"
+    category: "cros|week2"
+    short_name: "full"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/ChromiumOS ASAN Release"
+    category: "cros|week2"
+    short_name: "asan"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/Linux Chromium OS ASan LSan Builder"
+    category: "cros|week2"
+    short_name: "asan lsan"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/Linux ChromiumOS MSan Builder"
+    category: "cros|week2"
+    short_name: "msan"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/Mojo ChromiumOS"
+    category: "cros|week2"
+    short_name: "mojo"
+  >
+  builders: <
+    name: "buildbucket/luci.chromium.ci/Libfuzzer Upload Chrome OS ASan"
+    category: "cros|week2"
+    short_name: "fuzz"
+  >
   header: <
     oncalls: <
       name: "Chromium"
diff --git a/ios/chrome/browser/flags/about_flags.mm b/ios/chrome/browser/flags/about_flags.mm
index 7ac7fde..4ff154b 100644
--- a/ios/chrome/browser/flags/about_flags.mm
+++ b/ios/chrome/browser/flags/about_flags.mm
@@ -467,6 +467,13 @@
      flag_descriptions::kOmniboxUseDefaultSearchEngineFaviconDescription,
      flags_ui::kOsIos,
      FEATURE_VALUE_TYPE(kOmniboxUseDefaultSearchEngineFavicon)},
+    {"omnibox-preserve-default-match-against-async-update",
+     flag_descriptions::kOmniboxPreserveDefaultMatchAgainstAsyncUpdateName,
+     flag_descriptions::
+         kOmniboxPreserveDefaultMatchAgainstAsyncUpdateDescription,
+     flags_ui::kOsIos,
+     FEATURE_VALUE_TYPE(
+         omnibox::kOmniboxPreserveDefaultMatchAgainstAsyncUpdate)},
     {"enable-send-tab-to-self-broadcast",
      flag_descriptions::kSendTabToSelfBroadcastName,
      flag_descriptions::kSendTabToSelfBroadcastDescription, flags_ui::kOsIos,
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
index 52e83ec1..7505f5e 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.cc
@@ -276,6 +276,15 @@
     "Instead of ZeroSuggest, show most visited sites and collection shortcuts "
     "in the omnibox popup.";
 
+const char kOmniboxPreserveDefaultMatchAgainstAsyncUpdateName[] =
+    "Omnibox Preserve Default Match Against Async Update";
+const char kOmniboxPreserveDefaultMatchAgainstAsyncUpdateDescription[] =
+    "Preserves the default match against change when providers return results "
+    "asynchronously. This prevents the default match from changing after the "
+    "user finishes typing. Without this feature, if the default match is "
+    "updated right when the user presses Enter, the user may go to a "
+    "surprising destination.";
+
 const char kOmniboxUIMaxAutocompleteMatchesName[] =
     "Omnibox UI Max Autocomplete Matches";
 const char kOmniboxUIMaxAutocompleteMatchesDescription[] =
diff --git a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
index e0bc8c1..9915039 100644
--- a/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
+++ b/ios/chrome/browser/flags/ios_chrome_flag_descriptions.h
@@ -238,6 +238,11 @@
 extern const char kOmniboxPopupShortcutIconsInZeroStateName[];
 extern const char kOmniboxPopupShortcutIconsInZeroStateDescription[];
 
+// Title and description for the flag to preserve the default match when an
+// async match updates.
+extern const char kOmniboxPreserveDefaultMatchAgainstAsyncUpdateName[];
+extern const char kOmniboxPreserveDefaultMatchAgainstAsyncUpdateDescription[];
+
 // Title and description for the flag to change the max number of autocomplete
 // matches in the omnibox popup.
 extern const char kOmniboxUIMaxAutocompleteMatchesName[];
diff --git a/ios/chrome/browser/metrics/ukm_egtest.mm b/ios/chrome/browser/metrics/ukm_egtest.mm
index b97c5e2..607c533a 100644
--- a/ios/chrome/browser/metrics/ukm_egtest.mm
+++ b/ios/chrome/browser/metrics/ukm_egtest.mm
@@ -158,7 +158,7 @@
   // Note: URL-keyed anonymized data collection is turned on as part of the
   // flow to Sign in to Chrome and Turn sync on. This matches the main user
   // flow that enables UKM.
-  [SigninEarlGreyUI signinWithIdentity:[SigninEarlGreyUtils fakeIdentity1]];
+  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGreyUtils fakeIdentity1]];
   [ChromeEarlGrey waitForSyncInitialized:YES
                              syncTimeout:kSyncUKMOperationsTimeout];
 
@@ -285,7 +285,7 @@
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:NO],
              @"Failed to assert that UKM was not enabled.");
 
-  [SigninEarlGreyUI signinWithIdentity:[SigninEarlGreyUtils fakeIdentity1]];
+  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGreyUtils fakeIdentity1]];
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:YES],
              @"Failed to assert that UKM was enabled.");
 }
@@ -341,7 +341,7 @@
                      @"Client ID was not reset.");
 
   originalClientID = [MetricsAppInterface UKMClientID];
-  [SigninEarlGreyUI signinWithIdentity:[SigninEarlGreyUtils fakeIdentity1]];
+  [SigninEarlGreyUI signinWithFakeIdentity:[SigninEarlGreyUtils fakeIdentity1]];
 
   GREYAssert([MetricsAppInterface checkUKMRecordingEnabled:YES],
              @"Failed to assert that UKM was enabled.");
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h
index 658b14c..cb77c13 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.h
@@ -14,14 +14,15 @@
 // Methods used for the EarlGrey tests, related to UI.
 @interface SigninEarlGreyUI : NSObject
 
-// Calls [SigninEarlGreyUI signinWithIdentity:identity isManagedAccount:NO].
-+ (void)signinWithIdentity:(FakeChromeIdentity*)identity;
+// Calls [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity
+// isManagedAccount:NO].
++ (void)signinWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity;
 
 // Adds the identity (if not already added), and perform a sign-in. if
-// |isManagedAccount| is true, |identity| needs to be a managed account and the
-// managed dialog is expected while signing in.
-+ (void)signinWithIdentity:(FakeChromeIdentity*)identity
-          isManagedAccount:(BOOL)isManagedAccount;
+// |isManagedAccount| is true, |fakeIdentity| needs to be a managed account and
+// the managed dialog is expected while signing in.
++ (void)signinWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity
+              isManagedAccount:(BOOL)isManagedAccount;
 
 // Taps on the settings link in the sign-in view. The sign-in view has to be
 // opened before calling this method.
diff --git a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
index 204c1fb2..daed362 100644
--- a/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earl_grey_ui.mm
@@ -33,24 +33,24 @@
 
 @implementation SigninEarlGreyUI
 
-+ (void)signinWithIdentity:(FakeChromeIdentity*)identity {
-  [self signinWithIdentity:identity isManagedAccount:NO];
++ (void)signinWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
+  [self signinWithFakeIdentity:fakeIdentity isManagedAccount:NO];
 }
 
-+ (void)signinWithIdentity:(FakeChromeIdentity*)identity
-          isManagedAccount:(BOOL)isManagedAccount {
-  [SigninEarlGreyUtils addIdentity:identity];
++ (void)signinWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity
+              isManagedAccount:(BOOL)isManagedAccount {
+  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI
       tapSettingsMenuButton:chrome_test_util::SecondarySignInButton()];
-  [self selectIdentityWithEmail:identity.userEmail];
+  [self selectIdentityWithEmail:fakeIdentity.userEmail];
   [self confirmSigninConfirmationDialog];
   if (isManagedAccount) {
     [self confirmSigninWithManagedAccount];
   }
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity];
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
 }
 
 + (void)selectIdentityWithEmail:(NSString*)userEmail {
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h
index f15c13c..9b12cf1 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h
+++ b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.h
@@ -28,11 +28,12 @@
 // Returns a fake managed identity.
 - (FakeChromeIdentity*)fakeManagedIdentity;
 
-// Adds |identity| to the fake identity service.
-- (void)addIdentity:(FakeChromeIdentity*)identity;
+// Adds |fakeIdentity| to the fake identity service.
+- (void)addFakeIdentity:(FakeChromeIdentity*)fakeIdentity;
 
-// Induces a GREYAssert if |identity| is not signed in to the active profile.
-- (void)checkSignedInWithIdentity:(FakeChromeIdentity*)identity;
+// Induces a GREYAssert if |fakeIdentity| is not signed in to the active
+// profile.
+- (void)checkSignedInWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity;
 
 // Induces a GREYAssert if an identity is signed in.
 - (void)checkSignedOut;
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm
index eb9b8e9..ae46dca 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils.mm
@@ -33,13 +33,13 @@
                                           name:@"Fake Managed"];
 }
 
-- (void)addIdentity:(FakeChromeIdentity*)identity {
-  [SigninEarlGreyUtilsAppInterface addIdentity:identity];
+- (void)addFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
+  [SigninEarlGreyUtilsAppInterface addFakeIdentity:fakeIdentity];
 }
 
-- (void)checkSignedInWithIdentity:(FakeChromeIdentity*)identity {
-  BOOL identityIsNonNil = identity != nil;
-  EG_TEST_HELPER_ASSERT_TRUE(identityIsNonNil, @"Need to give an identity");
+- (void)checkSignedInWithFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
+  BOOL fakeIdentityIsNonNil = fakeIdentity != nil;
+  EG_TEST_HELPER_ASSERT_TRUE(fakeIdentityIsNonNil, @"Need to give an identity");
 
   // Required to avoid any problem since the following test is not dependant
   // to UI, and the previous action has to be totally finished before going
@@ -60,9 +60,9 @@
   NSString* errorStr = [NSString
       stringWithFormat:@"Unexpected Gaia ID of the signed in user [expected = "
                        @"\"%@\", actual = \"%@\"]",
-                       identity.gaiaID, primaryAccountGaiaID];
+                       fakeIdentity.gaiaID, primaryAccountGaiaID];
   EG_TEST_HELPER_ASSERT_TRUE(
-      [identity.gaiaID isEqualToString:primaryAccountGaiaID], errorStr);
+      [fakeIdentity.gaiaID isEqualToString:primaryAccountGaiaID], errorStr);
 }
 
 - (void)checkSignedOut {
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h
index 76f5eeb..62740d57 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h
+++ b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.h
@@ -15,8 +15,8 @@
 // compiled into the app binary and can be called from either app or test code.
 @interface SigninEarlGreyUtilsAppInterface : NSObject
 
-// Adds |identity| to the fake identity service.
-+ (void)addIdentity:(FakeChromeIdentity*)identity;
+// Adds |fakeIdentity| to the fake identity service.
++ (void)addFakeIdentity:(FakeChromeIdentity*)fakeIdentity;
 
 // Returns the gaia ID of the signed-in account.
 + (NSString*)primaryAccountGaiaID;
diff --git a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.mm b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.mm
index 29ae149..8998e02 100644
--- a/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.mm
+++ b/ios/chrome/browser/ui/authentication/signin_earlgrey_utils_app_interface.mm
@@ -21,9 +21,9 @@
 
 @implementation SigninEarlGreyUtilsAppInterface
 
-+ (void)addIdentity:(FakeChromeIdentity*)identity {
++ (void)addFakeIdentity:(FakeChromeIdentity*)fakeIdentity {
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 }
 
 + (NSString*)primaryAccountGaiaID {
diff --git a/ios/chrome/browser/ui/first_run/first_run_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
index ca56aed..b0ece23 100644
--- a/ios/chrome/browser/ui/first_run/first_run_egtest.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
@@ -138,8 +138,8 @@
 
 // Signs in to an account and then taps the Advanced link to go to settings.
 - (void)testSignInAndTapSettingsLink {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addIdentity:identity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
 
   // Launch First Run and accept tems of services.
   [FirstRunAppInterface showFirstRunUI];
@@ -157,7 +157,7 @@
   [[EarlGrey selectElementWithMatcher:SyncSettingsConfirmButton()]
       performAction:grey_tap()];
 
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity];
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
 
   GREYAssertTrue([FirstRunAppInterface isSyncFirstSetupComplete],
                  @"Sync should have finished its original setup");
diff --git a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
index 4ec3c8e0..2bc8386 100644
--- a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
@@ -47,9 +47,9 @@
 namespace {
 
 // Returns a matcher for a button that matches the userEmail in the given
-// |identity|.
-id<GREYMatcher> ButtonWithIdentity(FakeChromeIdentity* identity) {
-  return ButtonWithAccessibilityLabel(identity.userEmail);
+// |fakeIdentity|.
+id<GREYMatcher> ButtonWithFakeIdentity(FakeChromeIdentity* fakeIdentity) {
+  return ButtonWithAccessibilityLabel(fakeIdentity.userEmail);
 }
 }
 
@@ -62,17 +62,17 @@
 // Tests that the Sync and Account Settings screen are correctly popped if the
 // signed in account is removed.
 - (void)testSignInPopUpAccountOnSyncSettings {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
 
   // Sign In |identity|, then open the Sync Settings.
-  [SigninEarlGreyUI signinWithIdentity:identity];
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SettingsAccountButton()];
 
-  // Forget |identity|, screens should be popped back to the Main Settings.
+  // Forget |fakeIdentity|, screens should be popped back to the Main Settings.
   [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()
-      ->ForgetIdentity(identity, nil);
+      ->ForgetIdentity(fakeIdentity, nil);
 
   [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
@@ -85,20 +85,20 @@
 // Tests that the Account Settings screen is correctly popped if the signed in
 // account is removed while the "Disconnect Account" dialog is up.
 - (void)testSignInPopUpAccountOnDisconnectAccount {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
-  // Sign In |identity|, then open the Account Settings.
-  [SigninEarlGreyUI signinWithIdentity:identity];
+  // Sign In |fakeIdentity|, then open the Account Settings.
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SettingsAccountButton()];
   [ChromeEarlGreyUI tapAccountsMenuButton:SignOutAccountsButton()];
 
-  // Forget |identity|, screens should be popped back to the Main Settings.
+  // Forget |fakeIdentity|, screens should be popped back to the Main Settings.
   [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()
-      ->ForgetIdentity(identity, nil);
+      ->ForgetIdentity(fakeIdentity, nil);
 
   [[EarlGrey selectElementWithMatcher:PrimarySignInButton()]
       assertWithMatcher:grey_sufficientlyVisible()];
@@ -113,29 +113,29 @@
 - (void)testSignInReloadOnRemoveAccount {
   ios::FakeChromeIdentityService* identity_service =
       ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
-  FakeChromeIdentity* identity1 = [SigninEarlGreyUtils fakeIdentity1];
-  FakeChromeIdentity* identity2 = [SigninEarlGreyUtils fakeIdentity2];
-  identity_service->AddIdentity(identity2);
+  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGreyUtils fakeIdentity2];
+  identity_service->AddIdentity(fakeIdentity2);
 
-  // Sign In |identity|, then open the Account Settings.
-  [SigninEarlGreyUI signinWithIdentity:identity1];
+  // Sign In |fakeIdentity|, then open the Account Settings.
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity1];
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SettingsAccountButton()];
 
-  // Remove |identity2| from the device.
-  [[EarlGrey selectElementWithMatcher:ButtonWithIdentity(identity2)]
+  // Remove |fakeIdentity2| from the device.
+  [[EarlGrey selectElementWithMatcher:ButtonWithFakeIdentity(fakeIdentity2)]
       performAction:grey_tap()];
   [[EarlGrey
       selectElementWithMatcher:ButtonWithAccessibilityLabel(@"Remove account")]
       performAction:grey_tap()];
 
-  // Check that |identity2| isn't available anymore on the Account Settings.
+  // Check that |fakeIdentity2| isn't available anymore on the Account Settings.
   [[EarlGrey
-      selectElementWithMatcher:grey_allOf(
-                                   grey_accessibilityLabel(identity2.userEmail),
-                                   grey_sufficientlyVisible(), nil)]
+      selectElementWithMatcher:grey_allOf(grey_accessibilityLabel(
+                                              fakeIdentity2.userEmail),
+                                          grey_sufficientlyVisible(), nil)]
       assertWithMatcher:grey_nil()];
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity1];
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity1];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -144,15 +144,15 @@
 // Tests that the Account Settings screen is popped and the user signed out
 // when the account is removed.
 - (void)testSignOutOnRemoveAccount {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
 
-  // Sign In |identity|, then open the Account Settings.
-  [SigninEarlGreyUI signinWithIdentity:identity];
+  // Sign In |fakeIdentity|, then open the Account Settings.
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SettingsAccountButton()];
 
-  // Remove |identity| from the device.
-  [[EarlGrey selectElementWithMatcher:ButtonWithIdentity(identity)]
+  // Remove |fakeIdentity| from the device.
+  [[EarlGrey selectElementWithMatcher:ButtonWithFakeIdentity(fakeIdentity)]
       performAction:grey_tap()];
   [[EarlGrey
       selectElementWithMatcher:ButtonWithAccessibilityLabel(@"Remove account")]
@@ -174,10 +174,10 @@
 #if !TARGET_IPHONE_SIMULATOR
   EARL_GREY_TEST_DISABLED(@"Test disabled on device.");
 #endif
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
 
-  // Sign In |identity|, then open the Account Settings.
-  [SigninEarlGreyUI signinWithIdentity:identity];
+  // Sign In |fakeIdentity|, then open the Account Settings.
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SettingsAccountButton()];
 
@@ -186,11 +186,11 @@
   [[EarlGrey selectElementWithMatcher:chrome_test_util::CancelButton()]
       performAction:grey_tap()];
 
-  // Check that Account Settings screen is open and |identity| is signed in.
+  // Check that Account Settings screen is open and |fakeIdentity| is signed in.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                           SettingsAccountsCollectionView()]
       assertWithMatcher:grey_sufficientlyVisible()];
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity];
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
diff --git a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
index 03ba6ef..b0b7a16 100644
--- a/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services/google_services_settings_egtest.mm
@@ -96,8 +96,8 @@
   std::unique_ptr<Decider> _webStatePolicyDecider(
       new Decider(GetNormalTabModel().webStateList->GetActiveWebState()));
   // Adds default identity.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
-  [SigninEarlGreyUtils addIdentity:identity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
+  [SigninEarlGreyUtils addFakeIdentity:fakeIdentity];
   // Open "Google Services" settings.
   [self openGoogleServicesSettings];
   // Open sign-in.
diff --git a/ios/chrome/browser/ui/settings/signin_settings_egtest.mm b/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
index aeaa01c..d998afa 100644
--- a/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
+++ b/ios/chrome/browser/ui/settings/signin_settings_egtest.mm
@@ -54,9 +54,9 @@
 
 // Tests signing in, using the primary button with a warm state.
 - (void)testSignInPromoWithWarmStateUsingPrimaryButton {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
   [ChromeEarlGreyUI openSettingsMenu];
   [SigninEarlGreyUI
@@ -65,7 +65,7 @@
   [SigninEarlGreyUI confirmSigninConfirmationDialog];
 
   // User signed in.
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity];
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI checkSigninPromoNotVisible];
   [[EarlGrey selectElementWithMatcher:SettingsAccountButton()]
       assertWithMatcher:grey_interactable()];
@@ -73,19 +73,19 @@
 
 // Tests signing in, using the secondary button with a warm state.
 - (void)testSignInPromoWithWarmStateUsingSecondaryButton {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
   [ChromeEarlGreyUI openSettingsMenu];
   [SigninEarlGreyUI
       checkSigninPromoVisibleWithMode:SigninPromoViewModeWarmState];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  [SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity.userEmail];
   [SigninEarlGreyUI confirmSigninConfirmationDialog];
 
   // User signed in.
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity];
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
   [SigninEarlGreyUI checkSigninPromoNotVisible];
   [[EarlGrey selectElementWithMatcher:SettingsAccountButton()]
       assertWithMatcher:grey_interactable()];
diff --git a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
index f8a1582..8389ec1 100644
--- a/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
+++ b/ios/chrome/browser/ui/settings/sync/utils/sync_fake_server_egtest.mm
@@ -89,10 +89,10 @@
   [self addBookmark:GURL("https://www.foo.com") withTitle:@"foo"];
 
   // Sign in to sync, after a bookmark has been added.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Assert that the correct number of bookmarks have been synced.
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -101,10 +101,10 @@
 
 // Tests that a bookmark added on the client is uploaded to the Sync server.
 - (void)testSyncUploadBookmark {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Add a bookmark after sync is initialized.
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -120,10 +120,10 @@
   [ChromeEarlGrey addFakeSyncServerBookmarkWithURL:URL title:"hoo"];
 
   // Sign in to sync, after a bookmark has been injected in the sync server.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
   [[self class] assertBookmarksWithTitle:@"hoo" expectedCount:1];
 }
@@ -131,10 +131,10 @@
 // Tests that the local cache guid does not change when sync is restarted.
 - (void)testSyncCheckSameCacheGuid_SyncRestarted {
   // Sign in the fake identity.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
   // Store the original guid, then restart sync.
@@ -153,10 +153,10 @@
 // signs back in with the same account.
 - (void)testSyncCheckDifferentCacheGuid_SignOutAndSignIn {
   // Sign in a fake identity, and store the initial sync guid.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
   std::string original_guid = [ChromeEarlGrey syncCacheGUID];
 
@@ -171,7 +171,7 @@
   [ChromeEarlGrey waitForSyncInitialized:NO syncTimeout:kSyncOperationTimeout];
 
   // Sign the user back in, and verify the guid has changed.
-  [SigninEarlGreyUI signinWithIdentity:identity];
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
   GREYAssertTrue(
       [ChromeEarlGrey syncCacheGUID] != original_guid,
@@ -183,10 +183,10 @@
 // Test for http://crbug.com/413611 .
 - (void)testSyncCheckSameCacheGuid_SyncRestartedAfterSignOutAndSignIn {
   // Sign in a fake idenitty.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
   // Sign out the current user.
@@ -200,7 +200,7 @@
   [ChromeEarlGrey waitForSyncInitialized:NO syncTimeout:kSyncOperationTimeout];
 
   // Sign the user back in.
-  [SigninEarlGreyUI signinWithIdentity:identity];
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
   // Record the initial guid, before restarting sync.
@@ -229,10 +229,10 @@
   }];
 
   // Sign in to sync.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify that the autofill profile has been downloaded.
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -258,10 +258,10 @@
   }];
 
   // Sign in to sync.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify that the autofill profile has been downloaded.
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -305,10 +305,10 @@
   }];
 
   // Sign in to sync.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify that the autofill profile has been downloaded
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -346,10 +346,10 @@
   [ChromeEarlGrey loadURL:URL2];
 
   // Sign in to sync, after opening two tabs.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Verify the sessions on the sync server.
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
@@ -374,10 +374,10 @@
   [ChromeEarlGrey addHistoryServiceTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
@@ -402,10 +402,10 @@
   [ChromeEarlGrey addFakeSyncServerTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
@@ -429,10 +429,10 @@
   [ChromeEarlGrey addFakeSyncServerTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
 
@@ -464,10 +464,10 @@
   [ChromeEarlGrey addHistoryServiceTypedURL:mockURL];
 
   // Sign in to sync.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
-  [SigninEarlGreyUI signinWithIdentity:identity];
+      fakeIdentity);
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   [ChromeEarlGrey waitForSyncInitialized:YES syncTimeout:kSyncOperationTimeout];
   [ChromeEarlGrey triggerSyncCycleForType:syncer::TYPED_URLS];
diff --git a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm
index ebe1fad2e..be1c8ab 100644
--- a/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm
+++ b/ios/chrome/browser/ui/signin_interaction/signin_interaction_controller_egtest.mm
@@ -125,14 +125,14 @@
 // correctly when there is already an identity on the device.
 - (void)testSignInOneUser {
   // Set up a fake identity.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
-  [SigninEarlGreyUI signinWithIdentity:identity];
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
-  // Check |identity| is signed-in.
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity];
+  // Check |fakeIdentity| is signed-in.
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
 }
 
 // Tests signing in with one account, switching sync account to a second and
@@ -147,22 +147,22 @@
   // Set up the fake identities.
   ios::FakeChromeIdentityService* identity_service =
       ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
-  FakeChromeIdentity* identity1 = [SigninEarlGreyUtils fakeIdentity1];
-  FakeChromeIdentity* identity2 = [SigninEarlGreyUtils fakeIdentity2];
-  identity_service->AddIdentity(identity1);
-  identity_service->AddIdentity(identity2);
+  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGreyUtils fakeIdentity2];
+  identity_service->AddIdentity(fakeIdentity1);
+  identity_service->AddIdentity(fakeIdentity2);
 
-  [SigninEarlGreyUI signinWithIdentity:identity1];
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity1];
   [SigninEarlGreyUI signOutWithManagedAccount:NO];
 
-  // Sign in with |identity2|.
+  // Sign in with |fakeIdentity2|.
   [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:SecondarySignInButton()]
       performAction:grey_tap()];
-  [SigninEarlGreyUI selectIdentityWithEmail:identity2.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity2.userEmail];
   [SigninEarlGreyUI confirmSigninConfirmationDialog];
 
-  // Switch Sync account to |identity2| should ask whether date should be
+  // Switch Sync account to |fakeIdentity2| should ask whether date should be
   // imported or kept separate. Choose to keep data separate.
   [[EarlGrey selectElementWithMatcher:
                  chrome_test_util::SettingsImportDataKeepSeparateButton()]
@@ -173,7 +173,7 @@
       performAction:grey_tap()];
 
   // Check the signed-in user did change.
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity2];
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity2];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -191,23 +191,23 @@
   // Set up the fake identities.
   ios::FakeChromeIdentityService* identity_service =
       ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
-  FakeChromeIdentity* identity1 = [SigninEarlGreyUtils fakeIdentity1];
-  FakeChromeIdentity* identity2 = [SigninEarlGreyUtils fakeIdentity2];
-  identity_service->AddIdentity(identity1);
-  identity_service->AddIdentity(identity2);
+  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGreyUtils fakeIdentity2];
+  identity_service->AddIdentity(fakeIdentity1);
+  identity_service->AddIdentity(fakeIdentity2);
 
-  // Sign in to |identity1|.
-  [SigninEarlGreyUI signinWithIdentity:identity1];
+  // Sign in to |fakeIdentity1|.
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity1];
   [SigninEarlGreyUI signOutWithManagedAccount:NO];
 
-  // Sign in with |identity2|.
+  // Sign in with |fakeIdentity2|.
   [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:SecondarySignInButton()]
       performAction:grey_tap()];
-  [SigninEarlGreyUI selectIdentityWithEmail:identity2.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity2.userEmail];
   [SigninEarlGreyUI confirmSigninConfirmationDialog];
 
-  // Switch Sync account to |identity2| should ask whether date should be
+  // Switch Sync account to |fakeIdentity2| should ask whether date should be
   // imported or kept separate. Choose to import the data.
   [[EarlGrey selectElementWithMatcher:chrome_test_util::
                                           SettingsImportDataImportButton()]
@@ -217,7 +217,7 @@
       performAction:grey_tap()];
 
   // Check the signed-in user did change.
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity2];
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity2];
 
   [[EarlGrey selectElementWithMatcher:SettingsDoneButton()]
       performAction:grey_tap()];
@@ -225,12 +225,12 @@
 
 // Tests that signing out from the Settings works correctly.
 - (void)testSignInDisconnectFromChrome {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
-  // Sign in to |identity|.
-  [SigninEarlGreyUI signinWithIdentity:identity];
+  // Sign in to |fakeIdentity|.
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity];
 
   // Sign out.
   [SigninEarlGreyUI signOutWithManagedAccount:NO];
@@ -239,10 +239,10 @@
 // Tests that signing out of a managed account from the Settings works
 // correctly.
 - (void)testSignInDisconnectFromChromeManaged {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeManagedIdentity];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeManagedIdentity];
 
   // Sign-in with a managed account.
-  [SigninEarlGreyUI signinWithIdentity:identity isManagedAccount:YES];
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity isManagedAccount:YES];
 
   // Sign out.
   [SigninEarlGreyUI signOutWithManagedAccount:YES];
@@ -255,13 +255,13 @@
 // and closing the Settings correctly leaves the user signed in without any
 // Settings shown.
 - (void)testSignInOpenSettings {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  [SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity.userEmail];
 
   // Wait until the next screen appears.
   [SigninEarlGreyUI tapSettingsLink];
@@ -275,7 +275,7 @@
           IDS_IOS_SETTINGS_TITLE);
   [[EarlGrey selectElementWithMatcher:settings_matcher]
       assertWithMatcher:grey_notVisible()];
-  [SigninEarlGreyUtils checkSignedInWithIdentity:identity];
+  [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
 }
 
 // Opens the sign in screen and then cancel it by opening a new tab. Ensures
@@ -283,9 +283,9 @@
 - (void)testSignInCancelIdentityPicker {
   // Add an identity to avoid arriving on the Add Account screen when opening
   // sign-in.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
@@ -302,7 +302,7 @@
   // this will fail.
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  [SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity.userEmail];
 
   VerifyChromeSigninViewVisible();
 
@@ -319,25 +319,25 @@
   // Set up the fake identities.
   ios::FakeChromeIdentityService* identity_service =
       ios::FakeChromeIdentityService::GetInstanceFromChromeProvider();
-  FakeChromeIdentity* identity1 = [SigninEarlGreyUtils fakeIdentity1];
-  FakeChromeIdentity* identity2 = [SigninEarlGreyUtils fakeIdentity2];
-  identity_service->AddIdentity(identity1);
-  identity_service->AddIdentity(identity2);
+  FakeChromeIdentity* fakeIdentity1 = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity2 = [SigninEarlGreyUtils fakeIdentity2];
+  identity_service->AddIdentity(fakeIdentity1);
+  identity_service->AddIdentity(fakeIdentity2);
 
-  // This signs in |identity2| first, ensuring that the "Clear Data Before
+  // This signs in |fakeIdentity2| first, ensuring that the "Clear Data Before
   // Syncing" dialog is shown during the second sign-in. This dialog will
   // effectively block the authentication flow, ensuring that the authentication
   // flow is always still running when the sign-in is being cancelled.
-  [SigninEarlGreyUI signinWithIdentity:identity2];
+  [SigninEarlGreyUI signinWithFakeIdentity:fakeIdentity2];
 
   // Sign out.
   [SigninEarlGreyUI signOutWithManagedAccount:NO];
 
-  // Sign in with |identity1|.
+  // Sign in with |fakeIdentity1|.
   [ChromeEarlGreyUI openSettingsMenu];
   [[EarlGrey selectElementWithMatcher:SecondarySignInButton()]
       performAction:grey_tap()];
-  [SigninEarlGreyUI selectIdentityWithEmail:identity1.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity1.userEmail];
   // The authentication flow is only created when the confirm button is
   // selected. Note that authentication flow actually blocks as the
   // "Clear Browsing Before Syncing" dialog is presented.
@@ -355,7 +355,7 @@
   // this will fail.
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  [SigninEarlGreyUI selectIdentityWithEmail:identity1.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity1.userEmail];
   VerifyChromeSigninViewVisible();
 
   // Close sign-in screen and Settings.
@@ -369,9 +369,9 @@
 // done. Ensures that the sign in screen is correctly dismissed.
 // Regression test for crbug.com/596029.
 - (void)testSignInCancelFromBookmarks {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
   // Open Bookmarks and tap on Sign In promo button.
   [ChromeEarlGreyUI openToolsMenu];
@@ -379,7 +379,7 @@
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
 
   // Assert sign-in screen was shown.
-  [SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity.userEmail];
   VerifyChromeSigninViewVisible();
 
   // Open new tab to cancel sign-in.
@@ -395,7 +395,7 @@
   [ChromeEarlGreyUI openToolsMenu];
   [ChromeEarlGreyUI tapToolsMenuButton:chrome_test_util::BookmarksMenuButton()];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  [SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity.userEmail];
   VerifyChromeSigninViewVisible();
 
   // Close sign-in screen and Bookmarks.
@@ -476,9 +476,9 @@
   if (!base::ios::IsRunningOnOrLater(13, 0, 0)) {
     EARL_GREY_TEST_SKIPPED(@"Test disabled on iOS 12 and lower.");
   }
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
   [self openSigninFromView:OpenSigninMethodFromSettings tapSettingsLink:YES];
 
   [[EarlGrey
@@ -539,9 +539,9 @@
 // |tapSettingsLink| if YES, the setting link is tapped before opening the URL.
 - (void)assertOpenURLWhenSigninFromView:(OpenSigninMethod)openSigninMethod
                         tapSettingsLink:(BOOL)tapSettingsLink {
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
   [self openSigninFromView:openSigninMethod tapSettingsLink:tapSettingsLink];
   // Open the URL as if it was opened from another app.
   UIApplication* application = UIApplication.sharedApplication;
@@ -557,7 +557,7 @@
   GREYAssertEqual(expectedString, webState->GetVisibleURL(), @"url not loaded");
   if (tapSettingsLink) {
     // Should be signed in.
-    [SigninEarlGreyUtils checkSignedInWithIdentity:identity];
+    [SigninEarlGreyUtils checkSignedInWithFakeIdentity:fakeIdentity];
   } else {
     // Should be not signed in.
     [SigninEarlGreyUtils checkSignedOut];
@@ -587,23 +587,23 @@
 // Tests to remove the last identity in the identity chooser.
 - (void)testRemoveLastAccount {
   // Set up a fake identity.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
   // Open the identity chooser.
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  WaitForMatcher(identityChooserButtonMatcherWithEmail(identity.userEmail));
+  WaitForMatcher(identityChooserButtonMatcherWithEmail(fakeIdentity.userEmail));
 
   // Remove the fake identity.
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()
-      ->RemoveIdentity(identity);
+      ->RemoveIdentity(fakeIdentity);
   [[GREYUIThreadExecutor sharedInstance] drainUntilIdle];
 
   // Check that the identity has been removed.
   [[EarlGrey selectElementWithMatcher:identityChooserButtonMatcherWithEmail(
-                                          identity.userEmail)]
+                                          fakeIdentity.userEmail)]
       assertWithMatcher:grey_notVisible()];
 }
 
@@ -615,9 +615,9 @@
 - (void)DISABLED_testSignInCancelAddAccount {
   // Add an identity to avoid arriving on the Add Account screen when opening
   // sign-in.
-  FakeChromeIdentity* identity = [SigninEarlGreyUtils fakeIdentity1];
+  FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
   ios::FakeChromeIdentityService::GetInstanceFromChromeProvider()->AddIdentity(
-      identity);
+      fakeIdentity);
 
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
@@ -642,7 +642,7 @@
   // this will fail.
   [ChromeEarlGreyUI openSettingsMenu];
   [ChromeEarlGreyUI tapSettingsMenuButton:SecondarySignInButton()];
-  [SigninEarlGreyUI selectIdentityWithEmail:identity.userEmail];
+  [SigninEarlGreyUI selectIdentityWithEmail:fakeIdentity.userEmail];
   VerifyChromeSigninViewVisible();
 
   // Close sign-in screen and Settings.
diff --git a/ios/chrome/browser/ui/ui_feature_flags.cc b/ios/chrome/browser/ui/ui_feature_flags.cc
index 7e19dc8..e419523 100644
--- a/ios/chrome/browser/ui/ui_feature_flags.cc
+++ b/ios/chrome/browser/ui/ui_feature_flags.cc
@@ -29,7 +29,7 @@
                                       base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kEmbedderBlockRestoreUrl{"EmbedderBlockRestoreUrl",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kDisableAnimationOnLowBattery{
     "DisableAnimationOnLowBattery", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ios/chrome/test/earl_grey/chrome_egtest_bundle_main.mm b/ios/chrome/test/earl_grey/chrome_egtest_bundle_main.mm
index fb40e00..2052557d 100644
--- a/ios/chrome/test/earl_grey/chrome_egtest_bundle_main.mm
+++ b/ios/chrome/test/earl_grey/chrome_egtest_bundle_main.mm
@@ -89,13 +89,15 @@
   DCHECK(!_testMain);
   _testMain = std::make_unique<TestMain>();
 
-  // Ensure that //ios/web and the bulk of //ios/chrome/browser are not compiled
-  // into the test module. This is hard to assert at compile time, due to
-  // transitive dependencies, but at runtime it's easy to check if certain key
-  // classes are present or absent.
+  // Ensure that //ios/web, the bulk of //ios/chrome/browser and
+  // //ios/public/provider/chrome/browser/signin are not compiled into the test
+  // module. This is hard to assert at compile time, due to transitive
+  // dependencies, but at runtime it's easy to check if certain key classes are
+  // present or absent.
   CHECK(NSClassFromString(@"CRWWebController") == nil);
   CHECK(NSClassFromString(@"MainController") == nil);
   CHECK(NSClassFromString(@"BrowserViewController") == nil);
+  CHECK(NSClassFromString(@"ChromeIdentity") == nil);
 }
 
 - (void)testBundleDidFinish:(NSBundle*)testBundle {
diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc
index 5b342cc..1180f56 100644
--- a/ipc/ipc_sync_channel.cc
+++ b/ipc/ipc_sync_channel.cc
@@ -288,8 +288,8 @@
         listener_task_runner_(base::ThreadTaskRunnerHandle::Get()),
         sync_dispatch_watcher_(std::make_unique<mojo::SyncEventWatcher>(
             &dispatch_event_,
-            base::Bind(&ReceivedSyncMsgQueue::OnDispatchEventReady,
-                       base::Unretained(this)))) {
+            base::BindRepeating(&ReceivedSyncMsgQueue::OnDispatchEventReady,
+                                base::Unretained(this)))) {
     sync_dispatch_watcher_->AllowWokenUpBySyncWatchOnSameThread();
   }
 
@@ -662,13 +662,14 @@
     bool dispatch = false;
     bool send_done = false;
     bool should_pump_messages = false;
-    base::Closure on_send_done_callback = base::Bind(&OnEventReady, &send_done);
+    base::RepeatingClosure on_send_done_callback =
+        base::BindRepeating(&OnEventReady, &send_done);
     registry->RegisterEvent(context->GetSendDoneEvent(), on_send_done_callback);
 
-    base::Closure on_pump_messages_callback;
+    base::RepeatingClosure on_pump_messages_callback;
     if (pump_messages_event) {
       on_pump_messages_callback =
-          base::Bind(&OnEventReady, &should_pump_messages);
+          base::BindRepeating(&OnEventReady, &should_pump_messages);
       registry->RegisterEvent(pump_messages_event, on_pump_messages_callback);
     }
 
diff --git a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
index 34066500..0be3797 100644
--- a/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
+++ b/net/android/java/src/org/chromium/net/AndroidNetworkLibrary.java
@@ -256,20 +256,34 @@
             return -1;
         }
 
-        Intent intent = null;
-        try {
-            intent = ContextUtils.getApplicationContext().registerReceiver(
-                    null, new IntentFilter(WifiManager.RSSI_CHANGED_ACTION));
-        } catch (IllegalArgumentException e) {
-            // Some devices unexpectedly throw IllegalArgumentException when registering
-            // the broadcast receiver. See https://crbug.com/984179.
-            return -1;
-        }
-        if (intent == null) {
-            return -1;
+        int rssi;
+        // On Android Q and above, the WifiInfo cannot be obtained through broadcast. See
+        // https://crbug.com/1026686.
+        if (haveAccessWifiState()) {
+            WifiManager wifiManager =
+                    (WifiManager) ContextUtils.getApplicationContext().getSystemService(
+                            Context.WIFI_SERVICE);
+            WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+            if (wifiInfo == null) {
+                return -1;
+            }
+            rssi = wifiInfo.getRssi();
+        } else {
+            Intent intent = null;
+            try {
+                intent = ContextUtils.getApplicationContext().registerReceiver(
+                        null, new IntentFilter(WifiManager.RSSI_CHANGED_ACTION));
+            } catch (IllegalArgumentException e) {
+                // Some devices unexpectedly throw IllegalArgumentException when registering
+                // the broadcast receiver. See https://crbug.com/984179.
+                return -1;
+            }
+            if (intent == null) {
+                return -1;
+            }
+            rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, Integer.MIN_VALUE);
         }
 
-        final int rssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, Integer.MIN_VALUE);
         if (rssi == Integer.MIN_VALUE) {
             return -1;
         }
diff --git a/net/base/features.cc b/net/base/features.cc
index 67d67168..7abd6c8 100644
--- a/net/base/features.cc
+++ b/net/base/features.cc
@@ -74,8 +74,14 @@
     "SameSiteDefaultChecksMethodRigorously", base::FEATURE_DISABLED_BY_DEFAULT};
 
 #if BUILDFLAG(BUILTIN_CERT_VERIFIER_FEATURE_SUPPORTED)
-const base::Feature kCertVerifierBuiltinFeature{
-    "CertVerifierBuiltin", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kCertVerifierBuiltinFeature {
+  "CertVerifierBuiltin",
+#if defined(OS_CHROMEOS)
+      base::FEATURE_ENABLED_BY_DEFAULT
+#else
+      base::FEATURE_DISABLED_BY_DEFAULT
+#endif
+};
 #endif
 
 const base::Feature kAppendFrameOriginToNetworkIsolationKey{
diff --git a/remoting/host/it2me/it2me_native_messaging_host_main.cc b/remoting/host/it2me/it2me_native_messaging_host_main.cc
index 1df0d31..7eff440 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_main.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_main.cc
@@ -37,7 +37,9 @@
 #endif  // defined(OS_LINUX)
 
 #if defined(OS_MACOSX)
+#include "base/mac/mac_util.h"
 #include "base/mac/scoped_nsautorelease_pool.h"
+#include "remoting/host/desktop_capturer_checker.h"
 #include "remoting/host/mac/permission_utils.h"
 #endif  // defined(OS_MACOSX)
 
@@ -212,9 +214,16 @@
     return mac::CanInjectInput() ? EXIT_SUCCESS : EXIT_FAILURE;
   }
   if (cmd_line->HasSwitch(kCheckScreenRecordingPermissionSwitchName)) {
+    // Trigger screen-capture, even if CanRecordScreen() returns true. It uses a
+    // heuristic that might not be 100% reliable, but it is critically
+    // important to add the host bundle to the list of apps under
+    // Security & Privacy -> Screen Recording.
+    if (base::mac::IsAtLeastOS10_15()) {
+      DesktopCapturerChecker().TriggerSingleCapture();
+    }
     return mac::CanRecordScreen() ? EXIT_SUCCESS : EXIT_FAILURE;
   }
-#endif
+#endif  // defined(OS_MACOSX)
 
   // NetworkChangeNotifier must be initialized after SingleThreadTaskExecutor.
   std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier(
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 6d485e6..42791ba 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -507,6 +507,14 @@
     return false;
   }
   if (cmd_line->HasSwitch(kCheckScreenRecordingPermissionSwitchName)) {
+    // Trigger screen-capture, even if CanRecordScreen() returns true. It uses a
+    // heuristic that might not be 100% reliable, but it is critically
+    // important to add the host bundle to the list of apps under
+    // Security & Privacy -> Screen Recording.
+    if (base::mac::IsAtLeastOS10_15()) {
+      capture_checker_ = std::make_unique<DesktopCapturerChecker>();
+      capture_checker_->TriggerSingleCapture();
+    }
     checking_permission_state_ = true;
     permission_granted_ = mac::CanRecordScreen();
     return false;
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 35053f8..7224ec2 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -28775,25 +28775,6 @@
             }
           ]
         }
-      },
-      {
-        "isolate_name": "monochrome_apk_checker",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "monochrome_apk_checker",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ]
-        }
       }
     ],
     "junit_tests": [
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 7c912d4..6ce1850 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -2278,7 +2278,7 @@
       "--ui-test-action-timeout=60000",
       "--ui-test-action-max-timeout=60000",
       "--test-launcher-timeout=60000",
-      "--gtest_filter=TabCapturePerformanceTest.*:CastV2PerformanceTest.*",
+      "--gtest_filter=*/TabCapturePerformanceTest.*:*/CastV2PerformanceTest.*",
     ],
     "label": "//chrome/test:performance_browser_tests",
     "script": "//testing/scripts/run_performance_tests.py",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 74b9546..ca171ac3 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -5915,6 +5915,12 @@
     'marshmallow_pie_isolated_scripts': [
       'android_isolated_scripts',
       'components_perftests_isolated_scripts',
+      'telemetry_perf_unittests_isolated_scripts',
+    ],
+
+    'marshmallow_pie_isolated_scripts_with_proguard': [
+      'android_isolated_scripts',
+      'components_perftests_isolated_scripts',
       'monochrome_apk_checker_isolated_script',
       'telemetry_perf_unittests_isolated_scripts',
     ],
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 4efe740..b4bdb34 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -638,7 +638,7 @@
         ],
         'test_suites': {
           'gtest_tests': 'android_pie_rel_gtests',
-          'isolated_scripts': 'marshmallow_pie_isolated_scripts',
+          'isolated_scripts': 'marshmallow_pie_isolated_scripts_with_proguard',
         },
         'use_swarming': True,
         'os_type': 'android',
@@ -708,7 +708,7 @@
         'os_type': 'android',
         'test_suites': {
           'gtest_tests': 'android_lollipop_marshmallow_gtests',
-          'isolated_scripts': 'marshmallow_pie_isolated_scripts',
+          'isolated_scripts': 'marshmallow_pie_isolated_scripts_with_proguard',
         }
       },
       'android-pie-x86-fyi-rel': {
diff --git a/testing/merge_scripts/code_coverage/merge_lib.py b/testing/merge_scripts/code_coverage/merge_lib.py
index 89ab24e..1e64c7b 100644
--- a/testing/merge_scripts/code_coverage/merge_lib.py
+++ b/testing/merge_scripts/code_coverage/merge_lib.py
@@ -121,7 +121,7 @@
       A list of *invalid* profraw files.
       A list of profraw files that have counter overflows.
   """
-  logging.info('Validating and converting .profraw files.')
+  logging.info('Validating and converting .profraw files: %r', profraw_files)
 
   for profraw_file in profraw_files:
     if not profraw_file.endswith('.profraw'):
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index d56db77..b9bcb89 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1506,19 +1506,6 @@
     "CertVerifierBuiltin": [
         {
             "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "CertVerifierBuiltin"
-                    ]
-                }
-            ]
-        },
-        {
-            "platforms": [
                 "linux"
             ],
             "experiments": [
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc
index c3236469..a6e8c68 100644
--- a/third_party/blink/renderer/core/animation/animation.cc
+++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -224,7 +224,7 @@
   return content_ ? content_->SpecifiedTiming().EndTimeInternal() : 0;
 }
 
-bool Animation::Limited(double current_time) const {
+bool Animation::Limited(base::Optional<double> current_time) const {
   return (EffectivePlaybackRate() < 0 && current_time <= 0) ||
          (EffectivePlaybackRate() > 0 && current_time >= EffectEnd());
 }
@@ -368,21 +368,24 @@
     return;
 
   if (hold_time_) {
-    double new_current_time = hold_time_.value();
+    base::Optional<double> new_current_time = hold_time_;
     if (internal_play_state_ == kFinished && start_time_ && timeline_) {
       // Add hystersis due to floating point error accumulation
-      if (!Limited(CalculateCurrentTime() + 0.001 * playback_rate_)) {
+      base::Optional<double> current_time = CalculateCurrentTime();
+      DCHECK(current_time);
+      if (!Limited(current_time.value() + 0.001 * playback_rate_)) {
         // The current time became unlimited, eg. due to a backwards
         // seek of the timeline.
-        new_current_time = CalculateCurrentTime();
-      } else if (!Limited(hold_time_.value())) {
+        new_current_time = current_time;
+      } else if (!Limited(hold_time_)) {
         // The hold time became unlimited, eg. due to the effect
         // becoming longer.
         new_current_time =
-            clampTo<double>(CalculateCurrentTime(), 0, EffectEnd());
+            clampTo<double>(current_time.value(), 0, EffectEnd());
       }
     }
-    SetCurrentTimeInternal(new_current_time, reason);
+    DCHECK(new_current_time);
+    SetCurrentTimeInternal(new_current_time.value(), reason);
   } else if (Limited(CalculateCurrentTime())) {
     hold_time_ = playback_rate_ < 0 ? 0 : EffectEnd();
   }
@@ -435,15 +438,15 @@
 }
 
 double Animation::CurrentTimeInternal() const {
-  return hold_time_.value_or(CalculateCurrentTime());
+  return hold_time_.value_or(CalculateCurrentTime().value_or(NullValue()));
 }
 
-double Animation::UnlimitedCurrentTimeInternal() const {
+base::Optional<double> Animation::UnlimitedCurrentTimeInternal() const {
 #if DCHECK_IS_ON()
   CurrentTimeInternal();
 #endif
   return PlayStateInternal() == kPaused || !start_time_
-             ? CurrentTimeInternal()
+             ? ValueOrUnresolved(CurrentTimeInternal())
              : CalculateCurrentTime();
 }
 
@@ -670,14 +673,14 @@
   return start_time;
 }
 
-double Animation::CalculateCurrentTime() const {
+base::Optional<double> Animation::CalculateCurrentTime() const {
   if (!start_time_ || !timeline_ || !timeline_->IsActive())
-    return NullValue();
+    return base::nullopt;
   base::Optional<double> timeline_time = timeline_->CurrentTimeSeconds();
   // TODO(crbug.com/916117): Handle NaN time for scroll-linked animations.
   if (!timeline_time) {
     DCHECK(timeline_->IsScrollTimeline());
-    return NullValue();
+    return base::nullopt;
   }
   return (timeline_time.value() - start_time_.value()) * playback_rate_;
 }
@@ -1191,7 +1194,8 @@
   // distinction, a once-finished animation would remain finished even when its
   // timeline progresses in the opposite direction.
   double unconstrained_current_time =
-      did_seek ? CurrentTimeInternal() : CalculateCurrentTime();
+      did_seek ? CurrentTimeInternal()
+               : CalculateCurrentTime().value_or(NullValue());
 
   // 2. Conditionally update the hold time.
   if (!IsNull(unconstrained_current_time) && start_time_ && !pending_play_ &&
@@ -1348,14 +1352,18 @@
     //         animation with the did seek flag set to false, and the
     //         synchronously notify flag set to false.
     case kFinished: {
-      double unconstrained_current_time = CalculateCurrentTime();
+      base::Optional<double> unconstrained_current_time =
+          CalculateCurrentTime();
       base::Optional<double> timeline_time =
           timeline_ ? timeline_->CurrentTimeSeconds() : base::nullopt;
       if (playback_rate) {
         if (timeline_time) {
           start_time_ =
-              ValueOrUnresolved(timeline_time.value() -
-                                unconstrained_current_time / playback_rate);
+              (timeline_time && unconstrained_current_time)
+                  ? ValueOrUnresolved((timeline_time.value() -
+                                       unconstrained_current_time.value()) /
+                                      playback_rate)
+                  : base::nullopt;
         }
       } else {
         start_time_ = timeline_time;
@@ -1385,7 +1393,11 @@
     finished_promise_ = MakeGarbageCollected<AnimationPromise>(
         ExecutionContext::From(script_state), this,
         AnimationPromise::kFinished);
-    if (PlayStateInternal() == kFinished)
+    // Defer resolving the finished promise if the finish notification task is
+    // pending. The finished state could change before the next microtask
+    // checkpoint.
+    if (CalculateAnimationPlayState() == kFinished &&
+        !pending_finish_notification_)
       finished_promise_->Resolve(this);
   }
   return finished_promise_->Promise(script_state->World());
diff --git a/third_party/blink/renderer/core/animation/animation.h b/third_party/blink/renderer/core/animation/animation.h
index 04e6dd4..c39d83e5 100644
--- a/third_party/blink/renderer/core/animation/animation.h
+++ b/third_party/blink/renderer/core/animation/animation.h
@@ -126,7 +126,7 @@
   void setCurrentTime(double new_current_time,
                       bool is_null,
                       ExceptionState& = ASSERT_NO_EXCEPTION);
-  double UnlimitedCurrentTimeInternal() const;
+  base::Optional<double> UnlimitedCurrentTimeInternal() const;
 
   static const char* PlayStateString(AnimationPlayState);
   String playState() const { return PlayStateString(animation_play_state_); }
@@ -265,7 +265,7 @@
   void ForceServiceOnNextFrame();
 
   double EffectEnd() const;
-  bool Limited(double current_time) const;
+  bool Limited(base::Optional<double> current_time) const;
 
   // Playback rate that will take effect once any pending tasks are resolved.
   // If there are no pending tasks, then the effective playback rate equals the
@@ -287,7 +287,7 @@
   AnimationPlayState CalculateAnimationPlayState() const;
 
   base::Optional<double> CalculateStartTime(double current_time) const;
-  double CalculateCurrentTime() const;
+  base::Optional<double> CalculateCurrentTime() const;
 
   void UpdateCurrentTimingState(TimingUpdateReason);
 
diff --git a/third_party/blink/renderer/core/animation/inert_effect.cc b/third_party/blink/renderer/core/animation/inert_effect.cc
index 7148509..d808645 100644
--- a/third_party/blink/renderer/core/animation/inert_effect.cc
+++ b/third_party/blink/renderer/core/animation/inert_effect.cc
@@ -37,11 +37,11 @@
 InertEffect::InertEffect(KeyframeEffectModelBase* model,
                          const Timing& timing,
                          bool paused,
-                         double inherited_time)
+                         base::Optional<double> inherited_time)
     : AnimationEffect(timing),
       model_(model),
       paused_(paused),
-      inherited_time_(ValueOrUnresolved(inherited_time)) {}
+      inherited_time_(inherited_time) {}
 
 void InertEffect::Sample(HeapVector<Member<Interpolation>>& result) const {
   UpdateInheritedTime(inherited_time_, kTimingUpdateOnDemand);
diff --git a/third_party/blink/renderer/core/animation/inert_effect.h b/third_party/blink/renderer/core/animation/inert_effect.h
index 1f85a13..3b65625 100644
--- a/third_party/blink/renderer/core/animation/inert_effect.h
+++ b/third_party/blink/renderer/core/animation/inert_effect.h
@@ -45,7 +45,7 @@
   InertEffect(KeyframeEffectModelBase*,
               const Timing&,
               bool paused,
-              double inherited_time);
+              base::Optional<double> inherited_time);
 
   void Sample(HeapVector<Member<Interpolation>>&) const;
   KeyframeEffectModelBase* Model() const { return model_.Get(); }
diff --git a/third_party/blink/renderer/core/dom/abort_controller.cc b/third_party/blink/renderer/core/dom/abort_controller.cc
index 3ffb73a..5bfa9f48 100644
--- a/third_party/blink/renderer/core/dom/abort_controller.cc
+++ b/third_party/blink/renderer/core/dom/abort_controller.cc
@@ -10,11 +10,11 @@
 namespace blink {
 
 AbortController* AbortController::Create(ExecutionContext* context) {
-  return MakeGarbageCollected<AbortController>(context);
+  return MakeGarbageCollected<AbortController>(
+      MakeGarbageCollected<AbortSignal>(context));
 }
 
-AbortController::AbortController(ExecutionContext* execution_context)
-    : signal_(MakeGarbageCollected<AbortSignal>(execution_context)) {}
+AbortController::AbortController(AbortSignal* signal) : signal_(signal) {}
 
 AbortController::~AbortController() = default;
 
diff --git a/third_party/blink/renderer/core/dom/abort_controller.h b/third_party/blink/renderer/core/dom/abort_controller.h
index 7381dec..133f627 100644
--- a/third_party/blink/renderer/core/dom/abort_controller.h
+++ b/third_party/blink/renderer/core/dom/abort_controller.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_ABORT_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_ABORT_CONTROLLER_H_
 
+#include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 
@@ -16,13 +17,13 @@
 // Implementation of https://dom.spec.whatwg.org/#interface-abortcontroller
 // See also design doc at
 // https://docs.google.com/document/d/1OuoCG2uiijbAwbCw9jaS7tHEO0LBO_4gMNio1ox0qlY/edit
-class AbortController final : public ScriptWrappable {
+class CORE_EXPORT AbortController : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
   static AbortController* Create(ExecutionContext*);
 
-  explicit AbortController(ExecutionContext*);
+  explicit AbortController(AbortSignal*);
   ~AbortController() override;
 
   // abort_controller.idl
diff --git a/third_party/blink/renderer/core/dom/abort_signal.h b/third_party/blink/renderer/core/dom/abort_signal.h
index 96dcea1..1915edd 100644
--- a/third_party/blink/renderer/core/dom/abort_signal.h
+++ b/third_party/blink/renderer/core/dom/abort_signal.h
@@ -17,7 +17,7 @@
 class ExecutionContext;
 
 // Implementation of https://dom.spec.whatwg.org/#interface-AbortSignal
-class CORE_EXPORT AbortSignal final : public EventTargetWithInlineData {
+class CORE_EXPORT AbortSignal : public EventTargetWithInlineData {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -59,6 +59,8 @@
   // |this| is the followingSignal described in the standard.
   void Follow(AbortSignal* parentSignal);
 
+  virtual bool IsTaskSignal() const { return false; }
+
   void Trace(Visitor*) override;
 
  private:
diff --git a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
index e44913c6..c791e8ed5 100644
--- a/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
+++ b/third_party/blink/renderer/core/editing/commands/clipboard_commands.cc
@@ -359,11 +359,13 @@
     KURL url;
     const String markup = SystemClipboard::GetInstance().ReadHTML(
         url, fragment_start, fragment_end);
-    if (!markup.IsEmpty()) {
+    const String sanitized_markup =
+        SanitizeMarkupWithContext(markup, fragment_start, fragment_end);
+    if (!sanitized_markup.IsEmpty()) {
       DCHECK(frame.GetDocument());
-      fragment = CreateFragmentFromMarkupWithContext(
-          *frame.GetDocument(), markup, fragment_start, fragment_end, url,
-          kDisallowScriptingAndPluginContent);
+      fragment =
+          CreateFragmentFromMarkup(*frame.GetDocument(), sanitized_markup, url,
+                                   kDisallowScriptingAndPluginContent);
     }
   }
   if (fragment)
diff --git a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
index 850eb01..c7f8afb 100644
--- a/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/replace_selection_command.cc
@@ -855,7 +855,8 @@
   Node* next = nullptr;
   for (Node* node = fragment.FirstChild(); node; node = next) {
     if (IsA<HTMLBaseElement>(*node) || IsHTMLLinkElement(*node) ||
-        IsA<HTMLMetaElement>(*node) || IsA<HTMLTitleElement>(*node)) {
+        IsA<HTMLMetaElement>(*node) || IsA<HTMLStyleElement>(*node) ||
+        IsA<HTMLTitleElement>(*node)) {
       next = NodeTraversal::NextSkippingChildren(*node);
       fragment.RemoveNode(node);
     } else {
diff --git a/third_party/blink/renderer/core/editing/commands/replace_selection_command_test.cc b/third_party/blink/renderer/core/editing/commands/replace_selection_command_test.cc
index 78934ee..0f6244a 100644
--- a/third_party/blink/renderer/core/editing/commands/replace_selection_command_test.cc
+++ b/third_party/blink/renderer/core/editing/commands/replace_selection_command_test.cc
@@ -83,35 +83,6 @@
       << "'bar' should have been inserted";
 }
 
-// This is a regression test for https://crbug.com/121163
-TEST_F(ReplaceSelectionCommandTest, styleTagsInPastedHeadIncludedInContent) {
-  GetDocument().setDesignMode("on");
-  UpdateAllLifecyclePhasesForTest();
-  GetDummyPageHolder().GetFrame().Selection().SetSelection(
-      SelectionInDOMTree::Builder()
-          .Collapse(Position(GetDocument().body(), 0))
-          .Build(),
-      SetSelectionOptions());
-
-  DocumentFragment* fragment = GetDocument().createDocumentFragment();
-  fragment->ParseHTML(
-      "<head><style>foo { bar: baz; }</style></head>"
-      "<body><p>Text</p></body>",
-      GetDocument().documentElement(), kDisallowScriptingAndPluginContent);
-
-  ReplaceSelectionCommand::CommandOptions options = 0;
-  auto* command = MakeGarbageCollected<ReplaceSelectionCommand>(
-      GetDocument(), fragment, options);
-  EXPECT_TRUE(command->Apply()) << "the replace command should have succeeded";
-
-  EXPECT_EQ(
-      "<head><style>foo { bar: baz; }</style></head>"
-      "<body><p>Text</p></body>",
-      GetDocument().body()->InnerHTMLAsString())
-      << "the STYLE and P elements should have been pasted into the body "
-      << "of the document";
-}
-
 // Helper function to set autosizing multipliers on a document.
 bool SetTextAutosizingMultiplier(Document* document, float multiplier) {
   bool multiplier_set = false;
diff --git a/third_party/blink/renderer/core/editing/editing_style_utilities.cc b/third_party/blink/renderer/core/editing/editing_style_utilities.cc
index 90ac69a..871425a9 100644
--- a/third_party/blink/renderer/core/editing/editing_style_utilities.cc
+++ b/third_party/blink/renderer/core/editing/editing_style_utilities.cc
@@ -236,4 +236,15 @@
   return nullptr;
 }
 
+void EditingStyleUtilities::StripUAStyleRulesForMarkupSanitization(
+    EditingStyle* style) {
+  // This is a hacky approach to avoid 'font-family: ""' appearing in
+  // sanitized markup.
+  // TODO(editing-dev): Implement a non-hacky fix up for all properties
+  String font_family =
+      style->Style()->GetPropertyValue(CSSPropertyID::kFontFamily);
+  if (font_family == "\"\"")
+    style->Style()->RemoveProperty(CSSPropertyID::kFontFamily);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/editing_style_utilities.h b/third_party/blink/renderer/core/editing/editing_style_utilities.h
index efaf957..1aeae1ae4 100644
--- a/third_party/blink/renderer/core/editing/editing_style_utilities.h
+++ b/third_party/blink/renderer/core/editing/editing_style_utilities.h
@@ -64,6 +64,8 @@
            unicode_bidi == CSSValueID::kEmbed;
   }
 
+  static void StripUAStyleRulesForMarkupSanitization(EditingStyle* style);
+
   static bool IsTransparentColorValue(const CSSValue*);
   static bool HasTransparentBackgroundColor(CSSStyleDeclaration*);
   static bool HasTransparentBackgroundColor(CSSPropertyValueSet*);
diff --git a/third_party/blink/renderer/core/editing/serializers/create_markup_options.cc b/third_party/blink/renderer/core/editing/serializers/create_markup_options.cc
index d726657..b1ba531e 100644
--- a/third_party/blink/renderer/core/editing/serializers/create_markup_options.cc
+++ b/third_party/blink/renderer/core/editing/serializers/create_markup_options.cc
@@ -33,4 +33,11 @@
   return *this;
 }
 
+CreateMarkupOptions::Builder&
+CreateMarkupOptions::Builder::SetIsForMarkupSanitization(
+    bool is_for_sanitization) {
+  data_.is_for_markup_sanitization_ = is_for_sanitization;
+  return *this;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/serializers/create_markup_options.h b/third_party/blink/renderer/core/editing/serializers/create_markup_options.h
index c6bc939..109fb8a 100644
--- a/third_party/blink/renderer/core/editing/serializers/create_markup_options.h
+++ b/third_party/blink/renderer/core/editing/serializers/create_markup_options.h
@@ -31,12 +31,14 @@
   bool ShouldConvertBlocksToInlines() const {
     return should_convert_blocks_to_inlines_;
   }
+  bool IsForMarkupSanitization() const { return is_for_markup_sanitization_; }
 
  private:
   Member<const Node> constraining_ancestor_;
   AbsoluteURLs should_resolve_urls_ = kDoNotResolveURLs;
   bool should_annotate_for_interchange_ = false;
   bool should_convert_blocks_to_inlines_ = false;
+  bool is_for_markup_sanitization_ = false;
 };
 
 class CORE_EXPORT CreateMarkupOptions::Builder final {
@@ -52,6 +54,7 @@
   Builder& SetShouldResolveURLs(AbsoluteURLs absolute_urls);
   Builder& SetShouldAnnotateForInterchange(bool annotate_for_interchange);
   Builder& SetShouldConvertBlocksToInlines(bool convert_blocks_for_inlines);
+  Builder& SetIsForMarkupSanitization(bool is_for_sanitization);
 
  private:
   CreateMarkupOptions data_;
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index 7b9119c..0b11c46 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -51,6 +51,7 @@
 #include "third_party/blink/renderer/core/editing/visible_selection.h"
 #include "third_party/blink/renderer/core/editing/visible_units.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
+#include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/html/html_anchor_element.h"
 #include "third_party/blink/renderer/core/html/html_body_element.h"
 #include "third_party/blink/renderer/core/html/html_br_element.h"
@@ -62,6 +63,8 @@
 #include "third_party/blink/renderer/core/html/html_table_element.h"
 #include "third_party/blink/renderer/core/html_names.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
+#include "third_party/blink/renderer/core/loader/empty_clients.h"
+#include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/runtime_call_stats.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
@@ -754,6 +757,57 @@
     text_next->remove(exception_state);
 }
 
+static Document* CreateStagingDocumentForMarkupSanitization() {
+  Page::PageClients page_clients;
+  FillWithEmptyClients(page_clients);
+  Page* page = Page::CreateNonOrdinary(page_clients);
+
+  page->GetSettings().SetScriptEnabled(false);
+  page->GetSettings().SetPluginsEnabled(false);
+  page->GetSettings().SetAcceleratedCompositingEnabled(false);
+
+  LocalFrame* frame = MakeGarbageCollected<LocalFrame>(
+      MakeGarbageCollected<EmptyLocalFrameClient>(), *page,
+      nullptr,  // FrameOwner*
+      nullptr,  // WindowAgentFactory*
+      nullptr   // InterfaceRegistry*
+  );
+  // Don't leak the actual viewport size to unsanitized markup
+  LocalFrameView* frame_view =
+      MakeGarbageCollected<LocalFrameView>(*frame, IntSize(800, 600));
+  frame->SetView(frame_view);
+  frame->Init();
+
+  Document* document = frame->GetDocument();
+  DCHECK(document);
+  DCHECK(document->IsHTMLDocument());
+  DCHECK(document->body());
+
+  return document;
+}
+
+String SanitizeMarkupWithContext(const String& raw_markup,
+                                 unsigned fragment_start,
+                                 unsigned fragment_end) {
+  Document* staging_document = CreateStagingDocumentForMarkupSanitization();
+  Element* body = staging_document->body();
+
+  DocumentFragment* fragment = CreateFragmentFromMarkupWithContext(
+      *staging_document, raw_markup, fragment_start, fragment_end, KURL(),
+      kDisallowScriptingAndPluginContent);
+
+  body->appendChild(fragment);
+  staging_document->UpdateStyleAndLayout();
+
+  // This sanitizes stylesheets in the markup into element inline styles
+  return CreateMarkup(Position::FirstPositionInNode(*body),
+                      Position::LastPositionInNode(*body),
+                      CreateMarkupOptions::Builder()
+                          .SetShouldAnnotateForInterchange(true)
+                          .SetIsForMarkupSanitization(true)
+                          .Build());
+}
+
 template class CORE_TEMPLATE_EXPORT CreateMarkupAlgorithm<EditingStrategy>;
 template class CORE_TEMPLATE_EXPORT
     CreateMarkupAlgorithm<EditingInFlatTreeStrategy>;
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.h b/third_party/blink/renderer/core/editing/serializers/serialization.h
index 17d89c5..38e125e 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.h
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.h
@@ -96,6 +96,10 @@
              const PositionInFlatTree& end,
              const CreateMarkupOptions& options = CreateMarkupOptions());
 
+String SanitizeMarkupWithContext(const String& raw_markup,
+                                 unsigned fragment_start,
+                                 unsigned fragment_end);
+
 void MergeWithNextTextNode(Text*, ExceptionState&);
 
 bool PropertyMissingOrEqualToNone(CSSPropertyValueSet*, CSSPropertyID);
diff --git a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc
index 1f6008c1..7563aaaf 100644
--- a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc
+++ b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc
@@ -120,7 +120,12 @@
     StringBuilder buffer;
     MarkupFormatter::AppendCharactersReplacingEntities(
         buffer, content, 0, content.length(), kEntityMaskInPCDATA);
-    result_.Append(ConvertHTMLTextToInterchangeFormat(buffer.ToString(), text));
+    // Keep collapsible white spaces as is during markup sanitization.
+    const String text_to_append =
+        IsForMarkupSanitization()
+            ? buffer.ToString()
+            : ConvertHTMLTextToInterchangeFormat(buffer.ToString(), text);
+    result_.Append(text_to_append);
   }
   if (inline_style)
     result_.Append("</span>");
diff --git a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.h b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.h
index 3632bcf..32cda2c 100644
--- a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.h
+++ b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.h
@@ -76,6 +76,9 @@
   bool ShouldConvertBlocksToInlines() const {
     return options_.ShouldConvertBlocksToInlines();
   }
+  bool IsForMarkupSanitization() const {
+    return options_.IsForMarkupSanitization();
+  }
 
  private:
   String RenderedText(Text&);
diff --git a/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc b/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
index 1a5a3105..86b1a10f 100644
--- a/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
+++ b/third_party/blink/renderer/core/editing/serializers/styled_markup_serializer.cc
@@ -94,12 +94,14 @@
  private:
   bool ShouldAnnotate() const;
   bool ShouldConvertBlocksToInlines() const;
+  bool IsForMarkupSanitization() const;
   void AppendStartMarkup(Node&);
   void AppendEndMarkup(Node&);
   EditingStyle* CreateInlineStyle(Element&);
   bool NeedsInlineStyle(const Element&);
   bool ShouldApplyWrappingStyle(const Node&) const;
   bool ContainsOnlyBRElement(const Element&) const;
+  bool ShouldSerializeUnrenderedElement(const Node&) const;
 
   StyledMarkupAccumulator* accumulator_;
   Member<Node> last_closed_;
@@ -113,6 +115,11 @@
 }
 
 template <typename Strategy>
+bool StyledMarkupTraverser<Strategy>::IsForMarkupSanitization() const {
+  return accumulator_ && accumulator_->IsForMarkupSanitization();
+}
+
+template <typename Strategy>
 bool StyledMarkupTraverser<Strategy>::ShouldConvertBlocksToInlines() const {
   return accumulator_->ShouldConvertBlocksToInlines();
 }
@@ -364,10 +371,7 @@
       }
 
       auto* element = DynamicTo<Element>(n);
-      if (n->GetLayoutObject() ||
-          (element && element->HasDisplayContentsStyle()) ||
-          EnclosingElementWithTag(FirstPositionInOrBeforeNode(*n),
-                                  html_names::kSelectTag)) {
+      if (n->GetLayoutObject() || ShouldSerializeUnrenderedElement(*n)) {
         // Add the node to the markup if we're not skipping the descendants
         AppendStartMarkup(*n);
 
@@ -513,6 +517,11 @@
         // FIXME: Should this be included in forceInline?
         inline_style->Style()->SetProperty(CSSPropertyID::kFloat,
                                            CSSValueID::kNone);
+
+        if (IsForMarkupSanitization()) {
+          EditingStyleUtilities::StripUAStyleRulesForMarkupSanitization(
+              inline_style);
+        }
       }
       accumulator_->AppendTextWithInlineStyle(text, inline_style);
       break;
@@ -571,6 +580,9 @@
     inline_style->MergeStyleFromRulesForSerialization(html_element);
   }
 
+  if (IsForMarkupSanitization())
+    EditingStyleUtilities::StripUAStyleRulesForMarkupSanitization(inline_style);
+
   return inline_style;
 }
 
@@ -583,6 +595,25 @@
   return IsA<HTMLBRElement>(first_child) && first_child == element.lastChild();
 }
 
+template <typename Strategy>
+bool StyledMarkupTraverser<Strategy>::ShouldSerializeUnrenderedElement(
+    const Node& node) const {
+  DCHECK(!node.GetLayoutObject());
+  if (node.IsElementNode() && To<Element>(node).HasDisplayContentsStyle())
+    return true;
+  if (EnclosingElementWithTag(FirstPositionInOrBeforeNode(node),
+                              html_names::kSelectTag)) {
+    return true;
+  }
+  if (IsForMarkupSanitization()) {
+    // During sanitization, iframes in the staging document haven't loaded and
+    // are hence not rendered. They should still be serialized.
+    if (IsA<HTMLIFrameElement>(node))
+      return true;
+  }
+  return false;
+}
+
 template class StyledMarkupSerializer<EditingStrategy>;
 template class StyledMarkupSerializer<EditingInFlatTreeStrategy>;
 
diff --git a/third_party/blink/renderer/core/html/forms/resources/time_picker.css b/third_party/blink/renderer/core/html/forms/resources/time_picker.css
index 6c331c5..ebd30d8 100644
--- a/third_party/blink/renderer/core/html/forms/resources/time_picker.css
+++ b/third_party/blink/renderer/core/html/forms/resources/time_picker.css
@@ -40,13 +40,12 @@
 }
 
 .time-cell {
-  border: 1px solid transparent;
+  border: 2px solid transparent;
+  border-radius: 2px;
   color: #101010;
   font-size: 14px;
   height: 32px;
   line-height: 32px;
-  margin: 1px;
-  scroll-margin: 1px;
   scroll-snap-align: start;
   text-align: center;
   width: 48px;
@@ -54,18 +53,15 @@
 
 .time-cell:hover {
   background: #E5E5E5;
-  border-radius: 2px;
 }
 
 .time-cell.selected {
   background-color: #CECECE;
-  border-radius: 2px;
   font-weight: bold;
 }
 
 .time-column:focus .time-cell.selected {
-  outline: auto 1px -webkit-focus-ring-color;
-  outline-offset: 0px;
+  border-color: highlight;
 }
 
 .submission-controls {
@@ -74,7 +70,7 @@
   bottom: 0px;
   display: flex;
   flex-direction: row;
-  height: 41px;
+  height: 40px;
   position: absolute;
   width: 100%;
 }
@@ -86,7 +82,8 @@
 
 .submission-button {
   background-color: #FFFFFF;
-  border: 1px solid white;
+  border: 2px solid transparent;
+  border-radius: 2px;
   height: 32px;
   margin: 4px;
   padding: 8px;
@@ -95,7 +92,11 @@
 
 .submission-button:hover {
   background-color: #E5E5E5;
-  border-radius: 2px;
+}
+
+.submission-button:focus {
+  border-color: highlight;
+  outline: none;
 }
 
 @media (forced-colors: active) {
@@ -103,7 +104,6 @@
     background-color: Window;
     color: WindowText;
     forced-color-adjust: none;
-    scroll-margin: 0px;
   }
 
   .time-cell:hover {
@@ -117,14 +117,11 @@
   }
 
   .time-column:focus .time-cell.selected {
-    border: 2px solid WindowText;
-    margin: 0px;
-    outline: none;
+    border-color: WindowText;
   }
 
   .submission-button {
     background-color: Window;
-    border-color: transparent;
     forced-color-adjust: none;
   }
 
@@ -134,7 +131,7 @@
   }
 
   .submission-button:focus {
-    outline-color: WindowText;
+    border-color: WindowText;
   }
 
   .submission-button path {
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc
index 52a3ba9f..435529d 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.cc
@@ -30,6 +30,7 @@
 
 #include "third_party/blink/renderer/core/html/track/vtt/vtt_parser.h"
 
+#include "base/metrics/histogram_functions.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
 #include "third_party/blink/renderer/core/css/style_sheet_contents.h"
@@ -97,7 +98,8 @@
       current_start_time_(0),
       current_end_time_(0),
       current_region_(nullptr),
-      client_(client) {
+      client_(client),
+      contains_style_block_(false) {
   UseCounter::Count(document, WebFeature::kVTTCueParser);
 }
 
@@ -125,6 +127,9 @@
   Parse();
   FlushPendingCue();
   region_map_.clear();
+
+  base::UmaHistogramBoolean("Accessibility.VTTContainsStyleBlock",
+                            contains_style_block_);
 }
 
 void VTTParser::Parse() {
@@ -277,12 +282,13 @@
     // line starts with the substring "STYLE" and remaining characters
     // zero or more U+0020 SPACE characters or U+0009 CHARACTER TABULATION
     // (tab) characters expected other than these characters it is invalid.
-    if (RuntimeEnabledFeatures::EmbeddedVTTStylesheetsEnabled() &&
-        line.StartsWith("STYLE") &&
-        StringView(line, kStyleIdentifierLength)
-            .IsAllSpecialCharacters<IsASpace>()) {
-      current_content_.Clear();
-      return kStyle;
+    if (line.StartsWith("STYLE") && StringView(line, kStyleIdentifierLength)
+                                        .IsAllSpecialCharacters<IsASpace>()) {
+      contains_style_block_ = true;
+      if (RuntimeEnabledFeatures::EmbeddedVTTStylesheetsEnabled()) {
+        current_content_.Clear();
+        return kStyle;
+      }
     }
   }
 
diff --git a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.h b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.h
index ede0025d..92355c8 100644
--- a/third_party/blink/renderer/core/html/track/vtt/vtt_parser.h
+++ b/third_party/blink/renderer/core/html/track/vtt/vtt_parser.h
@@ -152,6 +152,9 @@
   HeapVector<Member<CSSStyleSheet>> style_sheets_;
   HeapVector<Member<TextTrackCue>> cue_list_;
 
+  // Used for histogram metric logging only.
+  bool contains_style_block_;
+
   VTTRegionMap region_map_;
 };
 
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
index ee3bbb94..7d0e7b4 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_controller.cc
@@ -244,6 +244,13 @@
     // TODO(bokan): Make work with OOPIF. crbug.com/642378.
     if (!frame_owner->OwnedEmbeddedContentView()->IsLocalFrameView())
       return false;
+
+    // It's possible for an iframe to have a LayoutView but not have performed
+    // the lifecycle yet. We shouldn't promote such an iframe until it has
+    // since we won't be able to use the scroller inside yet.
+    Document* doc = frame_owner->contentDocument();
+    if (!doc || !doc->View() || !doc->View()->DidFirstLayout())
+      return false;
   }
 
   if (!FillsViewport(element))
diff --git a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
index fb7baaa..e1adf597 100644
--- a/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/root_scroller_test.cc
@@ -1521,10 +1521,12 @@
                           <style>html {height: 300%;}</style>">
           </iframe>
       )HTML");
+  RunPendingTasks();
+  Compositor().BeginFrame();
+
   Element* container = GetDocument().getElementById("container");
   GetDocument().setRootScroller(container);
   Compositor().BeginFrame();
-  RunPendingTasks();
   ASSERT_EQ(container,
             GetDocument().GetRootScrollerController().EffectiveRootScroller());
   ASSERT_EQ(IntSize(800, 600), GetDocument().View()->Size());
diff --git a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
index 7ba7ea2..0c955aa1 100644
--- a/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
+++ b/third_party/blink/renderer/modules/canvas/htmlcanvas/html_canvas_element_module_test.cc
@@ -151,5 +151,5 @@
   platform->RunUntilIdle();
 }
 
-INSTANTIATE_TEST_SUITE_P(, HTMLCanvasElementModuleTest, Values(true, false));
+INSTANTIATE_TEST_SUITE_P(All, HTMLCanvasElementModuleTest, Values(true, false));
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc
index 025d75d..2b813cc 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas/offscreen_canvas_test.cc
@@ -196,5 +196,5 @@
     {true, false},
     {true, true}};
 
-INSTANTIATE_TEST_SUITE_P(, OffscreenCanvasTest, ValuesIn(kTestCases));
+INSTANTIATE_TEST_SUITE_P(All, OffscreenCanvasTest, ValuesIn(kTestCases));
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index dced481..c09572f 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -298,8 +298,8 @@
           "quota/storage_manager.idl",
           "remoteplayback/remote_playback.idl",
           "scheduler/scheduler.idl",
-          "scheduler/task.idl",
-          "scheduler/task_queue.idl",
+          "scheduler/task_controller.idl",
+          "scheduler/task_signal.idl",
           "screen_enumeration/screen_manager.idl",
           "screen_orientation/screen_orientation.idl",
           "sensor/absolute_orientation_sensor.idl",
@@ -777,7 +777,6 @@
           "quota/storage_estimate.idl",
           "quota/storage_usage_details.idl",
           "scheduler/scheduler_post_task_options.idl",
-          "scheduler/task_queue_post_task_options.idl",
           "sensor/sensor_error_event_init.idl",
           "sensor/sensor_options.idl",
           "sensor/spatial_sensor_options.idl",
diff --git a/third_party/blink/renderer/modules/scheduler/BUILD.gn b/third_party/blink/renderer/modules/scheduler/BUILD.gn
index 8612dd3d..a3dfaca 100644
--- a/third_party/blink/renderer/modules/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/modules/scheduler/BUILD.gn
@@ -10,8 +10,10 @@
     "dom_scheduler.h",
     "dom_task.cc",
     "dom_task.h",
-    "dom_task_queue.cc",
-    "dom_task_queue.h",
+    "dom_task_controller.cc",
+    "dom_task_controller.h",
+    "dom_task_signal.cc",
+    "dom_task_signal.h",
     "window_scheduler.cc",
     "window_scheduler.h",
   ]
diff --git a/third_party/blink/renderer/modules/scheduler/README.md b/third_party/blink/renderer/modules/scheduler/README.md
index a6f30bc..62637f3 100644
--- a/third_party/blink/renderer/modules/scheduler/README.md
+++ b/third_party/blink/renderer/modules/scheduler/README.md
@@ -7,102 +7,9 @@
 
 ## APIs Implemented
 
-`scheduler.postTask()`[[explainer](https://github.com/WICG/main-thread-scheduling/blob/master/PrioritizedPostTask.md), [design doc](https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#heading=h.iw2lczs6xwe6)]
+`scheduler.postTask()`[[explainer](https://github.com/WICG/main-thread-scheduling/blob/master/PrioritizedPostTask.md), [design doc](https://docs.google.com/document/d/1Apz-SD-pOagGeyWxIpgOi0ARNkrCrELhPdm18eeu9tw)]
 
 ## `scheduler.postTask()`
 
 For the full API shape and general design, please refer to the (active)
-[design doc](https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#heading=h.iw2lczs6xwe6).
-
-### API Overview
-
-The `postTask()` API allows developers to schedule tasks with a native scheduler
-(`window.scheduler`), at a specific priority. The API is based on prioritized
-task queues, which is a common scheduling paradigm.
-
-The scheduler maintains a set of global task queues&mdash;one of each
-priority&mdash;which developers can interact with directly via
-`scheduler.getTaskQueue()` or indirectly through `scheduler.postTask()`.
-
-Tasks are created with `scheduler.postTask(foo)` or `taskQueue.postTask(foo)`.
-Initially, tasks are in a "pending" state, and transition to "running",
-"canceled", or "completed" as illustrated in the following diagram:
-
-![Task Lifecycle](images/task_lifecycle.png)
-
-Tasks can be posted with an optional delay, which has similar behavior to
-`setTimeout()`.
-
-Tasks can be moved between task queues with `taskQueue.task(task)`.
-
-### Code Overview and Blink Scheduler Integration
-
-#### DOMScheduler
-
-The `DOMScheduler` is per-document, and observes document lifecycle changes via
-`ContextLifecycleObserver`. When the context is destroyed, the `DOMScheduler` stops
-running tasks.
-
-The `DOMScheduler`'s primary function is to maintain DOMTaskQueues. When the
-`DOMScheduler` is created (when first accessed via `window.scheduler`), it creates
-a set of *global* `DOMTaskQueues`, one of each priority.  Global here means
-document-global, meaning all document script can access these via
-`window.scheduler`. This is opposed to *custom* task queues, which developers
-will be able to create via `new TaskQueue()` (WIP).
-
-
-#### DOMTaskQueues
-
-Each `DOMTaskQueue` wraps a `WebSchedulingTaskQueue`, which is the interface to the
-Blink scheduler.
-
-The `WebSchedulingTaskQueue` is created through the document's `FrameScheduler`,
-and is created with a `WebSchedulingPriority`. The latter is used by the Blink
-scheduler to determine the priority of the underlying
-[base::sequence_manager::TaskQueue](https://cs.chromium.org/chromium/src/base/task/sequence_manager/task_queue.h).
-
-The `DOMTaskQueue` owns the `WebSchedulingTaskQueue`, and its lifetime is tied to
-that of the `Document` associated with the `DOMScheduler`.
-
-The `WebSchedulingTaskQueue` exposes a `TaskRunner`, which the `DOMTaskQueue` uses
-to post tasks to the Blink scheduler.
-
-
-#### DOMTasks
-
-A `DOMTask` is a wrapper for a callback, its arguments, its return value (for
-`task.result`), and a
-[`TaskHandle`](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h?sq=package:chromium&g=0&l=22),
-which allows the `DOMTask` to be canceled with `task.cancel()`.
-
-The Blink scheduler handles the actual task scheduling. A `DOMTask`'s callback is
-scheduled to run by posting it a task to a `TaskRunner`&mdash;just as all tasks
-in Blink are. The `TaskRunner` is obtained through the `WebSchedulingTaskQueue`,
-which is owned by the `DOMTaskQueue` that the `DOMTask` is posted to.
-
-TODO(shaseley): Add a diagram for the relation between all the different task
-queues.
-
-#### Task Run Order
-
-The API uses the Blink scheduler to run its tasks, and each
-`WebSchedulingPriority` maps to a
-[base::sequence_manager::TaskQueue::QueuePriority](https://cs.chromium.org/chromium/src/base/task/sequence_manager/task_queue.h?sq=package:chromium&g=0&l=76).
-
-There is no intervention from the scheduling API to enforce a certain
-order&mdash;the order that tasks run is strictly determined by the Blink
-scheduler based on the `WebSchedulingPriority`.
-
-By default, the Blink scheduler has an anti-starvation mechanism that will
-occasionally prioritize lower priority tasks over higher priority tasks. This
-can be disabled with
-[BlinkSchedulerDisableAntiStarvationForPriorities](https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/scheduler/common/features.h?q=BlinkSchedulerDisa&sq=package:chromium&g=0&l=173),
-which we are relying on to achieve a static ordering (highest to lowest
-priority) of tasks.
-
-#### Detached Documents
-
-The API is modeled after setTimeout and setInterval with regards to detached
-documents. When a document is detached, any queued tasks will be prevented from
-running, and future calls to postTask through a cached DOMScheduler or DOMTaskQueue
-will be no-ops and return null.
+[design doc](https://docs.google.com/document/d/1Apz-SD-pOagGeyWxIpgOi0ARNkrCrELhPdm18eeu9tw).
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
index d324ded..c8327ba 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
+++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.cc
@@ -5,14 +5,28 @@
 #include "third_party/blink/renderer/modules/scheduler/dom_scheduler.h"
 
 #include "base/memory/weak_ptr.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
-#include "third_party/blink/renderer/modules/scheduler/dom_task_queue.h"
+#include "third_party/blink/renderer/modules/scheduler/dom_task.h"
+#include "third_party/blink/renderer/modules/scheduler/dom_task_signal.h"
 #include "third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.h"
 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
+#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h"
 
 namespace blink {
 
+namespace {
+
+static ScriptPromise RejectPromiseImmediately(ScriptState* script_state) {
+  return ScriptPromise::RejectWithDOMException(
+      script_state,
+      MakeGarbageCollected<DOMException>(DOMExceptionCode::kNotSupportedError,
+                                         "Current document is detached"));
+}
+
+}  // namespace
+
 const char DOMScheduler::kSupplementName[] = "DOMScheduler";
 
 DOMScheduler* DOMScheduler::From(Document& document) {
@@ -38,59 +52,73 @@
 }
 
 void DOMScheduler::Trace(Visitor* visitor) {
-  visitor->Trace(global_task_queues_);
-  visitor->Trace(current_task_queue_);
   ScriptWrappable::Trace(visitor);
   ContextLifecycleObserver::Trace(visitor);
   Supplement<Document>::Trace(visitor);
 }
 
-DOMTaskQueue* DOMScheduler::currentTaskQueue() {
-  // The |current_task_queue_| will only be set if the task currently running
-  // was scheduled through window.scheduler. In other cases, e.g. setTimeout,
-  // |current_task_queue_| will be nullptr, in which case we return the default
-  // priority global task queue.
-  if (current_task_queue_)
-    return current_task_queue_;
-  return GetTaskQueue(WebSchedulingPriority::kDefaultPriority);
+// TODO(japhet,shaseley): These are probably useful for metrics/tracing, or
+// for handling incumbent task state.
+void DOMScheduler::OnTaskStarted(DOMTask*) {}
+void DOMScheduler::OnTaskCompleted(DOMTask*) {}
+
+ScriptPromise DOMScheduler::postTask(ScriptState* script_state,
+                                     V8Function* callback_function,
+                                     SchedulerPostTaskOptions* options,
+                                     const HeapVector<ScriptValue>& args) {
+  if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed())
+    return RejectPromiseImmediately(script_state);
+
+  // Always honor the priority and the task signal if given. Therefore:
+  // * If both priority and signal are set, use the signal but choose the
+  //   default task runner for the given priority, so that it is scheduled at
+  //   the given priority.
+  // * If only the signal is set, use the signal and its associated priority.
+  // * If only a priority is set, use the default task runner for that priority,
+  //   and no signal.
+  // * If neither is set, use the default priority task runner and no signal.
+  //
+  // Note that |options->signal()| may be a generic AbortSignal, rather than a
+  // TaskSignal. In that case, use it for abort signalling, but use a default
+  // task runner for priority purposes.
+  base::SingleThreadTaskRunner* task_runner = nullptr;
+  if (options->hasPriority()) {
+    WebSchedulingPriority priority =
+        WebSchedulingPriorityFromString(AtomicString(options->priority()));
+    task_runner = GetTaskRunnerFor(priority);
+  } else if (auto* task_signal = DynamicTo<DOMTaskSignal>(options->signal())) {
+    task_runner = task_signal->GetTaskRunner();
+    if (!task_runner)
+      return RejectPromiseImmediately(script_state);
+  } else {
+    task_runner = GetTaskRunnerFor(WebSchedulingPriority::kDefaultPriority);
+  }
+
+  // TODO(shaseley): We need to figure out the behavior we want for delay. For
+  // now, we use behavior that is very similar to setTimeout: negative delays
+  // are treated as 0, and we use the Blink scheduler's delayed task behavior.
+  // We don't, however, adjust the timeout based on nested calls (yet) or clamp
+  // the value to a minimal delay.
+  base::TimeDelta delay =
+      base::TimeDelta::FromMilliseconds(std::max(0, options->delay()));
+
+  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  MakeGarbageCollected<DOMTask>(this, resolver, callback_function, args,
+                                task_runner, options->signal(), delay);
+  return resolver->Promise();
 }
 
-void DOMScheduler::OnTaskStarted(DOMTaskQueue* task_queue, DOMTask*) {
-  // This is not reentrant; tasks are not nested. This allows us to set
-  // |current_task_queue_| to nullptr when this task completes.
-  DCHECK_EQ(current_task_queue_, nullptr);
-  current_task_queue_ = task_queue;
-}
-
-void DOMScheduler::OnTaskCompleted(DOMTaskQueue* task_queue, DOMTask*) {
-  DCHECK(current_task_queue_);
-  DCHECK_EQ(current_task_queue_, task_queue);
-  current_task_queue_ = nullptr;
-}
-
-DOMTaskQueue* DOMScheduler::getTaskQueue(AtomicString priority) {
-  return GetTaskQueue(WebSchedulingPriorityFromString(priority));
-}
-
-DOMTaskQueue* DOMScheduler::GetTaskQueue(WebSchedulingPriority priority) {
-  if (global_task_queues_.IsEmpty())
-    return nullptr;
-  return global_task_queues_[static_cast<int>(priority)];
-}
-
-DOMTask* DOMScheduler::postTask(V8Function* callback_function,
-                                SchedulerPostTaskOptions* options,
-                                const HeapVector<ScriptValue>& args) {
-  DOMTaskQueue* task_queue = getTaskQueue(AtomicString(options->priority()));
-  if (!task_queue)
-    return nullptr;
-  return task_queue->postTask(callback_function, options, args);
+base::SingleThreadTaskRunner* DOMScheduler::GetTaskRunnerFor(
+    WebSchedulingPriority priority) {
+  DCHECK(!global_task_queues_.IsEmpty());
+  return global_task_queues_[static_cast<int>(priority)]->GetTaskRunner().get();
 }
 
 void DOMScheduler::CreateGlobalTaskQueues(Document* document) {
+  FrameScheduler* scheduler = document->GetScheduler()->ToFrameScheduler();
   for (size_t i = 0; i < kWebSchedulingPriorityCount; i++) {
-    global_task_queues_.push_back(MakeGarbageCollected<DOMTaskQueue>(
-        document, static_cast<WebSchedulingPriority>(i), this));
+    global_task_queues_.push_back(scheduler->CreateWebSchedulingTaskQueue(
+        static_cast<WebSchedulingPriority>(i)));
   }
 }
 
diff --git a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
index 88f8d2bd..f83cd93 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
+++ b/third_party/blink/renderer/modules/scheduler/dom_scheduler.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_SCHEDULER_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_SCHEDULER_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
@@ -17,11 +18,11 @@
 namespace blink {
 
 class DOMTask;
-class DOMTaskQueue;
 class ExecutionContext;
 class SchedulerPostTaskOptions;
 class ScriptValue;
 class V8Function;
+class WebSchedulingTaskQueue;
 
 class MODULES_EXPORT DOMScheduler : public ScriptWrappable,
                                     public ContextLifecycleObserver,
@@ -36,26 +37,20 @@
 
   explicit DOMScheduler(Document*);
 
-  // Returns the DOMTaskQueue of the currently executing DOMTask. If the current
-  // task was not scheduled through the scheduler, this returns the default
-  // priority global task queue.
-  DOMTaskQueue* currentTaskQueue();
-
-  // Returns the DOMTaskQueue for the given |priority| or nullptr if the
-  // underlying context is destroyed, e.g. for detached documents.
-  DOMTaskQueue* getTaskQueue(AtomicString priority);
-
-  // postTask creates and queues a DOMTask in the |global_task_queues_| entry
-  // corresponding to the priority in the SchedulerPostTaskOptions, and returns
-  // the DOMTask. If the underlying context is destroyed, e.g. for detached
-  // documents, this returns nullptr.
-  DOMTask* postTask(V8Function*,
-                    SchedulerPostTaskOptions*,
-                    const HeapVector<ScriptValue>& args);
+  // postTask creates and queues a DOMTask and returns a Promise that will
+  // resolve when it completes. The task will be scheduled in the queue
+  // corresponding to the priority in the SchedulerPostTaskOptions, or in a
+  // queue associated with the given DOMTaskSignal if one is provided. If the
+  // underlying context is destroyed, e.g. for detached documents, this will
+  // return a rejected promise.
+  ScriptPromise postTask(ScriptState*,
+                         V8Function*,
+                         SchedulerPostTaskOptions*,
+                         const HeapVector<ScriptValue>& args);
 
   // Callbacks invoked by DOMTasks when they run.
-  void OnTaskStarted(DOMTaskQueue*, DOMTask*);
-  void OnTaskCompleted(DOMTaskQueue*, DOMTask*);
+  void OnTaskStarted(DOMTask*);
+  void OnTaskCompleted(DOMTask*);
 
   void ContextDestroyed(ExecutionContext*) override;
 
@@ -66,15 +61,12 @@
       static_cast<size_t>(WebSchedulingPriority::kLastPriority) + 1;
 
   void CreateGlobalTaskQueues(Document*);
-  DOMTaskQueue* GetTaskQueue(WebSchedulingPriority);
+  base::SingleThreadTaskRunner* GetTaskRunnerFor(WebSchedulingPriority);
 
   // |global_task_queues_| is initialized with one entry per priority, indexed
   // by priority. This will be empty when the document is detached.
-  HeapVector<Member<DOMTaskQueue>, kWebSchedulingPriorityCount>
+  Vector<std::unique_ptr<WebSchedulingTaskQueue>, kWebSchedulingPriorityCount>
       global_task_queues_;
-  // The DOMTaskQueue associated with the currently running DOMTask, or nullptr
-  // if the current task was not scheduled through this DOMScheduler.
-  Member<DOMTaskQueue> current_task_queue_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task.cc b/third_party/blink/renderer/modules/scheduler/dom_task.cc
index 038b65c..00531c8 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_task.cc
+++ b/third_party/blink/renderer/modules/scheduler/dom_task.cc
@@ -11,128 +11,56 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_function.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/modules/scheduler/dom_scheduler.h"
-#include "third_party/blink/renderer/modules/scheduler/dom_task_queue.h"
+#include "third_party/blink/renderer/modules/scheduler/dom_task_signal.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 
 namespace blink {
 
-DOMTask::DOMTask(DOMTaskQueue* task_queue,
-                 ExecutionContext* context,
+DOMTask::DOMTask(DOMScheduler* scheduler,
+                 ScriptPromiseResolver* resolver,
                  V8Function* callback,
                  const HeapVector<ScriptValue>& args,
+                 base::SingleThreadTaskRunner* task_runner,
+                 AbortSignal* signal,
                  base::TimeDelta delay)
-    : ContextLifecycleObserver(context),
-      status_(Status::kPending),
-      task_queue_(task_queue),
+    : scheduler_(scheduler),
       callback_(callback),
       arguments_(args),
-      delay_(delay),
-      queue_time_(delay.is_zero() ? base::TimeTicks()
-                                  : base::TimeTicks::Now()) {
-  DCHECK(task_queue_);
+      resolver_(resolver) {
   DCHECK(callback_);
+  DCHECK(task_runner);
+  if (signal) {
+    if (signal->aborted()) {
+      Abort();
+      return;
+    }
 
-  Schedule(delay_);
+    signal->AddAlgorithm(WTF::Bind(&DOMTask::Abort, WrapWeakPersistent(this)));
+  }
+
+  task_handle_ = PostDelayedCancellableTask(
+      *task_runner, FROM_HERE,
+      WTF::Bind(&DOMTask::Invoke, WrapPersistent(this)), delay);
 }
 
 void DOMTask::Trace(Visitor* visitor) {
-  visitor->Trace(task_queue_);
+  visitor->Trace(scheduler_);
   visitor->Trace(callback_);
   visitor->Trace(arguments_);
-  visitor->Trace(result_value_);
-  visitor->Trace(result_promise_);
-  visitor->Trace(exception_);
-  ScriptWrappable::Trace(visitor);
-  ContextLifecycleObserver::Trace(visitor);
-}
-
-void DOMTask::ContextDestroyed(ExecutionContext* context) {
-  if (status_ != Status::kPending)
-    return;
-  CancelPendingTask();
-}
-
-AtomicString DOMTask::priority() const {
-  DCHECK(task_queue_);
-  return task_queue_->priority();
-}
-
-AtomicString DOMTask::status() const {
-  return TaskStatusToString(status_);
-}
-
-void DOMTask::cancel(ScriptState* script_state) {
-  if (status_ != Status::kPending)
-    return;
-  CancelPendingTask();
-  SetTaskStatus(Status::kCanceled);
-  LazilyResolveOrRejectPromiseIfReady(script_state);
-}
-
-ScriptPromise DOMTask::result(ScriptState* script_state) {
-  if (!result_promise_) {
-    result_promise_ = MakeGarbageCollected<TaskResultPromise>(
-        ExecutionContext::From(script_state), this,
-        TaskResultPromise::kFinished);
-    LazilyResolveOrRejectPromiseIfReady(script_state);
-  }
-  return result_promise_->Promise(script_state->World());
-}
-
-void DOMTask::MoveTo(DOMTaskQueue* task_queue) {
-  if (status_ != Status::kPending || task_queue == task_queue_)
-    return;
-
-  CancelPendingTask();
-  task_queue_ = task_queue;
-
-  base::TimeDelta delay = base::TimeDelta();
-  if (!delay_.is_zero()) {
-    DCHECK(!queue_time_.is_null());
-    base::TimeTicks now = base::TimeTicks::Now();
-    if (queue_time_ + delay_ > now) {
-      delay = now - queue_time_;
-    }
-  }
-
-  Schedule(delay);
-}
-
-void DOMTask::Schedule(base::TimeDelta delay) {
-  DCHECK_EQ(status_, Status::kPending);
-  DCHECK(!task_handle_.IsActive());
-  DCHECK_GE(delay, base::TimeDelta());
-
-  task_handle_ = PostDelayedCancellableTask(
-      *task_queue_->GetTaskRunner(), FROM_HERE,
-      WTF::Bind(&DOMTask::Invoke, WrapPersistent(this)), delay_);
-}
-
-void DOMTask::CancelPendingTask() {
-  DCHECK_EQ(status_, Status::kPending);
-  DCHECK(task_handle_.IsActive());
-
-  task_handle_.Cancel();
+  visitor->Trace(resolver_);
 }
 
 void DOMTask::Invoke() {
-  DCHECK_EQ(status_, Status::kPending);
   DCHECK(callback_);
-  DCHECK(GetExecutionContext());
-  DCHECK(!GetExecutionContext()->IsContextDestroyed());
 
   ScriptState* script_state =
       callback_->CallbackRelevantScriptStateOrReportError("DOMTask", "Invoke");
   if (!script_state || !script_state->ContextIsValid())
     return;
 
-  SetTaskStatus(Status::kRunning);
-  task_queue_->GetScheduler()->OnTaskStarted(task_queue_, this);
+  scheduler_->OnTaskStarted(this);
   InvokeInternal(script_state);
-  SetTaskStatus(Status::kCompleted);
-  task_queue_->GetScheduler()->OnTaskCompleted(task_queue_, this);
-  LazilyResolveOrRejectPromiseIfReady(script_state);
+  scheduler_->OnTaskCompleted(this);
   callback_.Release();
 }
 
@@ -142,105 +70,16 @@
   try_catch.SetVerbose(true);
 
   ScriptValue result;
-  if (!callback_->Invoke(nullptr, arguments_).To(&result)) {
-    if (try_catch.HasCaught()) {
-      exception_.Set(script_state->GetIsolate(), try_catch.Exception());
-    }
-    return;
-  }
-  result_value_.Set(script_state->GetIsolate(), result.V8Value());
+  if (callback_->Invoke(nullptr, arguments_).To(&result))
+    resolver_->Resolve(result.V8Value());
+  else if (try_catch.HasCaught())
+    resolver_->Reject(try_catch.Exception());
 }
 
-void DOMTask::LazilyResolveOrRejectPromiseIfReady(ScriptState* script_state) {
-  if (!result_promise_)
-    return;
-
-  if (!script_state->ContextIsValid())
-    return;
-  ScriptState::Scope scope(script_state);
-
-  if (!exception_.IsEmpty()) {
-    result_promise_->Reject(ScriptValue::From(
-        script_state, exception_.NewLocal(script_state->GetIsolate())));
-    return;
-  }
-
-  // TODO(shaseley): Once we have continuation built, consider resolving this
-  // promise async with continuation timing.
-  if (status_ == Status::kCompleted) {
-    result_promise_->Resolve(ScriptValue::From(
-        script_state, result_value_.NewLocal(script_state->GetIsolate())));
-    return;
-  }
-
-  if (status_ == Status::kCanceled) {
-    result_promise_->Reject(ScriptValue::From(
-        script_state,
-        MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError)));
-    return;
-  }
-}
-
-void DOMTask::SetTaskStatus(Status status) {
-  DCHECK(IsValidStatusChange(status_, status))
-      << "Cannot transition from DOMTask::Status "
-      << TaskStatusToString(status_) << " to " << TaskStatusToString(status);
-  status_ = status;
-}
-
-// static
-AtomicString DOMTask::TaskStatusToString(Status status) {
-  DEFINE_STATIC_LOCAL(const AtomicString, pending, ("pending"));
-  DEFINE_STATIC_LOCAL(const AtomicString, running, ("running"));
-  DEFINE_STATIC_LOCAL(const AtomicString, canceled, ("canceled"));
-  DEFINE_STATIC_LOCAL(const AtomicString, completed, ("completed"));
-
-  switch (status) {
-    case Status::kPending:
-      return pending;
-    case Status::kRunning:
-      return running;
-    case Status::kCanceled:
-      return canceled;
-    case Status::kCompleted:
-      return completed;
-  }
-
-  NOTREACHED();
-  return g_empty_atom;
-}
-
-// static
-bool DOMTask::IsValidStatusChange(Status from, Status to) {
-  // Note: Self transitions are not valid.
-  switch (from) {
-    case Status::kPending: {
-      switch (to) {
-        // The task is invoked by the scheduler.
-        case Status::kRunning:
-        // task.cancel().
-        case Status::kCanceled:
-          return true;
-        default:
-          return false;
-      }
-    }
-    case Status::kRunning: {
-      switch (to) {
-        // The task completes with or without exception.
-        case Status::kCompleted:
-          return true;
-        default:
-          return false;
-      }
-    }
-    // Canceled and Completed are both end states.
-    case Status::kCanceled:
-    case Status::kCompleted:
-      return false;
-  }
-  NOTREACHED();
-  return false;
+void DOMTask::Abort() {
+  task_handle_.Cancel();
+  resolver_->Reject(
+      MakeGarbageCollected<DOMException>(DOMExceptionCode::kAbortError));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task.h b/third_party/blink/renderer/modules/scheduler/dom_task.h
index 0fc2c709..00b7ed66 100644
--- a/third_party/blink/renderer/modules/scheduler/dom_task.h
+++ b/third_party/blink/renderer/modules/scheduler/dom_task.h
@@ -6,107 +6,45 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_H_
 
 #include "base/time/time.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_property.h"
-#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/platform/heap/handle.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
-#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
 
 namespace blink {
-
-class DOMTaskQueue;
+class AbortSignal;
+class DOMScheduler;
 class ScriptState;
 class ScriptValue;
 class V8Function;
 
-class MODULES_EXPORT DOMTask : public ScriptWrappable,
-                               ContextLifecycleObserver {
-  DEFINE_WRAPPERTYPEINFO();
-  USING_GARBAGE_COLLECTED_MIXIN(DOMTask);
-
+// DOMTask represents a task scheduled via the web scheduling API. It will
+// keep itself alive until DOMTask::Invoke is called, which may be after the
+// callback's v8 context is invalid, in which case, the task will not be run.
+class DOMTask final : public GarbageCollected<DOMTask> {
  public:
-  // Creating a task also causes the DOMTask to be scheduled.
-  DOMTask(DOMTaskQueue*,
-          ExecutionContext*,
+  DOMTask(DOMScheduler*,
+          ScriptPromiseResolver*,
           V8Function*,
           const HeapVector<ScriptValue>& args,
+          base::SingleThreadTaskRunner*,
+          AbortSignal*,
           base::TimeDelta delay);
 
-  // Task IDL Interface.
-  AtomicString priority() const;
-  AtomicString status() const;
-  void cancel(ScriptState*);
-  ScriptPromise result(ScriptState*);
-
-  // Move this DOMTask to a different DOMTaskQueue. If the task is already
-  // enqueued in the given task queue, this method has no effect.
-  void MoveTo(DOMTaskQueue*);
-
-  void ContextDestroyed(ExecutionContext*) override;
-
-  void Trace(Visitor*) override;
+  virtual void Trace(Visitor*);
 
  private:
-  // The Status associated with the task lifecycle. Tasks are "pending" before
-  // they run, transition to "running" during execution, and end in "completed"
-  // when execution completes.
-  enum class Status {
-    kPending,
-    kRunning,
-    kCanceled,
-    kCompleted,
-  };
-
-  // Cancel a pending task. This will update the |status_| to kCanceled, and the
-  // task will no longer be runnable.
-  void CancelPendingTask();
-
-  // Set |status_| and validate the state transition.
-  void SetTaskStatus(Status);
-
-  // Schedule the task. If |delay| is 0, the task will be queued immediately,
-  // otherwise it's queued after |delay|.
-  void Schedule(base::TimeDelta delay);
-
   // Entry point for running this DOMTask's |callback_|.
   void Invoke();
   // Internal step of Invoke that handles invoking the callback, including
   // catching any errors and retrieving the result.
   void InvokeInternal(ScriptState*);
+  void Abort();
 
-  // Resolve |result_promise_| if both (a) the task has finished running (with
-  // or without an error) or has been canceled, and (b) task.result has been
-  // accessed and returned to userland. If invoked when either of these
-  // conditions are not met, this is a no-op.
-  void LazilyResolveOrRejectPromiseIfReady(ScriptState*);
-
-  static bool IsValidStatusChange(Status from, Status to);
-  static AtomicString TaskStatusToString(Status);
-
-  Status status_;
+  Member<DOMScheduler> scheduler_;
   TaskHandle task_handle_;
-  Member<DOMTaskQueue> task_queue_;
   Member<V8Function> callback_;
   HeapVector<ScriptValue> arguments_;
-  const base::TimeDelta delay_;
-  // Only set if |delay_| > 0 since Now() can be somewhat expensive. This
-  // optimizes the case where there is no delay, which we expect to be the
-  // common case.
-  const base::TimeTicks queue_time_;
-
-  using TaskResultPromise =
-      ScriptPromiseProperty<Member<DOMTask>, ScriptValue, ScriptValue>;
-  // Lazily initialized when the task.result property is accessed to avoid the
-  // overhead of unnecessarily creating promises for every task if they aren't
-  // going to be used.
-  Member<TaskResultPromise> result_promise_;
-
-  TraceWrapperV8Reference<v8::Value> result_value_;
-  TraceWrapperV8Reference<v8::Value> exception_;
+  Member<ScriptPromiseResolver> resolver_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_controller.cc b/third_party/blink/renderer/modules/scheduler/dom_task_controller.cc
new file mode 100644
index 0000000..71af9a9
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/dom_task_controller.cc
@@ -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.
+
+#include "third_party/blink/renderer/modules/scheduler/dom_task_controller.h"
+
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/modules/scheduler/dom_task_signal.h"
+
+namespace blink {
+
+// static
+DOMTaskController* DOMTaskController::Create(Document& document,
+                                             const AtomicString& priority) {
+  return MakeGarbageCollected<DOMTaskController>(
+      document, WebSchedulingPriorityFromString(priority));
+}
+
+DOMTaskController::DOMTaskController(Document& document,
+                                     WebSchedulingPriority priority)
+    : AbortController(
+          MakeGarbageCollected<DOMTaskSignal>(&document, priority)) {
+  DCHECK(!document.IsContextDestroyed());
+}
+
+void DOMTaskController::setPriority(const AtomicString& priority) {
+  GetTaskSignal()->SignalPriorityChange(
+      WebSchedulingPriorityFromString(priority));
+}
+
+DOMTaskSignal* DOMTaskController::GetTaskSignal() const {
+  return static_cast<DOMTaskSignal*>(signal());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_controller.h b/third_party/blink/renderer/modules/scheduler/dom_task_controller.h
new file mode 100644
index 0000000..f7911e4
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/dom_task_controller.h
@@ -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.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_CONTROLLER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_CONTROLLER_H_
+
+#include <memory>
+
+#include "third_party/blink/renderer/core/dom/abort_controller.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
+
+namespace blink {
+class DOMTaskSignal;
+class Document;
+
+class MODULES_EXPORT DOMTaskController final : public AbortController {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static DOMTaskController* Create(Document&, const AtomicString& priority);
+  DOMTaskController(Document&, WebSchedulingPriority);
+
+  void setPriority(const AtomicString& priority);
+
+ private:
+  DOMTaskSignal* GetTaskSignal() const;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_CONTROLLER_H_
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_queue.cc b/third_party/blink/renderer/modules/scheduler/dom_task_queue.cc
deleted file mode 100644
index 003733e..0000000
--- a/third_party/blink/renderer/modules/scheduler/dom_task_queue.cc
+++ /dev/null
@@ -1,80 +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 "third_party/blink/renderer/modules/scheduler/dom_task_queue.h"
-
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/time/time.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_function.h"
-#include "third_party/blink/renderer/modules/scheduler/dom_scheduler.h"
-#include "third_party/blink/renderer/modules/scheduler/dom_task.h"
-#include "third_party/blink/renderer/modules/scheduler/task_queue_post_task_options.h"
-#include "third_party/blink/renderer/platform/bindings/exception_code.h"
-#include "third_party/blink/renderer/platform/bindings/exception_state.h"
-#include "third_party/blink/renderer/platform/heap/persistent.h"
-#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
-#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h"
-#include "third_party/blink/renderer/platform/wtf/functional.h"
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
-
-namespace blink {
-
-DOMTaskQueue::DOMTaskQueue(Document* document,
-                           WebSchedulingPriority priority,
-                           DOMScheduler* scheduler)
-    : ContextLifecycleObserver(document),
-      priority_(priority),
-      web_scheduling_task_queue_(document->GetScheduler()
-                                     ->ToFrameScheduler()
-                                     ->CreateWebSchedulingTaskQueue(priority)),
-      task_runner_(web_scheduling_task_queue_->GetTaskRunner()),
-      scheduler_(scheduler) {
-  DCHECK(!document->IsContextDestroyed());
-}
-
-void DOMTaskQueue::Trace(Visitor* visitor) {
-  visitor->Trace(scheduler_);
-  ScriptWrappable::Trace(visitor);
-  ContextLifecycleObserver::Trace(visitor);
-}
-
-void DOMTaskQueue::ContextDestroyed(ExecutionContext* context) {
-  web_scheduling_task_queue_.reset(nullptr);
-}
-
-AtomicString DOMTaskQueue::priority() const {
-  return WebSchedulingPriorityToString(priority_);
-}
-
-DOMTask* DOMTaskQueue::postTask(V8Function* function,
-                                TaskQueuePostTaskOptions* options,
-                                const HeapVector<ScriptValue>& args) {
-  if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed())
-    return nullptr;
-
-  // TODO(shaseley): We need to figure out the behavior we want for delay. For
-  // now, we use behavior that is very similar to setTimeout: negative delays
-  // are treated as 0, and we use the Blink scheduler's delayed task behavior.
-  // We don't, however, adjust the timeout based on nested calls (yet) or clamp
-  // the value to a minimal delay.
-  base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
-      options->delay() > 0 ? options->delay() : 0);
-
-  // For global task queues, we don't need to track the task objects separately;
-  // tracking is handled by the |web_scheduling_task_queue_|.
-  return MakeGarbageCollected<DOMTask>(this, GetExecutionContext(), function,
-                                       args, delay);
-}
-
-void DOMTaskQueue::take(DOMTask* task) {
-  if (!GetExecutionContext() || GetExecutionContext()->IsContextDestroyed())
-    return;
-  task->MoveTo(this);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_queue.h b/third_party/blink/renderer/modules/scheduler/dom_task_queue.h
deleted file mode 100644
index 417fa14..0000000
--- a/third_party/blink/renderer/modules/scheduler/dom_task_queue.h
+++ /dev/null
@@ -1,86 +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 THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_QUEUE_H_
-#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_QUEUE_H_
-
-#include <memory>
-
-#include "base/memory/scoped_refptr.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
-#include "third_party/blink/renderer/modules/modules_export.h"
-#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
-#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}  // namespace base
-
-namespace blink {
-
-class DOMScheduler;
-class DOMTask;
-class Document;
-class ExecutionContext;
-class ScriptValue;
-class TaskQueuePostTaskOptions;
-class V8Function;
-class WebSchedulingTaskQueue;
-
-namespace scheduler {
-class WebSchedulingTaskQueue;
-}  // namespace scheduler
-
-class MODULES_EXPORT DOMTaskQueue : public ScriptWrappable,
-                                    ContextLifecycleObserver {
-  DEFINE_WRAPPERTYPEINFO();
-  USING_GARBAGE_COLLECTED_MIXIN(DOMTaskQueue);
-
- public:
-  DOMTaskQueue(Document*, WebSchedulingPriority, DOMScheduler*);
-
-  // Returns the priority of the DOMTaskQueue.
-  AtomicString priority() const;
-
-  // postTask creates and queues a DOMTask in this DOMTaskQueue and returns the
-  // DOMTask. If the underlying context is destroyed, e.g. for detached
-  // documents, this returns nullptr.
-  DOMTask* postTask(V8Function*,
-                    TaskQueuePostTaskOptions*,
-                    const HeapVector<ScriptValue>& args);
-
-  // Move the task from its current DOMTaskQueue to this one. For pending
-  // non-delayed tasks, the task is enqueued at the end of this DOMTaskQueue.
-  // For delayed tasks, the delay is adjusted before reposting it.
-  void take(DOMTask*);
-
-  const scoped_refptr<base::SingleThreadTaskRunner>& GetTaskRunner() {
-    return task_runner_;
-  }
-
-  DOMScheduler* GetScheduler() { return scheduler_.Get(); }
-
-  void ContextDestroyed(ExecutionContext*) override;
-
-  void Trace(Visitor*) override;
-
- private:
-  void RunTaskCallback(DOMTask*);
-  void ScheduleTask(DOMTask*, base::TimeDelta delay);
-
-  const WebSchedulingPriority priority_;
-  // This is destroyed when the Context is destroyed, and we rely on this
-  // happening before the underlying FrameScheduler is destroyed.
-  std::unique_ptr<WebSchedulingTaskQueue> web_scheduling_task_queue_;
-  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-  Member<DOMScheduler> scheduler_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_QUEUE_H_
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_signal.cc b/third_party/blink/renderer/modules/scheduler/dom_task_signal.cc
new file mode 100644
index 0000000..a350ac8
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/dom_task_signal.cc
@@ -0,0 +1,59 @@
+// 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/modules/scheduler/dom_task_signal.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "third_party/blink/renderer/core/dom/document.h"
+#include "third_party/blink/renderer/modules/scheduler/dom_scheduler.h"
+#include "third_party/blink/renderer/platform/heap/persistent.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h"
+#include "third_party/blink/renderer/platform/wtf/functional.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+DOMTaskSignal::DOMTaskSignal(Document* document, WebSchedulingPriority priority)
+    : AbortSignal(document),
+      ContextLifecycleObserver(document),
+      priority_(priority),
+      web_scheduling_task_queue_(document->GetScheduler()
+                                     ->ToFrameScheduler()
+                                     ->CreateWebSchedulingTaskQueue(priority)) {
+}
+
+DOMTaskSignal::~DOMTaskSignal() = default;
+
+AtomicString DOMTaskSignal::priority() {
+  return WebSchedulingPriorityToString(priority_);
+}
+
+void DOMTaskSignal::ContextDestroyed(ExecutionContext*) {
+  web_scheduling_task_queue_.reset();
+}
+
+void DOMTaskSignal::SignalPriorityChange(WebSchedulingPriority priority) {
+  if (priority_ == priority)
+    return;
+  priority_ = priority;
+  if (web_scheduling_task_queue_)
+    web_scheduling_task_queue_->SetPriority(priority);
+}
+
+base::SingleThreadTaskRunner* DOMTaskSignal::GetTaskRunner() {
+  return web_scheduling_task_queue_
+             ? web_scheduling_task_queue_->GetTaskRunner().get()
+             : nullptr;
+}
+
+void DOMTaskSignal::Trace(Visitor* visitor) {
+  AbortSignal::Trace(visitor);
+  ContextLifecycleObserver::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/scheduler/dom_task_signal.h b/third_party/blink/renderer/modules/scheduler/dom_task_signal.h
new file mode 100644
index 0000000..8311a09
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/dom_task_signal.h
@@ -0,0 +1,64 @@
+// 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_MODULES_SCHEDULER_DOM_TASK_SIGNAL_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_SIGNAL_H_
+
+#include "third_party/blink/renderer/core/dom/abort_signal.h"
+#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
+#include "third_party/blink/renderer/modules/modules_export.h"
+#include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
+#include "third_party/blink/renderer/platform/wtf/casting.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace blink {
+class Document;
+class ExecutionContext;
+class WebSchedulingTaskQueue;
+
+class MODULES_EXPORT DOMTaskSignal final : public AbortSignal,
+                                           public ContextLifecycleObserver {
+  DEFINE_WRAPPERTYPEINFO();
+  USING_GARBAGE_COLLECTED_MIXIN(DOMTaskSignal);
+
+ public:
+  explicit DOMTaskSignal(Document*, WebSchedulingPriority);
+  ~DOMTaskSignal() override;
+
+  // task_signal.idl
+  AtomicString priority();
+
+  void ContextDestroyed(ExecutionContext*) override;
+
+  void SignalPriorityChange(WebSchedulingPriority);
+  base::SingleThreadTaskRunner* GetTaskRunner();
+
+  bool IsTaskSignal() const override { return true; }
+
+  void Trace(Visitor*) override;
+
+ private:
+  WebSchedulingPriority priority_;
+
+  // The lifetime of |web_scheduling_task_queue_| matches DOMTaskSignal, but
+  // |web_scheduling_task_queue_| only holds a weak reference to the underlying
+  // MainThreadTaskQueue. That weak reference will be invalidated on frame
+  // detach, so a DOMTaskSignal will fail to schedule tasks in a detached
+  // frame.
+  std::unique_ptr<WebSchedulingTaskQueue> web_scheduling_task_queue_;
+};
+
+template <>
+struct DowncastTraits<DOMTaskSignal> {
+  static bool AllowFrom(const AbortSignal& signal) {
+    return signal.IsTaskSignal();
+  }
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_SCHEDULER_DOM_TASK_SIGNAL_H_
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler.idl b/third_party/blink/renderer/modules/scheduler/scheduler.idl
index cf184c1..5c2f4b7a0 100644
--- a/third_party/blink/renderer/modules/scheduler/scheduler.idl
+++ b/third_party/blink/renderer/modules/scheduler/scheduler.idl
@@ -3,12 +3,10 @@
 // found in the LICENSE file.
 
 // Experimental Scheduling API Proposal:
-// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+// https://docs.google.com/document/d/1Apz-SD-pOagGeyWxIpgOi0ARNkrCrELhPdm18eeu9tw
 [
     ImplementedAs=DOMScheduler,
     RuntimeEnabled=WebScheduler
 ] interface Scheduler {
-    readonly attribute TaskQueue currentTaskQueue;
-    TaskQueue getTaskQueue(TaskQueuePriority priority);
-    Task postTask(Function callback, optional SchedulerPostTaskOptions options, any... arguments);
+    [CallWith=ScriptState] Promise<any> postTask(Function callback, optional SchedulerPostTaskOptions options, any... arguments);
 };
diff --git a/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl
index d0a92b55..854de5b 100644
--- a/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl
+++ b/third_party/blink/renderer/modules/scheduler/scheduler_post_task_options.idl
@@ -4,6 +4,16 @@
 
 // Experimental Scheduling API Proposal:
 // https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
-dictionary SchedulerPostTaskOptions : TaskQueuePostTaskOptions {
-    TaskQueuePriority priority = "default";
+enum TaskPriority {
+    "immediate",
+    "high",
+    "default",
+    "low",
+    "idle"
+};
+
+dictionary SchedulerPostTaskOptions {
+    AbortSignal? signal;
+    TaskPriority? priority;
+    long delay = 0;
 };
diff --git a/third_party/blink/renderer/modules/scheduler/task.idl b/third_party/blink/renderer/modules/scheduler/task.idl
deleted file mode 100644
index 80e1ccf..0000000
--- a/third_party/blink/renderer/modules/scheduler/task.idl
+++ /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.
-
-// Experimental Scheduling API Proposal:
-// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
-enum TaskStatus { "pending", "running", "canceled", "completed" };
-
-[
-    ImplementedAs=DOMTask,
-    RuntimeEnabled=WebScheduler
-] interface Task {
-    readonly attribute TaskStatus status;
-    readonly attribute TaskQueuePriority priority;
-    [CallWith=ScriptState] readonly attribute Promise<any> result;
-    [CallWith=ScriptState] void cancel();
-};
diff --git a/third_party/blink/renderer/modules/scheduler/task_controller.idl b/third_party/blink/renderer/modules/scheduler/task_controller.idl
new file mode 100644
index 0000000..bfa247c2
--- /dev/null
+++ b/third_party/blink/renderer/modules/scheduler/task_controller.idl
@@ -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.
+
+// Experimental Scheduling API Proposal:
+// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
+
+[
+    Constructor(optional TaskPriority priority = "default"),
+    ConstructorCallWith=Document,
+    ImplementedAs=DOMTaskController,
+    RuntimeEnabled=WebScheduler
+] interface TaskController : AbortController {
+    void setPriority(TaskPriority priority);
+};
+
diff --git a/third_party/blink/renderer/modules/scheduler/task_queue.idl b/third_party/blink/renderer/modules/scheduler/task_queue.idl
deleted file mode 100644
index 8c6e06d..0000000
--- a/third_party/blink/renderer/modules/scheduler/task_queue.idl
+++ /dev/null
@@ -1,22 +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.
-
-// Experimental Scheduling API Proposal:
-// https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
-enum TaskQueuePriority {
-    "immediate",
-    "high",
-    "default",
-    "low",
-    "idle"
-};
-
-[
-    ImplementedAs=DOMTaskQueue,
-    RuntimeEnabled=WebScheduler
-] interface TaskQueue {
-    readonly attribute TaskQueuePriority priority;
-    Task postTask(Function callback, optional TaskQueuePostTaskOptions options, any... arguments);
-    void take(Task task);
-};
diff --git a/third_party/blink/renderer/modules/scheduler/task_queue_post_task_options.idl b/third_party/blink/renderer/modules/scheduler/task_signal.idl
similarity index 66%
rename from third_party/blink/renderer/modules/scheduler/task_queue_post_task_options.idl
rename to third_party/blink/renderer/modules/scheduler/task_signal.idl
index 75661afa..ff62edc 100644
--- a/third_party/blink/renderer/modules/scheduler/task_queue_post_task_options.idl
+++ b/third_party/blink/renderer/modules/scheduler/task_signal.idl
@@ -4,6 +4,10 @@
 
 // Experimental Scheduling API Proposal:
 // https://docs.google.com/document/d/1xU7HyNsEsbXhTgt0ZnXDbeSXm5-m5FzkLJAT6LTizEI/edit#
-dictionary TaskQueuePostTaskOptions {
-    long delay = 0;
+
+[
+    ImplementedAs=DOMTaskSignal,
+    RuntimeEnabled=WebScheduler
+] interface TaskSignal : AbortSignal {
+  readonly attribute TaskPriority priority;
 };
diff --git a/third_party/blink/renderer/platform/graphics/image_data_buffer.cc b/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
index 8e1255c..fe622fb 100644
--- a/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
+++ b/third_party/blink/renderer/platform/graphics/image_data_buffer.cc
@@ -34,6 +34,7 @@
 
 #include <memory>
 
+#include "base/compiler_specific.h"
 #include "base/memory/ptr_util.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
@@ -78,10 +79,12 @@
       pixmap_.reset();
       return;
     }
+    MSAN_CHECK_MEM_IS_INITIALIZED(pixmap_.addr(), pixmap_.computeByteSize());
     retained_image_ = SkImage::MakeRasterData(info, std::move(data), rowBytes);
   } else {
     if (!retained_image_->peekPixels(&pixmap_))
       return;
+    MSAN_CHECK_MEM_IS_INITIALIZED(pixmap_.addr(), pixmap_.computeByteSize());
   }
   is_valid_ = true;
   size_ = IntSize(image->width(), image->height());
@@ -161,7 +164,9 @@
     if (!pixmap.colorSpace()->isSRGB()) {
       skia_image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr);
       skia_image = skia_image->makeColorSpace(SkColorSpace::MakeSRGB());
-      skia_image->peekPixels(&pixmap);
+      if (!skia_image->peekPixels(&pixmap))
+        return "data:,";
+      MSAN_CHECK_MEM_IS_INITIALIZED(pixmap.addr(), pixmap.computeByteSize());
     }
     pixmap.setColorSpace(nullptr);
   }
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index f8430faa..f981d74 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -925,6 +925,7 @@
     {
       name: "MathMLCore",
       status:"test",
+      depends_on: ["LayoutNG"],
     },
     {
       name:"MeasureMemory",
diff --git a/third_party/blink/renderer/platform/scheduler/common/features.h b/third_party/blink/renderer/platform/scheduler/common/features.h
index 73eab66..e288d3e 100644
--- a/third_party/blink/renderer/platform/scheduler/common/features.h
+++ b/third_party/blink/renderer/platform/scheduler/common/features.h
@@ -103,6 +103,16 @@
     &kVeryHighPriorityForCompositingBudget, "CompositorBudgetRecoveryRate",
     0.25};
 
+// This feature functions as an experiment parameter for the
+// VeryHighPriorityForCompositing alternating, delay, and budget experiments.
+// When enabled, it does nothing unless one of these experiments is also
+// enabled. If one of these experiments is enabled it will change the behavior
+// of that experiment such that the stop signal for prioritzation of the
+// compositor is a BeginMainFrame task instead of any compositor task.
+const base::Feature kPrioritizeCompositingUntilBeginMainFrame{
+    "BlinkSchedulerPrioritizeCompositingUntilBeginMainFrame",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 // LOAD PRIORITY EXPERIMENT CONTROLS
 
 // Enables setting the priority of background (with no audio) pages'
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.cc b/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.cc
index fa9a882..8564cbc 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.cc
@@ -16,7 +16,11 @@
     : scheduler_(scheduler),
       experiment_(GetExperimentFromFeatureList()),
       prioritize_compositing_after_delay_length_(
-          base::TimeDelta::FromMilliseconds(kCompositingDelayLength.Get())) {
+          base::TimeDelta::FromMilliseconds(kCompositingDelayLength.Get())),
+      stop_signal_(base::FeatureList::IsEnabled(
+                       kPrioritizeCompositingUntilBeginMainFrame)
+                       ? StopSignalType::kBeginMainFrameTask
+                       : StopSignalType::kAnyCompositorTask) {
   do_prioritize_compositing_after_delay_callback_.Reset(base::BindRepeating(
       &CompositorPriorityExperiments::DoPrioritizeCompositingAfterDelay,
       base::Unretained(this)));
@@ -94,6 +98,10 @@
   budget_pool_controller_.reset();
 }
 
+void CompositorPriorityExperiments::OnWillBeginMainFrame() {
+  will_begin_main_frame_ = true;
+}
+
 void CompositorPriorityExperiments::PostPrioritizeCompositingAfterDelayTask() {
   DCHECK_EQ(experiment_, Experiment::kVeryHighPriorityForCompositingAfterDelay);
 
@@ -109,6 +117,16 @@
   if (!queue)
     return;
 
+  bool have_seen_stop_signal = false;
+  if (queue->queue_type() == MainThreadTaskQueue::QueueType::kCompositor) {
+    if (stop_signal_ == StopSignalType::kAnyCompositorTask) {
+      have_seen_stop_signal = true;
+    } else if (will_begin_main_frame_) {
+      have_seen_stop_signal = true;
+      will_begin_main_frame_ = false;
+    }
+  }
+
   switch (experiment_) {
     case Experiment::kVeryHighPriorityForCompositingAlways:
       return;
@@ -119,9 +137,8 @@
       // compositor if another task has run regardless of its priority. This
       // prevents starving the compositor while allowing other work to run
       // in-between.
-      if (queue->queue_type() == MainThreadTaskQueue::QueueType::kCompositor &&
-          alternating_compositor_priority_ ==
-              QueuePriority::kVeryHighPriority) {
+      if (have_seen_stop_signal && alternating_compositor_priority_ ==
+                                       QueuePriority::kVeryHighPriority) {
         alternating_compositor_priority_ = QueuePriority::kNormalPriority;
         scheduler_->OnCompositorPriorityExperimentUpdateCompositorPriority();
       } else if (alternating_compositor_priority_ ==
@@ -131,7 +148,7 @@
       }
       return;
     case Experiment::kVeryHighPriorityForCompositingAfterDelay:
-      if (queue->queue_type() == MainThreadTaskQueue::QueueType::kCompositor) {
+      if (have_seen_stop_signal) {
         delay_compositor_priority_ = QueuePriority::kNormalPriority;
         do_prioritize_compositing_after_delay_callback_.Cancel();
         PostPrioritizeCompositingAfterDelayTask();
@@ -141,7 +158,8 @@
       }
       return;
     case Experiment::kVeryHighPriorityForCompositingBudget:
-      budget_pool_controller_->OnTaskCompleted(queue, task_timing);
+      budget_pool_controller_->OnTaskCompleted(queue, task_timing,
+                                               have_seen_stop_signal);
       return;
     case Experiment::kNone:
       return;
@@ -207,8 +225,9 @@
 
 void CompositorPriorityExperiments::CompositorBudgetPoolController::
     OnTaskCompleted(MainThreadTaskQueue* queue,
-                    MainThreadTaskQueue::TaskTiming* task_timing) {
-  if (queue->queue_type() == MainThreadTaskQueue::QueueType::kCompositor) {
+                    MainThreadTaskQueue::TaskTiming* task_timing,
+                    bool have_seen_stop_signal) {
+  if (have_seen_stop_signal) {
     compositor_budget_pool_->RecordTaskRunTime(queue, task_timing->start_time(),
                                                task_timing->end_time());
   }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h b/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h
index 1cb8789..bb034bf 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/compositor_priority_experiments.h
@@ -52,6 +52,8 @@
     return alternating_compositor_priority_;
   }
 
+  void OnWillBeginMainFrame();
+
   void OnMainThreadSchedulerInitialized();
   void OnMainThreadSchedulerShutdown();
 
@@ -76,7 +78,8 @@
     void UpdateCompositorBudgetState(base::TimeTicks now);
 
     void OnTaskCompleted(MainThreadTaskQueue* queue,
-                         MainThreadTaskQueue::TaskTiming* task_timing);
+                         MainThreadTaskQueue::TaskTiming* task_timing,
+                         bool have_seen_stop_signal);
 
     // Unimplemented methods.
     void AddQueueToBudgetPool(TaskQueue* queue,
@@ -94,14 +97,16 @@
     const base::TickClock* tick_clock_;  // Not owned.
   };
 
-  MainThreadSchedulerImpl* scheduler_;  // Not owned.
-
   static Experiment GetExperimentFromFeatureList();
 
   void DoPrioritizeCompositingAfterDelay();
 
   void PostPrioritizeCompositingAfterDelayTask();
 
+  enum class StopSignalType { kAnyCompositorTask, kBeginMainFrameTask };
+
+  MainThreadSchedulerImpl* scheduler_;  // Not owned.
+
   const Experiment experiment_;
 
   QueuePriority alternating_compositor_priority_ =
@@ -113,6 +118,9 @@
 
   QueuePriority budget_compositor_priority_ = QueuePriority::kVeryHighPriority;
   std::unique_ptr<CompositorBudgetPoolController> budget_pool_controller_;
+
+  const StopSignalType stop_signal_;
+  bool will_begin_main_frame_ = false;
 };
 
 }  // namespace scheduler
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
index 45a470a6..0e52953 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -1142,8 +1142,13 @@
   scoped_refptr<MainThreadTaskQueue> task_queue =
       frame_task_queue_controller_->NewWebSchedulingTaskQueue(
           PausableTaskQueueTraits(), priority);
-  return std::make_unique<WebSchedulingTaskQueueImpl>(priority,
-                                                      task_queue.get());
+  return std::make_unique<WebSchedulingTaskQueueImpl>(task_queue->AsWeakPtr());
+}
+
+void FrameSchedulerImpl::OnWebSchedulingTaskQueuePriorityChanged(
+    MainThreadTaskQueue* queue) {
+  UpdateQueuePolicy(queue,
+                    frame_task_queue_controller_->GetQueueEnabledVoter(queue));
 }
 
 const base::UnguessableToken& FrameSchedulerImpl::GetAgentClusterId() const {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
index b4b07a78..860cd68 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h
@@ -186,6 +186,7 @@
 
   std::unique_ptr<WebSchedulingTaskQueue> CreateWebSchedulingTaskQueue(
       WebSchedulingPriority) override;
+  void OnWebSchedulingTaskQueuePriorityChanged(MainThreadTaskQueue*);
 
   const base::UnguessableToken& GetAgentClusterId() const;
 
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
index 674930f..43efd83 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -2300,6 +2300,7 @@
       std::unique_ptr<WebSchedulingTaskQueue> task_queue =
           frame_scheduler_->CreateWebSchedulingTaskQueue(priority);
       web_scheduling_task_runners_.push_back(task_queue->GetTaskRunner());
+      task_queues_.push_back(std::move(task_queue));
     }
   }
 
@@ -2353,6 +2354,8 @@
 
   Vector<scoped_refptr<base::SingleThreadTaskRunner>>
       web_scheduling_task_runners_;
+
+  Vector<std::unique_ptr<WebSchedulingTaskQueue>> task_queues_;
 };
 
 TEST_F(WebSchedulingTaskQueueTest, TasksRunInPriorityOrder) {
@@ -2365,6 +2368,18 @@
                                               "D2", "L1", "L2", "E1", "E2"));
 }
 
+TEST_F(WebSchedulingTaskQueueTest, DynamicTaskPriorityOrder) {
+  Vector<String> run_order;
+
+  PostWebSchedulingTestTasks(&run_order, "E1 E2 D1 D2 I1 I2");
+  task_queues_[static_cast<int>(WebSchedulingPriority::kImmediatePriority)]
+      ->SetPriority(WebSchedulingPriority::kLowPriority);
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(run_order,
+              testing::ElementsAre("D1", "D2", "I1", "I2", "E1", "E2"));
+}
+
 }  // namespace frame_scheduler_impl_unittest
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 6dc3dc2a..1c1a130 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -844,6 +844,7 @@
     any_thread().begin_main_frame_on_critical_path = args.on_critical_path;
   }
   main_thread_only().compositing_experiment.OnWillBeginMainFrame();
+  main_thread_only().compositor_priority_experiments.OnWillBeginMainFrame();
 }
 
 void MainThreadSchedulerImpl::DidCommitFrameToCompositor() {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
index d50b498..a6afaa1 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl_unittest.cc
@@ -3869,7 +3869,7 @@
 
 TEST_P(VeryHighPriorityForCompositingAfterDelayExperimentTest,
        TestCompositorPolicy_FirstCompositorTaskSetToVeryHighPriority) {
-  // 1.5ms task to complete the countdown and prioritze compositing.
+  // 150ms task to complete the countdown and prioritze compositing.
   AdvanceTimeWithTask(0.15);
 
   Vector<String> run_order;
@@ -3968,6 +3968,126 @@
   EXPECT_EQ(UseCase::kNone, CurrentUseCase());
 }
 
+class VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest
+    : public MainThreadSchedulerImplTest {
+ public:
+  VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest()
+      : MainThreadSchedulerImplTest({kVeryHighPriorityForCompositingAlternating,
+                                     kPrioritizeCompositingUntilBeginMainFrame},
+                                    {}) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest,
+    testing::Values(AntiStarvationLogic::kEnabled,
+                    AntiStarvationLogic::kDisabled),
+    GetTestNameSuffix);
+
+TEST_P(VeryHighPriorityForCompositingAlternatingBeginMainFrameExperimentTest,
+       TestCompositorPolicy_AlternatingCompositorTasks) {
+  Vector<String> run_order;
+  PostTestTasks(&run_order, "C1 D1 C2 D2");
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(run_order, testing::ElementsAre("C1", "C2", "D1", "D2"));
+  EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+
+  // Next compositor task is the BeginMainFrame. Compositor priority is set
+  // to normal for a single task before being prioritized again.
+  DoMainFrame();
+
+  run_order.clear();
+  PostTestTasks(&run_order, "C1 D1 D2 C2");
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(run_order, testing::ElementsAre("C1", "D1", "C2", "D2"));
+  EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+}
+
+class VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest
+    : public MainThreadSchedulerImplTest {
+ public:
+  VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest()
+      : MainThreadSchedulerImplTest({kVeryHighPriorityForCompositingAfterDelay,
+                                     kPrioritizeCompositingUntilBeginMainFrame},
+                                    {}) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest,
+    testing::Values(AntiStarvationLogic::kEnabled,
+                    AntiStarvationLogic::kDisabled),
+    GetTestNameSuffix);
+
+TEST_P(
+    VeryHighPriorityForCompositingAfterDelayUntilBeginMainFrameExperimentTest,
+    TestCompositorPolicy_FirstCompositorTaskSetToVeryHighPriority) {
+  // 150ms task to complete the countdown and prioritze compositing.
+  AdvanceTimeWithTask(0.15);
+
+  Vector<String> run_order;
+  PostTestTasks(&run_order, "D1 C1 D2 C2 P1");
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(run_order, testing::ElementsAre("P1", "C1", "C2", "D1", "D2"));
+  EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+
+  // Next compositor task is the BeginMainFrame.
+  DoMainFrame();
+  run_order.clear();
+  PostTestTasks(&run_order, "C1 D1 D2 C2 P1");
+
+  base::RunLoop().RunUntilIdle();
+  EXPECT_THAT(run_order, testing::ElementsAre("P1", "C1", "D1", "D2", "C2"));
+  EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+}
+
+class VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest
+    : public MainThreadSchedulerImplTest {
+ public:
+  VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest()
+      : MainThreadSchedulerImplTest({kVeryHighPriorityForCompositingBudget,
+                                     kPrioritizeCompositingUntilBeginMainFrame},
+                                    {}) {}
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest,
+    testing::Values(AntiStarvationLogic::kEnabled,
+                    AntiStarvationLogic::kDisabled),
+    GetTestNameSuffix);
+
+TEST_P(
+    VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest,
+    TestCompositorPolicy_CompositorPriorityNonBeginMainFrameDoesntExhaustBudget) {
+  // 1000ms compositor task will not exhaust the budget.
+  RunSlowCompositorTask();
+
+  Vector<String> run_order;
+  PostTestTasks(&run_order, "D1 C1 D2 C2 P1");
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(run_order, testing::ElementsAre("P1", "C1", "C2", "D1", "D2"));
+  EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+}
+
+TEST_P(VeryHighPriorityForCompositingBudgetBeginMainFrameExperimentTest,
+       TestCompositorPolicy_CompositorPriorityBeginMainFrameExhaustsBudget) {
+  // 1000ms BeginMainFrame will exhaust the budget.
+  DoMainFrame();
+  RunSlowCompositorTask();
+
+  Vector<String> run_order;
+  PostTestTasks(&run_order, "D1 C1 D2 C2 P1");
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_THAT(run_order, testing::ElementsAre("P1", "D1", "C1", "D2", "C2"));
+  EXPECT_EQ(UseCase::kNone, CurrentUseCase());
+}
+
 }  // namespace main_thread_scheduler_impl_unittest
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
index e4d579a..d008643 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
 #include "third_party/blink/renderer/platform/wtf/wtf.h"
 
@@ -251,6 +252,14 @@
   return net_request_priority_;
 }
 
+void MainThreadTaskQueue::SetWebSchedulingPriority(
+    WebSchedulingPriority priority) {
+  if (web_scheduling_priority_ == priority)
+    return;
+  web_scheduling_priority_ = priority;
+  frame_scheduler_->OnWebSchedulingTaskQueuePriorityChanged(this);
+}
+
 base::Optional<WebSchedulingPriority>
 MainThreadTaskQueue::web_scheduling_priority() const {
   return web_scheduling_priority_;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
index 5823914..c1ea93cd 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -384,8 +384,13 @@
   void SetNetRequestPriority(net::RequestPriority net_request_priority);
   base::Optional<net::RequestPriority> net_request_priority() const;
 
+  void SetWebSchedulingPriority(WebSchedulingPriority priority);
   base::Optional<WebSchedulingPriority> web_scheduling_priority() const;
 
+  base::WeakPtr<MainThreadTaskQueue> AsWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+
  protected:
   void SetFrameSchedulerForTest(FrameSchedulerImpl* frame_scheduler);
 
@@ -424,10 +429,7 @@
   // |web_scheduling_priority_| is the priority of the task queue within the web
   // scheduling API. This priority is used in conjunction with the frame
   // scheduling policy to determine the task queue priority.
-  //
-  // For the initial prototype, we aren't allowing the priority to change since
-  // we're only implementing a set of global task queues.
-  const base::Optional<WebSchedulingPriority> web_scheduling_priority_;
+  base::Optional<WebSchedulingPriority> web_scheduling_priority_;
 
   // Needed to notify renderer scheduler about completed tasks.
   MainThreadSchedulerImpl* main_thread_scheduler_;  // NOT OWNED
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_task_queue_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_task_queue_impl.cc
index a8a7ce3..30b6e4b4 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_task_queue_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_task_queue_impl.cc
@@ -11,11 +11,15 @@
 namespace scheduler {
 
 WebSchedulingTaskQueueImpl::WebSchedulingTaskQueueImpl(
-    WebSchedulingPriority priority,
-    MainThreadTaskQueue* task_queue)
-    : priority_(priority),
-      task_runner_(
-          task_queue->CreateTaskRunner(TaskType::kExperimentalWebScheduling)) {}
+    base::WeakPtr<MainThreadTaskQueue> task_queue)
+    : task_runner_(
+          task_queue->CreateTaskRunner(TaskType::kExperimentalWebScheduling)),
+      task_queue_(std::move(task_queue)) {}
+
+void WebSchedulingTaskQueueImpl::SetPriority(WebSchedulingPriority priority) {
+  if (task_queue_)
+    task_queue_->SetWebSchedulingPriority(priority);
+}
 
 scoped_refptr<base::SingleThreadTaskRunner>
 WebSchedulingTaskQueueImpl::GetTaskRunner() {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_task_queue_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_task_queue_impl.h
index 3dfba91..a4da9eaf 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_task_queue_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/web_scheduling_task_queue_impl.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h"
 
 #include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/single_thread_task_runner.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/public/web_scheduling_priority.h"
@@ -20,16 +21,16 @@
 class PLATFORM_EXPORT WebSchedulingTaskQueueImpl
     : public WebSchedulingTaskQueue {
  public:
-  WebSchedulingTaskQueueImpl(WebSchedulingPriority, MainThreadTaskQueue*);
+  WebSchedulingTaskQueueImpl(base::WeakPtr<MainThreadTaskQueue>);
   ~WebSchedulingTaskQueueImpl() override = default;
 
-  WebSchedulingPriority Priority() override { return priority_; }
+  void SetPriority(WebSchedulingPriority) override;
 
   scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override;
 
  private:
-  const WebSchedulingPriority priority_;
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+  base::WeakPtr<MainThreadTaskQueue> task_queue_;
 };
 
 }  // namespace scheduler
diff --git a/third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h b/third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h
index 12f5f63..0f580027 100644
--- a/third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h
+++ b/third_party/blink/renderer/platform/scheduler/public/web_scheduling_task_queue.h
@@ -19,7 +19,7 @@
  public:
   virtual ~WebSchedulingTaskQueue() = default;
 
-  virtual WebSchedulingPriority Priority() = 0;
+  virtual void SetPriority(WebSchedulingPriority) = 0;
 
   // Returns a task runner that is suitable with the web scheduling task type
   // associated with the priority of this task queue.
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 8fecf46..0f3755e 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2742,6 +2742,9 @@
 crbug.com/1012627 [ Win7 ] external/wpt/css/css-text/line-breaking/line-breaking-021.html [ Failure ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 [ Linux ] external/wpt/css/css-sizing/percentage-resolution-001.html [ Failure ]
+crbug.com/626703 [ Mac ] external/wpt/css/css-sizing/percentage-resolution-001.html [ Failure ]
+crbug.com/626703 [ Win ] external/wpt/css/css-sizing/percentage-resolution-001.html [ Failure ]
 crbug.com/626703 [ Linux ] external/wpt/css/css-fonts/standard-font-family-13.html [ Failure ]
 crbug.com/626703 [ Mac ] external/wpt/css/css-fonts/standard-font-family-13.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/css/css-fonts/standard-font-family-13.html [ Failure ]
@@ -5791,9 +5794,6 @@
 crbug.com/1014950 [ Mac ] virtual/threaded/http/tests/devtools/tracing/timeline-js/timeline-js-line-level-profile-no-url-end-to-end.js [ Pass Timeout ]
 crbug.com/1015187 [ Linux Mac ] virtual/cross-origin-isolation/external/wpt/html/cross-origin-embedder-policy/none.https.html [ Pass Failure ]
 
-# Temporarily disabled until we find the root cause in v8
-crbug.com/1027131 fast/encoding/resource-default-encoding.html [ Pass Failure ]
-
 # Sheriff 2019-10-18
 crbug.com/1015859 [ Linux ] http/tests/devtools/a11y-axe-core/performance/landing-page-a11y-test.js [ Pass Failure ]
 crbug.com/1015975 media/video-currentTime.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/animations/interpolation/background-image-interpolation.html b/third_party/blink/web_tests/animations/interpolation/background-image-interpolation.html
deleted file mode 100644
index 181dac42..0000000
--- a/third_party/blink/web_tests/animations/interpolation/background-image-interpolation.html
+++ /dev/null
@@ -1,174 +0,0 @@
-<!DOCTYPE html>
-<meta charset="UTF-8">
-<style>
-.parent {
-  background-image: url(../resources/blue-100.png);
-  background-size: 0 0;
-}
-.target {
-  width: 100px;
-  height: 100px;
-  display: inline-block;
-  border: 10px solid black;
-  background-repeat: no-repeat;
-  background-image: url(../resources/blue-100.png);
-}
-.expected {
-  border-color: green;
-  margin-right: 2px;
-}
-</style>
-<body>
-<script src="resources/interpolation-test.js"></script>
-<script>
-// Neutral to image
-var from = 'url(../resources/blue-100.png)';
-var to = 'url(../resources/green-100.png)';
-assertInterpolation({
-  property: 'background-image',
-  from: neutralKeyframe,
-  to: to,
-}, [
-  {at: -0.3, is: from},
-  {at: 0, is: from},
-  {at: 0.3, is: '-webkit-cross-fade(' + from + ', ' + to + ', 0.3)'},
-  {at: 0.6, is: '-webkit-cross-fade(' + from + ', ' + to + ', 0.6)'},
-  {at: 1, is: to},
-  {at: 1.5, is: to},
-]);
-
-// initial to image
-to = 'url(../resources/green-100.png)';
-assertNoInterpolation({
-  property: 'background-image',
-  from: 'initial',
-  to: to,
-});
-
-// inherit to image
-from = 'url(../resources/blue-100.png)';
-to = 'url(../resources/green-100.png)';
-assertInterpolation({
-  property: 'background-image',
-  from: 'inherit',
-  to: to,
-}, [
-  {at: -0.3, is: from},
-  {at: 0, is: from},
-  {at: 0.3, is: '-webkit-cross-fade(' + from + ', ' + to + ', 0.3)'},
-  {at: 0.6, is: '-webkit-cross-fade(' + from + ', ' + to + ', 0.6)'},
-  {at: 1, is: to},
-  {at: 1.5, is: to},
-]);
-
-// unset to image
-assertNoInterpolation({
-  property: 'background-image',
-  from: 'unset',
-  to: to,
-});
-
-// Image to image
-from = 'url(../resources/blue-100.png)';
-to = 'url(../resources/green-100.png)';
-assertInterpolation({
-  property: 'background-image',
-  from: from,
-  to: to,
-}, [
-  {at: -0.3, is: from},
-  {at: 0, is: from},
-  {at: 0.3, is: '-webkit-cross-fade(' + from + ', ' + to + ', 0.3)'},
-  {at: 0.6, is: '-webkit-cross-fade(' + from + ', ' + to + ', 0.6)'},
-  {at: 1, is: to},
-  {at: 1.5, is: to},
-]);
-
-// Image to gradient
-from = 'url(../resources/blue-100.png)';
-to = 'linear-gradient(45deg, blue, orange)';
-assertNoInterpolation({
-  property: 'background-image',
-  from: from,
-  to: to,
-});
-
-// Image to crossfade
-from = 'url(../resources/blue-100.png)';
-to = '-webkit-cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)';
-assertNoInterpolation({
-  property: 'background-image',
-  from: from,
-  to: to,
-});
-
-// Gradient to gradient
-from = 'linear-gradient(-45deg, red, yellow)';
-to = 'linear-gradient(45deg, blue, orange)';
-assertNoInterpolation({
-  property: 'background-image',
-  from: from,
-  to: to,
-});
-
-// Keyword to image
-from = 'none';
-to = 'url(../resources/green-100.png)';
-assertNoInterpolation({
-  property: 'background-image',
-  from: from,
-  to: to,
-});
-
-// Multiple to multiple
-var fromA = 'url(../resources/stripes-100.png)';
-var fromB = 'url(../resources/blue-100.png)';
-var toA = 'url(../resources/blue-100.png)';
-var toB = 'url(../resources/stripes-100.png)';
-from = fromA + ', ' + fromB;
-to = toA + ', ' + toB;
-assertInterpolation({
-  property: 'background-image',
-  from: from,
-  to: to,
-}, [
-  {at: -0.3, is: from},
-  {at: 0, is: from},
-  {at: 0.3, is: '-webkit-cross-fade(' + fromA + ', ' + toA + ', 0.3), -webkit-cross-fade(' + fromB + ', ' + toB + ', 0.3)'},
-  {at: 0.6, is: '-webkit-cross-fade(' + fromA + ', ' + toA + ', 0.6), -webkit-cross-fade(' + fromB + ', ' + toB + ', 0.6)'},
-  {at: 1, is: to},
-  {at: 1.5, is: to},
-]);
-
-// Single to multiple
-from = 'url(../resources/blue-100.png)';
-var toA = 'url(../resources/stripes-100.png)';
-var toB = 'url(../resources/green-100.png)';
-to = toA + ', ' + toB;
-assertInterpolation({
-  property: 'background-image',
-  from: from,
-  to: to,
-}, [
-  // The interpolation of different numbers of background-images looks a bit strange here.
-  // Animating background-image is not specified to be possible however we do it for backwards compatibility.
-  // With this in mind we kept the implementation simple at the expense of this corner case because there is
-  // no official specification to support.
-  {at: -0.3, is: from + ', ' + from},
-  {at: 0, is: from},
-  {at: 0.3, is: '-webkit-cross-fade(' + from + ', ' + toA + ', 0.3), -webkit-cross-fade(' + from + ', ' + toB + ', 0.3)'},
-  {at: 0.6, is: '-webkit-cross-fade(' + from + ', ' + toA + ', 0.6), -webkit-cross-fade(' + from + ', ' + toB + ', 0.6)'},
-  {at: 1, is: to},
-  {at: 1.5, is: to},
-]);
-
-// Multiple mismatched types
-from = 'url(../resources/blue-100.png), none';
-to = 'url(../resources/stripes-100.png), url(../resources/green-100.png)';
-assertNoInterpolation({
-  property: 'background-image',
-  from: from,
-  to: to,
-});
-</script>
-</body>
diff --git a/third_party/blink/web_tests/editing/pasteboard/paste-from-excel.html b/third_party/blink/web_tests/editing/pasteboard/paste-from-excel.html
new file mode 100644
index 0000000..8365e98
--- /dev/null
+++ b/third_party/blink/web_tests/editing/pasteboard/paste-from-excel.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+// Snapshot of real HTML written by Microsoft Excel to clipboard.
+const kExcelMarkup = `
+<html xmlns:v="urn:schemas-microsoft-com:vml"
+xmlns:o="urn:schemas-microsoft-com:office:office"
+xmlns:x="urn:schemas-microsoft-com:office:excel"
+xmlns="http://www.w3.org/TR/REC-html40">
+
+<head>
+<meta http-equiv=Content-Type content="text/html; charset=utf-8">
+<meta name=ProgId content=Excel.Sheet>
+<meta name=Generator content="Microsoft Excel 15">
+<link id=Main-File rel=Main-File
+href="file:///C:/Users/mockuser/AppData/Local/Temp/msohtmlclip1/01/clip.htm">
+<link rel=File-List
+href="file:///C:/Users/mockuser/AppData/Local/Temp/msohtmlclip1/01/clip_filelist.xml">
+<style>
+<!--table
+	{mso-displayed-decimal-separator:"\.";
+	mso-displayed-thousand-separator:"\,";}
+@page
+	{margin:.75in .7in .75in .7in;
+	mso-header-margin:.3in;
+	mso-footer-margin:.3in;}
+tr
+	{mso-height-source:auto;}
+col
+	{mso-width-source:auto;}
+br
+	{mso-data-placement:same-cell;}
+td
+	{padding-top:1px;
+	padding-right:1px;
+	padding-left:1px;
+	mso-ignore:padding;
+	color:black;
+	font-size:11.0pt;
+	font-weight:400;
+	font-style:normal;
+	text-decoration:none;
+	font-family:Calibri, sans-serif;
+	mso-font-charset:0;
+	mso-number-format:General;
+	text-align:general;
+	vertical-align:bottom;
+	border:none;
+	mso-background-source:auto;
+	mso-pattern:auto;
+	mso-protection:locked visible;
+	white-space:nowrap;
+	mso-rotate:0;}
+.xl63
+	{font-size:20.0pt;}
+.xl64
+	{font-weight:700;}
+.xl65
+	{font-style:italic;}
+.xl66
+	{text-decoration:underline;
+	text-underline-style:single;}
+.xl67
+	{color:red;}
+.xl68
+	{background:yellow;
+	mso-pattern:black none;}
+-->
+</style>
+</head>
+
+<body link="#0563C1" vlink="#954F72">
+
+<table border=0 cellpadding=0 cellspacing=0 width=128 style='border-collapse:
+ collapse;width:96pt'>
+<!--StartFragment-->
+ <col width=64 span=2 style='width:48pt'>
+ <tr height=35 style='height:26.0pt'>
+  <td height=35 class=xl63 width=64 style='height:26.0pt;width:48pt'>large</td>
+  <td class=xl64 width=64 style='width:48pt'>bold</td>
+ </tr>
+ <tr height=19 style='height:14.5pt'>
+  <td height=19 class=xl65 style='height:14.5pt'>italic</td>
+  <td class=xl66>underline</td>
+ </tr>
+ <tr height=19 style='height:14.5pt'>
+  <td height=19 class=xl67 style='height:14.5pt'>color</td>
+  <td class=xl68>highlight</td>
+ </tr>
+<!--EndFragment-->
+</table>
+
+</body>
+
+</html>
+`;
+
+document.oncopy = event => {
+  event.clipboardData.setData('text/html', kExcelMarkup);
+  event.preventDefault();
+}
+
+function parsePixelValue(str) {
+  const parsed = CSSNumericValue.parse(str);
+  assert_equals(parsed.unit, 'px');
+  return parsed.value;
+}
+</script>
+
+<p>Test passes if we can paste content from Excel with style preserved.</p>
+<div id="target" contenteditable></div>
+<script>
+test(() => {
+  target.focus();
+  document.execCommand('copy');
+  document.execCommand('paste');
+
+  // Stylesheets in clipboard should have been stripped or turned into inline style
+  assert_equals(document.styleSheets.length, 0);
+
+  // large  bold
+  // italic underline
+  // color  highlight
+
+  const cells = document.querySelectorAll('td');
+
+  assert_approx_equals(
+      parsePixelValue(getComputedStyle(cells[0]).fontSize),
+      20 * 1.3333, // 20pt to pixels
+      0.01);
+
+  assert_equals(getComputedStyle(cells[1]).fontWeight, '700');
+  assert_equals(getComputedStyle(cells[2]).fontStyle, 'italic');
+  assert_equals(getComputedStyle(cells[3]).textDecorationLine, 'underline');
+  assert_equals(getComputedStyle(cells[4]).color, 'rgb(255, 0, 0)');
+  assert_equals(getComputedStyle(cells[5]).backgroundColor, 'rgb(255, 255, 0)');
+}, 'Style on content pasted from Excel is preserved');
+</script>
diff --git a/third_party/blink/web_tests/editing/pasteboard/paste-head-contents-expected.txt b/third_party/blink/web_tests/editing/pasteboard/paste-head-contents-expected.txt
index d6d5bb5..377fd36 100644
--- a/third_party/blink/web_tests/editing/pasteboard/paste-head-contents-expected.txt
+++ b/third_party/blink/web_tests/editing/pasteboard/paste-head-contents-expected.txt
@@ -1,4 +1,4 @@
-This test ensures WebKit strips away base, link, meta and title elements before inserting HTML.
+This test ensures WebKit strips away base, link, meta, style and title elements before inserting HTML.
 
 PASS
 
diff --git a/third_party/blink/web_tests/editing/pasteboard/paste-head-contents.html b/third_party/blink/web_tests/editing/pasteboard/paste-head-contents.html
index 52da906d..1784afb 100644
--- a/third_party/blink/web_tests/editing/pasteboard/paste-head-contents.html
+++ b/third_party/blink/web_tests/editing/pasteboard/paste-head-contents.html
@@ -64,7 +64,7 @@
 </body>
 </html>
 </script>
-<p>This test ensures WebKit strips away base, link, meta and title elements before inserting HTML.</p>
+<p>This test ensures WebKit strips away base, link, meta, style and title elements before inserting HTML.</p>
 <div id="test" contenteditable></div>
 <pre><script type="text/javascript">
 
@@ -99,10 +99,9 @@
 expectNoInstanceOf('base');
 expectNoInstanceOf('meta');
 expectNoInstanceOf('link');
+expectNoInstanceOf('style');
 expectNoInstanceOf('title');
 
-expectInstancesOf('style');
-
 if (passed)
     document.writeln('PASS');
 
diff --git a/third_party/blink/web_tests/editing/pasteboard/paste-xss-injection.html b/third_party/blink/web_tests/editing/pasteboard/paste-xss-injection.html
index 2d3cc84..91f1f2d 100644
--- a/third_party/blink/web_tests/editing/pasteboard/paste-xss-injection.html
+++ b/third_party/blink/web_tests/editing/pasteboard/paste-xss-injection.html
@@ -9,6 +9,6 @@
     selection.setClipboardData('<math><xss style=display:block>t<style>X<a title="</style><img src onerror=alert(1)>">.<a>.');
     selection.document.execCommand('paste');
   },
-  '<div contenteditable>te<br>t<style>X<a title="</style><img src>">.<a>.|</a>st</div>',
+  '<div contenteditable>te<br>t<img src>">.<a>.|</a>st</div>',
   'Paste blocks script injection');
 </script>
diff --git a/third_party/blink/web_tests/editing/pasteboard/preserve-underline-color-expected.txt b/third_party/blink/web_tests/editing/pasteboard/preserve-underline-color-expected.txt
index b17143f..5ea8131 100644
--- a/third_party/blink/web_tests/editing/pasteboard/preserve-underline-color-expected.txt
+++ b/third_party/blink/web_tests/editing/pasteboard/preserve-underline-color-expected.txt
@@ -1,5 +1,5 @@
 This test for a bug copy/pasting underlined text. The color of the underline should be the color of the element that has the text-decoration property.
 | <span>
-|   style="color: rgb(255, 0, 0); text-decoration-line: underline;"
+|   style="text-decoration-line: underline; color: rgb(255, 0, 0);"
 |   "This should be underlined.<#selection-caret>"
 | <br>
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 77a55f3..f92e2e6 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
@@ -66665,6 +66665,18 @@
      {}
     ]
    ],
+   "css/css-sizing/percentage-resolution-001.html": [
+    [
+     "css/css-sizing/percentage-resolution-001.html",
+     [
+      [
+       "/css/css-sizing/percentage-resolution-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-sizing/range-percent-intrinsic-size-1.html": [
     [
      "css/css-sizing/range-percent-intrinsic-size-1.html",
@@ -143767,6 +143779,9 @@
    "css/css-sizing/parsing/width-valid-expected.txt": [
     []
    ],
+   "css/css-sizing/percentage-resolution-001-ref.html": [
+    []
+   ],
    "css/css-sizing/range-percent-intrinsic-size-1-ref.html": [
     []
    ],
@@ -174016,7 +174031,37 @@
    "tools/ci/taskcluster-run.py": [
     []
    ],
-   "tools/ci/tcdownload.py": [
+   "tools/ci/tc/README.md": [
+    []
+   ],
+   "tools/ci/tc/__init__.py": [
+    []
+   ],
+   "tools/ci/tc/decision.py": [
+    []
+   ],
+   "tools/ci/tc/download.py": [
+    []
+   ],
+   "tools/ci/tc/taskgraph.py": [
+    []
+   ],
+   "tools/ci/tc/tasks/test.yml": [
+    []
+   ],
+   "tools/ci/tc/testdata/master_push_event.json": [
+    []
+   ],
+   "tools/ci/tc/testdata/pr_event.json": [
+    []
+   ],
+   "tools/ci/tc/tests/test_decision.py": [
+    []
+   ],
+   "tools/ci/tc/tests/test_taskgraph.py": [
+    []
+   ],
+   "tools/ci/tc/tests/test_valid.py": [
     []
    ],
    "tools/ci/website_build.sh": [
@@ -174370,21 +174415,6 @@
    "tools/serve/test_serve.py": [
     []
    ],
-   "tools/taskcluster/__init__.py": [
-    []
-   ],
-   "tools/taskcluster/commands.json": [
-    []
-   ],
-   "tools/taskcluster/testdata/master_push_event.json": [
-    []
-   ],
-   "tools/taskcluster/testdata/pr_event.json": [
-    []
-   ],
-   "tools/taskcluster/verify.py": [
-    []
-   ],
    "tools/third_party/atomicwrites/CONTRIBUTING.rst": [
     []
    ],
@@ -219875,6 +219905,12 @@
      {}
     ]
    ],
+   "css/css-transitions/animations/vertical-align-interpolation.html": [
+    [
+     "css/css-transitions/animations/vertical-align-interpolation.html",
+     {}
+    ]
+   ],
    "css/css-transitions/before-load-001.html": [
     [
      "css/css-transitions/before-load-001.html",
@@ -399016,6 +399052,14 @@
    "11c34eafc32812da1b4da1552afe08c231cd69db",
    "testharness"
   ],
+  "css/css-sizing/percentage-resolution-001-ref.html": [
+   "a4e7f1ece4675bd415ed29ed0801b4890a31c9c4",
+   "support"
+  ],
+  "css/css-sizing/percentage-resolution-001.html": [
+   "b2cc727ca7a94f4c4f27a3d4c515f8665b769f34",
+   "reftest"
+  ],
   "css/css-sizing/range-percent-intrinsic-size-1-ref.html": [
    "9a68590b6389eb9c1fefc2dc37f42b2a60b3b41a",
    "support"
@@ -413012,6 +413056,10 @@
    "c007816ecb04236421ff0aea8861da19c244338a",
    "testharness"
   ],
+  "css/css-transitions/animations/vertical-align-interpolation.html": [
+   "d8807b185fb4dec83e74cc47bae5cf66488b43dd",
+   "testharness"
+  ],
   "css/css-transitions/before-load-001.html": [
    "009260eea430721971053f6d98330f9a9ed56d6d",
    "testharness"
@@ -469849,7 +469897,7 @@
    "testharness"
   ],
   "lint.whitelist": [
-   "4e6a21464c80418462b451aecb9fbab87f0c698e",
+   "be6ae4f0314942a35632a1b0dc8b10dd9dd34657",
    "support"
   ],
   "loading/lazyload/common.js": [
@@ -507433,7 +507481,7 @@
    "support"
   ],
   "tools/ci/commands.json": [
-   "841fd855c8056829afd347e3067e55804ca65139",
+   "c9cd7c458989ec77bacd645cd5145351443ff73c",
    "support"
   ],
   "tools/ci/epochs_update.sh": [
@@ -507457,15 +507505,55 @@
    "support"
   ],
   "tools/ci/run_tc.py": [
-   "be7dbe082a1709493b62316d15668ea10ae93607",
+   "da4e18ae8c6660f1507f2fe5efe43a82755c7d77",
    "support"
   ],
   "tools/ci/taskcluster-run.py": [
    "8a60012d09b4d5be9845cf4ab74e5f87bf4b58ce",
    "support"
   ],
-  "tools/ci/tcdownload.py": [
-   "256726da7b568f32115dfeaa6bc1407490c0d076",
+  "tools/ci/tc/README.md": [
+   "11f367f986f97fdbfe40f09b2ae4114f263916f7",
+   "support"
+  ],
+  "tools/ci/tc/__init__.py": [
+   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+   "support"
+  ],
+  "tools/ci/tc/decision.py": [
+   "f3ef34b557eafac68f85b68c5ce8c7f58f9c8015",
+   "support"
+  ],
+  "tools/ci/tc/download.py": [
+   "359ec33405048c134ddcfac7a564ea9a17ac1b14",
+   "support"
+  ],
+  "tools/ci/tc/taskgraph.py": [
+   "6a6cb497f2d406862d18bc214d36c1ea2ecd7c3e",
+   "support"
+  ],
+  "tools/ci/tc/tasks/test.yml": [
+   "3a71ab3a0a1bebb9141f92b639acbc8ccf318c6a",
+   "support"
+  ],
+  "tools/ci/tc/testdata/master_push_event.json": [
+   "8e79f7537349a79fd70746ab6e371897acc4d5e1",
+   "support"
+  ],
+  "tools/ci/tc/testdata/pr_event.json": [
+   "5acc1a6010291c88d26f8c12573aeeb9d0b33e97",
+   "support"
+  ],
+  "tools/ci/tc/tests/test_decision.py": [
+   "425b5829edd360b145acebf249f4b1ac3f6d1396",
+   "support"
+  ],
+  "tools/ci/tc/tests/test_taskgraph.py": [
+   "dd8535bb6470ff919251973b073d24489edfff8b",
+   "support"
+  ],
+  "tools/ci/tc/tests/test_valid.py": [
+   "f51a633b537ff5a71daa8daed5569ac1b586db9a",
    "support"
   ],
   "tools/ci/website_build.sh": [
@@ -507936,26 +508024,6 @@
    "d6c082a970357f477e82cd25a1c0952ee744db54",
    "support"
   ],
-  "tools/taskcluster/__init__.py": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
-  "tools/taskcluster/commands.json": [
-   "dcbd8961fc2efbb800565b927a3e5d9314d6aba6",
-   "support"
-  ],
-  "tools/taskcluster/testdata/master_push_event.json": [
-   "8e79f7537349a79fd70746ab6e371897acc4d5e1",
-   "support"
-  ],
-  "tools/taskcluster/testdata/pr_event.json": [
-   "5acc1a6010291c88d26f8c12573aeeb9d0b33e97",
-   "support"
-  ],
-  "tools/taskcluster/verify.py": [
-   "b37e45ec74cfbccffee84319fc8b94512f170dca",
-   "support"
-  ],
   "tools/third_party/atomicwrites/CONTRIBUTING.rst": [
    "86d3e4a65e7d28249a9b8bbac15bff37c49b2ae4",
    "support"
@@ -511713,7 +511781,7 @@
    "support"
   ],
   "tools/tox.ini": [
-   "7a5c1eb648a97aebc718912cb22492385c588439",
+   "e8673db0a69ae4695e7b1fa152c533aa1b0f98d1",
    "support"
   ],
   "tools/webdriver/README.md": [
@@ -511753,7 +511821,7 @@
    "support"
   ],
   "tools/wpt/browser.py": [
-   "e7d57d8638c231f9c3c65e932bdc0209ba3cb021",
+   "a94d5509cc8ba1bfcc4cbceafd9f511a4f9804ab",
    "support"
   ],
   "tools/wpt/commands.json": [
@@ -511765,7 +511833,7 @@
    "support"
   ],
   "tools/wpt/install.py": [
-   "8215dfe09161ad49ba3975e1eacc927c61715acf",
+   "24915f0c98d0be3a864a3a515ba0e162dc37da8c",
    "support"
   ],
   "tools/wpt/mach-emulator.manifest": [
@@ -511777,7 +511845,7 @@
    "support"
   ],
   "tools/wpt/paths": [
-   "093a71568920a944b0b8361165f5ea5fa002ee14",
+   "35867c4ccbfe95c4e71a8d71ce90a7b378951fd5",
    "support"
   ],
   "tools/wpt/requirements.txt": [
@@ -512105,11 +512173,11 @@
    "support"
   ],
   "tools/wptrunner/wptrunner/formatters/chromium.py": [
-   "569239018a2b72cd0d7daee6b3ad27af38130d05",
+   "56435eabf4bbeadbee7afa294e3e0bb1e7685b99",
    "support"
   ],
   "tools/wptrunner/wptrunner/formatters/tests/test_chromium.py": [
-   "9bb72d957f4d46d411d06810db08ec7f11a72cd9",
+   "83c128ac5cb46a65373a2fea008c6ba60350fd8f",
    "support"
   ],
   "tools/wptrunner/wptrunner/formatters/wptreport.py": [
@@ -517985,7 +518053,7 @@
    "support"
   ],
   "web-nfc/NDEFReader-document-hidden-manual.https.html": [
-   "565492bc4f8d8323ef766ab163cb32dc77becb4d",
+   "9cf42e83bf306b5272fd753461fb78c3c9a4992f",
    "manual"
   ],
   "web-nfc/NDEFReader_options.https.html": [
@@ -517993,7 +518061,7 @@
    "testharness"
   ],
   "web-nfc/NDEFReader_scan.https.html": [
-   "edd968cf0ff1975d38ec7bff3ff946d1aa00cbdf",
+   "78160fbcb2ab11a8ebc80c39a6fd935eddc0844b",
    "testharness"
   ],
   "web-nfc/NDEFReader_scan_iframe.https.html": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/background-image-interpolation-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/background-image-interpolation-expected.txt
new file mode 100644
index 0000000..86d1000
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/background-image-interpolation-expected.txt
@@ -0,0 +1,320 @@
+This is a testharness.js-based test.
+Found 316 tests; 248 PASS, 68 FAIL, 0 TIMEOUT, 0 NOTRUN.
+PASS CSS Transitions: property <background-image> from neutral to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Transitions: property <background-image> from neutral to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Transitions: property <background-image> from neutral to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Transitions: property <background-image> from neutral to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Transitions: property <background-image> from neutral to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from neutral to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from neutral to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from neutral to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Transitions with transition: all: property <background-image> from neutral to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Transitions with transition: all: property <background-image> from neutral to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Transitions with transition: all: property <background-image> from neutral to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from neutral to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS Web Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL Web Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL Web Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS Web Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from neutral to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [initial] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [initial] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [initial] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [initial] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [initial] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [initial] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (-0.3) should be [initial]
+PASS CSS Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0) should be [initial]
+PASS CSS Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.3) should be [initial]
+PASS CSS Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (-0.3) should be [initial]
+PASS Web Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0) should be [initial]
+PASS Web Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.3) should be [initial]
+PASS Web Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [initial] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Transitions: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Transitions: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Transitions: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Transitions: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Transitions with transition: all: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Transitions with transition: all: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Transitions with transition: all: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS Web Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL Web Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL Web Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS Web Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [inherit] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [unset] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [unset] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [unset] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [unset] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [unset] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [unset] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (-0.3) should be [unset]
+PASS CSS Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0) should be [unset]
+PASS CSS Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.3) should be [unset]
+PASS CSS Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (-0.3) should be [unset]
+PASS Web Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0) should be [unset]
+PASS Web Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.3) should be [unset]
+PASS Web Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [unset] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (-0.3) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.3) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.6) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (1) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (1.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (-0.3) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.3) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.6) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (1) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (1.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0) should be [url(../resources/blue-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.3) should be [url(../resources/blue-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.6) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (1) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (1.5) should be [linear-gradient(45deg, blue, orange)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (-0.3) should be [url(../resources/blue-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0) should be [url(../resources/blue-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.3) should be [url(../resources/blue-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.5) should be [linear-gradient(45deg, blue, orange)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (0.6) should be [linear-gradient(45deg, blue, orange)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (1) should be [linear-gradient(45deg, blue, orange)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [linear-gradient(45deg, blue, orange)] at (1.5) should be [linear-gradient(45deg, blue, orange)]
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (-0.3) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.3) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.5) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.6) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (1) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (1.5) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (-0.3) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.3) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.5) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.6) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (1) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (1.5) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (-0.3) should be [url(../resources/blue-100.png)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0) should be [url(../resources/blue-100.png)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.3) should be [url(../resources/blue-100.png)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.5) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.6) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (1) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (1.5) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (-0.3) should be [url(../resources/blue-100.png)] assert_true: 'to' value should be supported expected true got false
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0) should be [url(../resources/blue-100.png)] assert_true: 'to' value should be supported expected true got false
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.3) should be [url(../resources/blue-100.png)] assert_true: 'to' value should be supported expected true got false
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.5) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (0.6) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (1) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] at (1.5) should be [cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)] assert_true: 'to' value should be supported expected true got false
+PASS CSS Transitions: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (-0.3) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.3) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.6) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (1) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (1.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (-0.3) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.3) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.6) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (1) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions with transition: all: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (1.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (-0.3) should be [linear-gradient(-45deg, red, yellow)]
+PASS CSS Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0) should be [linear-gradient(-45deg, red, yellow)]
+PASS CSS Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.3) should be [linear-gradient(-45deg, red, yellow)]
+PASS CSS Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.6) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (1) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (1.5) should be [linear-gradient(45deg, blue, orange)]
+PASS Web Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (-0.3) should be [linear-gradient(-45deg, red, yellow)]
+PASS Web Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0) should be [linear-gradient(-45deg, red, yellow)]
+PASS Web Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.3) should be [linear-gradient(-45deg, red, yellow)]
+PASS Web Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.5) should be [linear-gradient(45deg, blue, orange)]
+PASS Web Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (0.6) should be [linear-gradient(45deg, blue, orange)]
+PASS Web Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (1) should be [linear-gradient(45deg, blue, orange)]
+PASS Web Animations: property <background-image> from [linear-gradient(-45deg, red, yellow)] to [linear-gradient(45deg, blue, orange)] at (1.5) should be [linear-gradient(45deg, blue, orange)]
+PASS CSS Transitions: property <background-image> from [none] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [none] to [url(../resources/green-100.png)] at (0) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [none] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [none] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [none] to [url(../resources/green-100.png)] at (-0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [none] to [url(../resources/green-100.png)] at (0) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.3) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [none] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [none] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (-0.3) should be [none]
+PASS CSS Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (0) should be [none]
+PASS CSS Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.3) should be [none]
+PASS CSS Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (-0.3) should be [none]
+PASS Web Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (0) should be [none]
+PASS Web Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.3) should be [none]
+PASS Web Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.5) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (0.6) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (1) should be [url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [none] to [url(../resources/green-100.png)] at (1.5) should be [url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (-0.3) should be [url(../resources/stripes-100.png), url(../resources/blue-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0) should be [url(../resources/stripes-100.png), url(../resources/blue-100.png)]
+FAIL CSS Transitions: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0.3) should be [cross-fade(url(../resources/stripes-100.png), url(../resources/blue-100.png), 0.3), cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , 0.3 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.3 ) "
+FAIL CSS Transitions: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0.6) should be [cross-fade(url(../resources/stripes-100.png), url(../resources/blue-100.png), 0.6), cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , 0.6 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.6 ) "
+PASS CSS Transitions: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (1) should be [url(../resources/blue-100.png), url(../resources/stripes-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (1.5) should be [url(../resources/blue-100.png), url(../resources/stripes-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (-0.3) should be [url(../resources/stripes-100.png), url(../resources/blue-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0) should be [url(../resources/stripes-100.png), url(../resources/blue-100.png)]
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0.3) should be [cross-fade(url(../resources/stripes-100.png), url(../resources/blue-100.png), 0.3), cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , 0.3 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.3 ) "
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0.6) should be [cross-fade(url(../resources/stripes-100.png), url(../resources/blue-100.png), 0.6), cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , 0.6 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.6 ) "
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (1) should be [url(../resources/blue-100.png), url(../resources/stripes-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (1.5) should be [url(../resources/blue-100.png), url(../resources/stripes-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (-0.3) should be [url(../resources/stripes-100.png), url(../resources/blue-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0) should be [url(../resources/stripes-100.png), url(../resources/blue-100.png)]
+FAIL CSS Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0.3) should be [cross-fade(url(../resources/stripes-100.png), url(../resources/blue-100.png), 0.3), cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , 0.3 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.3 ) "
+FAIL CSS Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0.6) should be [cross-fade(url(../resources/stripes-100.png), url(../resources/blue-100.png), 0.6), cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , 0.6 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.6 ) "
+PASS CSS Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (1) should be [url(../resources/blue-100.png), url(../resources/stripes-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (1.5) should be [url(../resources/blue-100.png), url(../resources/stripes-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (-0.3) should be [url(../resources/stripes-100.png), url(../resources/blue-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0) should be [url(../resources/stripes-100.png), url(../resources/blue-100.png)]
+FAIL Web Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0.3) should be [cross-fade(url(../resources/stripes-100.png), url(../resources/blue-100.png), 0.3), cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , 0.3 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.3 ) "
+FAIL Web Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (0.6) should be [cross-fade(url(../resources/stripes-100.png), url(../resources/blue-100.png), 0.6), cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , 0.6 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.6 ) "
+PASS Web Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (1) should be [url(../resources/blue-100.png), url(../resources/stripes-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/stripes-100.png), url(../resources/blue-100.png)] to [url(../resources/blue-100.png), url(../resources/stripes-100.png)] at (1.5) should be [url(../resources/blue-100.png), url(../resources/stripes-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png), url(../resources/blue-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.3), cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.3 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.6), cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.6 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png), url(../resources/blue-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.3), cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.3 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.6), cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.6 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png), url(../resources/blue-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.3), cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.3 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.6), cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.6 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png), url(../resources/blue-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png)]
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.3) should be [cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.3), cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.3)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.3 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.3 ) "
+FAIL Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.6) should be [cross-fade(url(../resources/blue-100.png), url(../resources/stripes-100.png), 0.6), cross-fade(url(../resources/blue-100.png), url(../resources/green-100.png), 0.6)] assert_equals: expected "url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) " but got "- webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / stripes - 100.png ) , 0.6 ) , - webkit - cross - fade ( url ( http : / / web - platform.test : 8001 / ... / blue - 100.png ) , url ( http : / / web - platform.test : 8001 / ... / green - 100.png ) , 0.6 ) "
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png)] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (-0.3) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.3) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.6) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (-0.3) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.3) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.6) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Transitions with transition: all: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png), none]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png), none]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.3) should be [url(../resources/blue-100.png), none]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.6) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS CSS Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (-0.3) should be [url(../resources/blue-100.png), none]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0) should be [url(../resources/blue-100.png), none]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.3) should be [url(../resources/blue-100.png), none]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (0.6) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+PASS Web Animations: property <background-image> from [url(../resources/blue-100.png), none] to [url(../resources/stripes-100.png), url(../resources/green-100.png)] at (1.5) should be [url(../resources/stripes-100.png), url(../resources/green-100.png)]
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/background-image-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/background-image-interpolation.html
new file mode 100644
index 0000000..b442c4e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/animations/background-image-interpolation.html
@@ -0,0 +1,181 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<link rel="help" href="https://drafts.csswg.org/css-backgrounds-3/#background-image">
+<meta name="test" content="background-image supports animation">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<style>
+.parent {
+  background-image: url(../resources/blue-100.png);
+  background-size: 0 0;
+}
+.target {
+  width: 100px;
+  height: 100px;
+  display: inline-block;
+  border: 10px solid black;
+  background-repeat: no-repeat;
+  background-image: url(../resources/blue-100.png);
+}
+.expected {
+  border-color: green;
+  margin-right: 2px;
+}
+</style>
+
+<body>
+<script>
+// Neutral to image
+var from = 'url(../resources/blue-100.png)';
+var to = 'url(../resources/green-100.png)';
+test_interpolation({
+  property: 'background-image',
+  from: neutralKeyframe,
+  to: to,
+}, [
+  {at: -0.3, expect: from},
+  {at: 0, expect: from},
+  {at: 0.3, expect: 'cross-fade(' + from + ', ' + to + ', 0.3)'},
+  {at: 0.6, expect: 'cross-fade(' + from + ', ' + to + ', 0.6)'},
+  {at: 1, expect: to},
+  {at: 1.5, expect: to},
+]);
+
+// initial to image
+to = 'url(../resources/green-100.png)';
+test_no_interpolation({
+  property: 'background-image',
+  from: 'initial',
+  to: to,
+});
+
+// inherit to image
+from = 'url(../resources/blue-100.png)';
+to = 'url(../resources/green-100.png)';
+test_interpolation({
+  property: 'background-image',
+  from: 'inherit',
+  to: to,
+}, [
+  {at: -0.3, expect: from},
+  {at: 0, expect: from},
+  {at: 0.3, expect: 'cross-fade(' + from + ', ' + to + ', 0.3)'},
+  {at: 0.6, expect: 'cross-fade(' + from + ', ' + to + ', 0.6)'},
+  {at: 1, expect: to},
+  {at: 1.5, expect: to},
+]);
+
+// unset to image
+test_no_interpolation({
+  property: 'background-image',
+  from: 'unset',
+  to: to,
+});
+
+// Image to image
+from = 'url(../resources/blue-100.png)';
+to = 'url(../resources/green-100.png)';
+test_interpolation({
+  property: 'background-image',
+  from: from,
+  to: to,
+}, [
+  {at: -0.3, expect: from},
+  {at: 0, expect: from},
+  {at: 0.3, expect: 'cross-fade(' + from + ', ' + to + ', 0.3)'},
+  {at: 0.6, expect: 'cross-fade(' + from + ', ' + to + ', 0.6)'},
+  {at: 1, expect: to},
+  {at: 1.5, expect: to},
+]);
+
+// Image to gradient
+from = 'url(../resources/blue-100.png)';
+to = 'linear-gradient(45deg, blue, orange)';
+test_no_interpolation({
+  property: 'background-image',
+  from: from,
+  to: to,
+});
+
+// Image to crossfade
+from = 'url(../resources/blue-100.png)';
+to = 'cross-fade(url(../resources/green-100.png), url(../resources/stripes-100.png), 0.5)';
+test_no_interpolation({
+  property: 'background-image',
+  from: from,
+  to: to,
+});
+
+// Gradient to gradient
+from = 'linear-gradient(-45deg, red, yellow)';
+to = 'linear-gradient(45deg, blue, orange)';
+test_no_interpolation({
+  property: 'background-image',
+  from: from,
+  to: to,
+});
+
+// Keyword to image
+from = 'none';
+to = 'url(../resources/green-100.png)';
+test_no_interpolation({
+  property: 'background-image',
+  from: from,
+  to: to,
+});
+
+// Multiple to multiple
+var fromA = 'url(../resources/stripes-100.png)';
+var fromB = 'url(../resources/blue-100.png)';
+var toA = 'url(../resources/blue-100.png)';
+var toB = 'url(../resources/stripes-100.png)';
+from = fromA + ', ' + fromB;
+to = toA + ', ' + toB;
+test_interpolation({
+  property: 'background-image',
+  from: from,
+  to: to,
+}, [
+  {at: -0.3, expect: from},
+  {at: 0, expect: from},
+  {at: 0.3, expect: 'cross-fade(' + fromA + ', ' + toA + ', 0.3), cross-fade(' + fromB + ', ' + toB + ', 0.3)'},
+  {at: 0.6, expect: 'cross-fade(' + fromA + ', ' + toA + ', 0.6), cross-fade(' + fromB + ', ' + toB + ', 0.6)'},
+  {at: 1, expect: to},
+  {at: 1.5, expect: to},
+]);
+
+// Single to multiple
+from = 'url(../resources/blue-100.png)';
+var toA = 'url(../resources/stripes-100.png)';
+var toB = 'url(../resources/green-100.png)';
+to = toA + ', ' + toB;
+test_interpolation({
+  property: 'background-image',
+  from: from,
+  to: to,
+}, [
+  // The interpolation of different numbers of background-images looks a bit strange here.
+  // Animating background-image is not specified to be possible however we do it for backwards compatibility.
+  // With this in mind we kept the implementation simple at the expense of this corner case because there is
+  // no official specification to support.
+  {at: -0.3, expect: from + ', ' + from},
+  {at: 0, expect: from},
+  {at: 0.3, expect: 'cross-fade(' + from + ', ' + toA + ', 0.3), cross-fade(' + from + ', ' + toB + ', 0.3)'},
+  {at: 0.6, expect: 'cross-fade(' + from + ', ' + toA + ', 0.6), cross-fade(' + from + ', ' + toB + ', 0.6)'},
+  {at: 1, expect: to},
+  {at: 1.5, expect: to},
+]);
+
+// Multiple mismatched types
+from = 'url(../resources/blue-100.png), none';
+to = 'url(../resources/stripes-100.png), url(../resources/green-100.png)';
+test_no_interpolation({
+  property: 'background-image',
+  from: from,
+  to: to,
+});
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/resources/blue-100.png b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/resources/blue-100.png
new file mode 100644
index 0000000..f578ae72
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/resources/blue-100.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/resources/green-100.png b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/resources/green-100.png
new file mode 100644
index 0000000..e45bbbe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/resources/green-100.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-backgrounds/resources/stripes-100.png b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/resources/stripes-100.png
new file mode 100644
index 0000000..cfb69804
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-backgrounds/resources/stripes-100.png
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/percentage-resolution-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/percentage-resolution-001-ref.html
new file mode 100644
index 0000000..a4e7f1e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/percentage-resolution-001-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Sizing: Test that percentages don't resolve against min-content</title>
+<link rel="author" title="Google Inc." href="https://www.google.com/" />
+
+<style>
+.inner {
+  background-color: green;
+}
+</style>
+
+<body>
+  <div class="outer">
+    <div class="inner">This should be a single-line green background with no red visible.</div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/percentage-resolution-001.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/percentage-resolution-001.html
new file mode 100644
index 0000000..b2cc727
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/percentage-resolution-001.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Sizing: Test that percentages don't resolve against min-content</title>
+<link rel="help" href="https://drafts.csswg.org/css-sizing-3/#percentage-sizing" />
+<link rel="author" title="Google Inc." href="https://www.google.com/" />
+<link rel="match" href="percentage-resolution-001-ref.html" />
+
+<style>
+.outer {
+  min-height: min-content;
+  max-height: min-content;
+  height: 500px;
+  background-color: red;
+}
+
+.inner {
+  height: 50%;
+  background-color: green;
+}
+</style>
+
+<body>
+  <div class="outer">
+    <div class="inner">This should be a single-line green background with no red visible.</div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/animations/vertical-align-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/animations/vertical-align-interpolation.html
index d8807b1..c81c832 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/animations/vertical-align-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/animations/vertical-align-interpolation.html
@@ -7,6 +7,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/interpolation-testcommon.js"></script>
+
 <style>
 .parent {
   vertical-align: 100px;
@@ -22,8 +23,8 @@
   background-color: green;
 }
 </style>
-<body>
 
+<body>
 <script>
 test_interpolation({
   property: 'vertical-align',
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/commands.json b/third_party/blink/web_tests/external/wpt/tools/ci/commands.json
index 841fd85..c9cd7c4 100644
--- a/third_party/blink/web_tests/external/wpt/tools/ci/commands.json
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/commands.json
@@ -14,7 +14,7 @@
     "virtualenv": false
   },
   "tc-download": {
-    "path": "tcdownload.py",
+    "path": "tc/download.py",
     "script": "run",
     "parser": "get_parser",
     "parse_known": true,
@@ -24,5 +24,27 @@
       "requests",
       "pygithub"
     ]
+  },
+  "tc-taskgraph": {
+    "path": "tc/taskgraph.py",
+    "script": "run",
+    "help": "Build the taskgraph",
+    "virtualenv": true,
+    "install": [
+      "requests",
+      "pyyaml"
+    ]
+  },
+  "tc-decision": {
+    "path": "tc/decision.py",
+    "parser": "get_parser",
+    "script": "run",
+    "help": "Run the decision task",
+    "virtualenv": true,
+    "install": [
+        "requests",
+        "pyyaml",
+        "taskcluster"
+    ]
   }
 }
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 be7dbe0..da4e18ae 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
@@ -38,7 +38,6 @@
 import argparse
 import json
 import os
-import re
 import subprocess
 import sys
 import tempfile
@@ -99,8 +98,12 @@
                    help="Start xvfb")
     p.add_argument("--checkout",
                    help="Revision to checkout before starting job")
-    p.add_argument("job",
-                   help="Name of the job associated with the current event")
+    p.add_argument("--install-certificates", action="store_true", default=None,
+                   help="Install web-platform.test certificates to UA store")
+    p.add_argument("--no-install-certificates", action="store_false", default=None,
+                   help="Don't install web-platform.test certificates to UA store")
+    p.add_argument("--rev",
+                   help="Revision that the task_head ref is expected to point to")
     p.add_argument("script",
                    help="Script to run for the job")
     p.add_argument("script_args",
@@ -123,6 +126,12 @@
     subprocess.check_call(["git", "checkout", "--quiet", rev])
 
 
+def install_certificates():
+    subprocess.check_call(["sudo", "cp", "tools/certs/cacert.pem",
+                           "/usr/local/share/ca-certificates/cacert.crt"])
+    subprocess.check_call(["sudo", "update-ca-certificates"])
+
+
 def install_chrome(channel):
     if channel in ("experimental", "dev", "nightly"):
         deb_archive = "google-chrome-unstable_current_amd64.deb"
@@ -213,29 +222,6 @@
     start(["sudo", "fluxbox", "-display", os.environ["DISPLAY"]])
 
 
-def get_extra_jobs(event):
-    body = None
-    jobs = set()
-    if "commits" in event and event["commits"]:
-        body = event["commits"][0]["message"]
-    elif "pull_request" in event:
-        body = event["pull_request"]["body"]
-
-    if not body:
-        return jobs
-
-    regexp = re.compile(r"\s*tc-jobs:(.*)$")
-
-    for line in body.splitlines():
-        m = regexp.match(line)
-        if m:
-            items = m.group(1)
-            for item in items.split(","):
-                jobs.add(item.strip())
-            break
-    return jobs
-
-
 def set_variables(event):
     # Set some variables that we use to get the commits on the current branch
     ref_prefix = "refs/heads/"
@@ -256,23 +242,13 @@
         os.environ["GITHUB_BRANCH"] = branch
 
 
-def include_job(job):
-    # Special case things that unconditionally run on pushes,
-    # assuming a higher layer is filtering the required list of branches
-    if (os.environ["GITHUB_PULL_REQUEST"] == "false" and
-        job == "run-all"):
-        return True
-
-    jobs_str = run([os.path.join(root, "wpt"),
-                    "test-jobs"], return_stdout=True)
-    print(jobs_str)
-    return job in set(jobs_str.splitlines())
-
-
 def setup_environment(args):
     if args.hosts_file:
         make_hosts_file()
 
+    if args.install_certificates:
+        install_certificates()
+
     if "chrome" in args.browser:
         assert args.channel is not None
         install_chrome(args.channel)
@@ -340,6 +316,13 @@
 def main():
     args = get_parser().parse_args()
 
+    if args.rev is not None:
+        task_head = subprocess.check_output(["git", "rev-parse", "task_head"]).strip()
+        if task_head != args.rev:
+            print("CRITICAL: task_head points at %s, expected %s. "
+                  "This may be because the branch was updated" % (task_head, args.rev))
+            sys.exit(1)
+
     if "TASK_EVENT" in os.environ:
         event = json.loads(os.environ["TASK_EVENT"])
     else:
@@ -350,25 +333,6 @@
 
     setup_repository()
 
-    extra_jobs = get_extra_jobs(event)
-
-    job = args.job
-
-    print("Job %s" % job)
-
-    run_if = [(lambda: job == "all", "job set to 'all'"),
-              (lambda:"all" in extra_jobs, "Manually specified jobs includes 'all'"),
-              (lambda:job in extra_jobs, "Manually specified jobs includes '%s'" % job),
-              (lambda:include_job(job), "CI required jobs includes '%s'" % job)]
-
-    for fn, msg in run_if:
-        if fn():
-            print(msg)
-            break
-    else:
-        print("Job not scheduled for this push")
-        return
-
     # Run the job
     setup_environment(args)
     os.chdir(root)
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/tc/README.md b/third_party/blink/web_tests/external/wpt/tools/ci/tc/README.md
new file mode 100644
index 0000000..11f367f9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/tc/README.md
@@ -0,0 +1,235 @@
+# Taskgraph Setup
+
+The taskgraph is built from a YAML file. This file has two top-level
+properties: `components` and `tasks`. The full list of tasks is
+defined by the `tasks` object; each task is an object with a single
+property representing the task with the corresponding value an object
+representing the task properties. Each task requires the following
+top-level properties:
+
+* `provisionerId`: String. Name of Taskcluster provisioner
+* `schedulerId`: String. Name of Taskcluster scheduler
+* `deadline`: String. Time until the task expires
+* `image`: String. Name of docker image to use for task
+* `maxRunTime`: Number. Maximum time in seconds for which the task can
+  run.
+* `artifacts`: Object. List of artifacts and directories to upload; see
+  Taskcluster documentation.
+* `command`: String. Command to run. This is automatically wrapped in a
+  run_tc command
+* `options`: Optional Object. Options to pass into run_tc
+  - xvfb: Boolean. Enable Xvfb for run
+  - oom-killer: Boolean. Enable xvfb for run
+  - hosts: Boolean. Update hosts file with wpt hosts before run
+  - install-certificates: Boolean. Install wpt certs into OS
+    certificate store for run
+  - browser: List. List of browser names for run
+  - channel: String. Browser channel for run
+* `trigger`: Object. Conditions on which to consider task. One or more
+  of following properties:
+  - branch: List. List of branch names on which to trigger.
+  - pull-request: No value. Trigger for pull request actions
+* `schedule-if`: Optional Object. Conditions on which task should be
+  scheduled given it meets the trigger conditions.
+  - `run-job`: List. Job names for which this task should be considered,
+    matching the output from `./wpt test-jobs`
+* `env`: Optional Object. Environment variables to set when running task.
+* `depends-on`: Optional list. List of task names that must be complete
+  before the current task is scheduled.
+* `description`: String. Task description.
+* `name`: Optional String. Name to use for the task overriding the
+  property name. This is useful in combination with substitutions
+  described below.
+
+## Task Expansions
+
+Using the above syntax it's possble to describe each task
+directly. But typically in a taskgraph there are many common
+properties between tasks so it's tedious and error prone to repeat
+information that's common to multiple tasks. Therefore the taskgraph
+format provides several mechanisms to reuse partial task definitions
+across multiple tasks.
+
+### Components
+
+The other top-level property in the taskgraph format is
+`components`. The value of this property is an object containing named
+partial task definitions. Each task definition may contain a property called
+`use` which is a list of components to use as the basis for the task
+definition. The components list is evaluated in order. If a property
+is not previously defined in the output it is added to the output. If
+it was previously defined, the value is updated according to the type:
+ * Strings and numbers are replaced with a new value
+ * Lists are extended with the additional values
+ * Objects are updated recursively following the above rules
+This means that types must always match between components and the
+final value.
+
+For example
+```
+components:
+  example-1:
+    list_prop:
+      - first
+      - second
+    object_prop:
+      key1: value1
+      key2: base_value
+  example-2:
+    list_prop:
+      - third
+      - fourth
+    object_prop:
+      key3:
+        - value3-1
+
+tasks:
+  - example-task:
+      use:
+        - example-1
+        - example-2
+      object_prop:
+        key2: value2
+        key3:
+          - value3-2
+```
+
+will evaluate to the following task:
+
+```
+example-task:
+  list_prop:
+    - first
+    - second
+    - third
+    - fourth
+  object_prop:
+    key1: value1
+    key2: value2
+    key3:
+      - value3-1
+      - value3-2
+```
+
+Note that components cannot currently define `use` properties of their own.
+
+## Substitutions
+
+Components and tasks can define a property `vars` that holds variables
+which are later substituted into the task definition using the syntax
+`${vars.property-name}`. For example:
+
+```
+components:
+  generic-component:
+    prop: ${vars.value}
+
+tasks:
+  - first:
+      use:
+        - generic-component
+      vars:
+        value: value1
+  - second:
+      use:
+        - generic-component
+      vars:
+        value: value2
+```
+
+Results in the following tasks:
+
+```
+first:
+  prop: value1
+second:
+  prop: value2
+```
+
+## Maps
+
+Instead of defining a task directly, an item in the tasks property may
+be an object with a single property `$map`. This object itself has two
+child properties; `for` and `do`. The value of `for` is a list of
+objects, and the value of `do` is either an object or a list of
+objects. For each object in the `for` property, a set of tasks is
+created by taking a copy of that object for each task in the `do`
+property, updating the object with the properties from the
+corresponding `do` object, using the same rules as for components
+above, and then processing as for a normal task. `$map` rules can also
+be nested.
+
+Note: Although `$map` shares a name with the `$map` used in json-e
+(used. in `.taskcluster.yml`), the semantics are different.
+
+For example
+
+```
+components: {}
+tasks:
+  $map:
+    for:
+      - vars:
+          example: value1
+      - vars:
+          example: value2
+    do:
+      example-${vars.example}
+        prop: ${vars.example}
+```
+
+Results in the tasks
+
+```
+example-value1:
+  prop: value1
+example-value2:
+  prop: value2
+```
+
+Note that in combination with `$map`, variable substitutions are
+applied *twice*; once after the `$map` is evaluated and once after the
+`use` statements are evaluated.
+
+## Chunks
+
+A common requirements for tasks is that they are "chunked" into N
+partial tasks. This is handled specially in the syntax. A top level
+property `chunks` can be used to define the number of individual
+chunks to create for a specific task. Each chunked task is created
+with a `chunks` property set to an object containing an `id` property
+containing the one-based index of the chunk an a `total` property
+containing the total number of chunks. These can be substituted into
+the task definition using the same syntax as for `vars` above
+e.g. `${chunks.id}`. Note that because task names must be unique, it's
+common to specify a `name` property on the task that will override the
+property name e.g.
+
+```
+components: {}
+tasks:
+  - chunked-task:
+      chunks:2
+      command: "task-run --chunk=${chunks.id} --totalChunks=${chunks.total}"
+      name: task-chunk-${chunks.id}
+```
+
+creates tasks:
+
+```
+task-chunk-1:
+  command: "task-run --chunk=1 --totalChunks=2"
+task-chunk-2:
+  command: "task-run --chunk=2 --totalChunks=2"
+```
+
+# Overall processing model
+
+The overall processing model for tasks is as follows:
+ * Evaluate maps
+ * Perform subsitutions
+ * Evaluate use statements
+ * Expand chunks
+ * Perform subsitutions
+
+At each point after maps are evaluated tasks must have a unique name.
diff --git a/third_party/blink/web_tests/external/wpt/tools/taskcluster/__init__.py b/third_party/blink/web_tests/external/wpt/tools/ci/tc/__init__.py
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/tools/taskcluster/__init__.py
rename to third_party/blink/web_tests/external/wpt/tools/ci/tc/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/tc/decision.py b/third_party/blink/web_tests/external/wpt/tools/ci/tc/decision.py
new file mode 100644
index 0000000..f3ef34b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/tc/decision.py
@@ -0,0 +1,314 @@
+import argparse
+import json
+import logging
+import os
+import re
+import subprocess
+from collections import OrderedDict
+
+import taskcluster
+from six import iteritems, itervalues
+
+from . import taskgraph
+
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+
+logging.basicConfig()
+logger = logging.getLogger()
+
+
+def get_triggers(event):
+    # Set some variables that we use to get the commits on the current branch
+    ref_prefix = "refs/heads/"
+    is_pr = "pull_request" in event
+    branch = None
+    if not is_pr and "ref" in event:
+        branch = event["ref"]
+        if branch.startswith(ref_prefix):
+            branch = branch[len(ref_prefix):]
+
+    return is_pr, branch
+
+
+def fetch_event_data(queue):
+    try:
+        task_id = os.environ["TASK_ID"]
+    except KeyError:
+        logger.warning("Missing TASK_ID environment variable")
+        # For example under local testing
+        return None
+
+    task_data = queue.task(task_id)
+
+    return task_data.get("extra", {}).get("github_event")
+
+
+def filter_triggers(event, all_tasks):
+    is_pr, branch = get_triggers(event)
+    triggered = {}
+    for name, task in iteritems(all_tasks):
+        if "trigger" in task:
+            if is_pr and "pull-request" in task["trigger"]:
+                triggered[name] = task
+            elif branch is not None and "branch" in task["trigger"]:
+                for trigger_branch in task["trigger"]["branch"]:
+                    if (trigger_branch == branch or
+                        trigger_branch.endswith("*") and branch.startswith(trigger_branch[:-1])):
+                        triggered[name] = task
+    logger.info("Triggers match tasks:\n * %s" % "\n * ".join(triggered.keys()))
+    return triggered
+
+
+def get_run_jobs(event):
+    from tools.ci import jobs
+    revish = "%s..%s" % (event["pull_request"]["base"]["sha"]
+                         if "pull_request" in event
+                         else event["before"],
+                         event["after"])
+    logger.info("Looking for changes in range %s" % revish)
+    paths = jobs.get_paths(revish=revish)
+    logger.info("Found changes in paths:%s" % "\n".join(paths))
+    path_jobs = jobs.get_jobs(paths)
+    all_jobs = path_jobs | get_extra_jobs(event)
+    logger.info("Including jobs:\n * %s" % "\n * ".join(all_jobs))
+    return all_jobs
+
+
+def get_extra_jobs(event):
+    body = None
+    jobs = set()
+    if "commits" in event and event["commits"]:
+        body = event["commits"][0]["message"]
+    elif "pull_request" in event:
+        body = event["pull_request"]["body"]
+
+    if not body:
+        return jobs
+
+    regexp = re.compile(r"\s*tc-jobs:(.*)$")
+
+    for line in body.splitlines():
+        m = regexp.match(line)
+        if m:
+            items = m.group(1)
+            for item in items.split(","):
+                jobs.add(item.strip())
+            break
+    return jobs
+
+
+def filter_schedule_if(event, tasks):
+    scheduled = {}
+    run_jobs = None
+    for name, task in iteritems(tasks):
+        if "schedule-if" in task:
+            if "run-job" in task["schedule-if"]:
+                if run_jobs is None:
+                    run_jobs = get_run_jobs(event)
+                if "all" in run_jobs or any(item in run_jobs for item in task["schedule-if"]["run-job"]):
+                    scheduled[name] = task
+        else:
+            scheduled[name] = task
+    logger.info("Scheduling rules match tasks:\n * %s" % "\n * ".join(scheduled.keys()))
+    return scheduled
+
+
+def get_fetch_rev(event):
+    is_pr, _ = get_triggers(event)
+    if is_pr:
+        # Try to get the actual rev so that all non-decision tasks are pinned to that
+        ref = "refs/pull/%s/merge" % event["pull_request"]["number"]
+        try:
+            output = subprocess.check_output(["git", "ls-remote", "origin", ref])
+        except subprocess.CalledProcessError:
+            import traceback
+            logger.error(traceback.format_exc())
+            logger.error("Failed to get merge commit sha1")
+            return ref, None
+        if not output:
+            logger.error("Failed to get merge commit")
+            return ref, None
+        return ref, output.split()[0]
+    else:
+        return event["ref"], event["after"]
+
+
+def build_full_command(event, task):
+    fetch_ref, fetch_sha = get_fetch_rev(event)
+    cmd_args = {
+        "task_name": task["name"],
+        "repo_url": event["repository"]["clone_url"],
+        "fetch_ref": fetch_ref,
+        "task_cmd": task["command"],
+        "install_str": "",
+    }
+
+    options = task.get("options", {})
+    options_args = []
+    if fetch_sha is not None:
+        options_args.append("--rev=%s" % fetch_sha)
+    if options.get("oom-killer"):
+        options_args.append("--oom-killer")
+    if options.get("xvfb"):
+        options_args.append("--xvfb")
+    if not options.get("hosts"):
+        options_args.append("--no-hosts")
+    else:
+        options_args.append("--hosts")
+    if options.get("checkout"):
+        options_args.append("--checkout=%s" % options["checkout"])
+    for browser in options.get("browser", []):
+        options_args.append("--browser=%s" % browser)
+    if options.get("channel"):
+        options_args.append("--channel=%s" % options["channel"])
+    if options.get("install-certificates"):
+        options_args.append("--install-certificates")
+
+    cmd_args["options_str"] = " ".join(str(item) for item in options_args)
+
+    install_packages = task.get("install")
+    if install_packages:
+        install_items = ["apt update -qqy"]
+        install_items.extend("apt install -qqy %s" % item
+                             for item in install_packages)
+        cmd_args["install_str"] = "\n".join("sudo %s;" % item for item in install_items)
+
+    return ["/bin/bash",
+            "--login",
+            "-c",
+            """
+~/start.sh \
+  %(repo_url)s \
+  %(fetch_ref)s;
+%(install_str)s
+cd web-platform-tests;
+./tools/ci/run_tc.py %(options_str)s -- %(task_cmd)s;
+""" % cmd_args]
+
+
+def get_owner(event):
+    pusher = event.get("pusher", {}).get("email", "")
+    if "@" in pusher:
+        return pusher
+    return "web-platform-tests@users.noreply.github.com"
+
+
+def create_tc_task(event, task, taskgroup_id, depends_on_ids):
+    command = build_full_command(event, task)
+    task_id = taskcluster.slugId()
+    task_data = {
+        "taskGroupId": taskgroup_id,
+        "created": taskcluster.fromNowJSON(""),
+        "deadline": taskcluster.fromNowJSON(task["deadline"]),
+        "provisionerId": task["provisionerId"],
+        "schedulerId": task["schedulerId"],
+        "workerType": task["workerType"],
+        "metadata": {
+            "name": task["name"],
+            "description": task.get("description", ""),
+            "owner": get_owner(event),
+            "source": event["repository"]["clone_url"]
+        },
+        "payload": {
+            "artifacts": task.get("artifacts"),
+            "command": command,
+            "image": task.get("image"),
+            "maxRunTime": task.get("maxRunTime"),
+            "env": task.get("env", {}),
+        },
+        "extra": {
+            "github_event": json.dumps(event)
+        }
+    }
+    if depends_on_ids:
+        task_data["dependencies"] = depends_on_ids
+        task_data["requires"] = "all-completed"
+    return task_id, task_data
+
+
+def build_task_graph(event, all_tasks, tasks):
+    task_id_map = OrderedDict()
+    taskgroup_id = os.environ.get("TASK_ID", taskcluster.slugId())
+
+    def add_task(task_name, task):
+        depends_on_ids = []
+        if "depends-on" in task:
+            for depends_name in task["depends-on"]:
+                if depends_name not in task_id_map:
+                    add_task(depends_name,
+                             all_tasks[depends_name])
+                depends_on_ids.append(task_id_map[depends_name][0])
+        task_id, task_data = create_tc_task(event, task, taskgroup_id, depends_on_ids)
+        task_id_map[task_name] = (task_id, task_data)
+
+    for task_name, task in iteritems(tasks):
+        add_task(task_name, task)
+
+    return task_id_map
+
+
+def create_tasks(queue, task_id_map):
+    for (task_id, task_data) in itervalues(task_id_map):
+        queue.createTask(task_id, task_data)
+
+
+def get_event(queue, event_path):
+    if event_path is not None:
+        try:
+            with open(event_path) as f:
+                event_str = f.read()
+        except IOError:
+            logger.error("Missing event file at path %s" % event_path)
+            raise
+    elif "TASK_EVENT" in os.environ:
+        event_str = os.environ["TASK_EVENT"]
+    else:
+        event_str = fetch_event_data(queue)
+    if not event_str:
+        raise ValueError("Can't find GitHub event definition; for local testing pass --event-path")
+    try:
+        return json.loads(event_str)
+    except ValueError:
+        logger.error("Event was not valid JSON")
+        raise
+
+
+def decide(event):
+    all_tasks = taskgraph.load_tasks_from_path(os.path.join(here, "tasks", "test.yml"))
+
+    triggered_tasks = filter_triggers(event, all_tasks)
+    scheduled_tasks = filter_schedule_if(event, triggered_tasks)
+
+    task_id_map = build_task_graph(event, all_tasks, scheduled_tasks)
+    return task_id_map
+
+
+def get_parser():
+    parser = argparse.ArgumentParser()
+    parser.add_argument("--event-path",
+                        help="Path to file containing serialized GitHub event")
+    parser.add_argument("--dry-run", action="store_true",
+                        help="Don't actually create the tasks, just output the tasks that "
+                        "would be created")
+    parser.add_argument("--tasks-path",
+                        help="Path to file in which to write payload for all scheduled tasks")
+    return parser
+
+
+def run(venv, **kwargs):
+    queue = taskcluster.Queue({'rootUrl': os.environ['TASKCLUSTER_PROXY_URL']})
+    event = get_event(queue, event_path=kwargs["event_path"])
+
+    task_id_map = decide(event)
+
+    try:
+        if not kwargs["dry_run"]:
+            create_tasks(queue, task_id_map)
+        else:
+            print(json.dumps(task_id_map, indent=2))
+    finally:
+        if kwargs["tasks_path"]:
+            with open(kwargs["tasks_path"], "w") as f:
+                json.dump(task_id_map, f, indent=2)
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/tcdownload.py b/third_party/blink/web_tests/external/wpt/tools/ci/tc/download.py
similarity index 99%
rename from third_party/blink/web_tests/external/wpt/tools/ci/tcdownload.py
rename to third_party/blink/web_tests/external/wpt/tools/ci/tc/download.py
index 256726d..359ec33 100644
--- a/third_party/blink/web_tests/external/wpt/tools/ci/tcdownload.py
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/tc/download.py
@@ -15,6 +15,7 @@
 # be https://community-tc.services.mozilla.com)
 TASKCLUSTER_ROOT_URL = 'https://taskcluster.net'
 
+
 def get_parser():
     parser = argparse.ArgumentParser()
     parser.add_argument("--ref", action="store", default="master",
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/tc/taskgraph.py b/third_party/blink/web_tests/external/wpt/tools/ci/tc/taskgraph.py
new file mode 100644
index 0000000..6a6cb49
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/tc/taskgraph.py
@@ -0,0 +1,170 @@
+import json
+import os
+import re
+from copy import deepcopy
+
+import six
+import yaml
+from six import iteritems
+
+here = os.path.dirname(__file__)
+
+
+def first(iterable):
+    # First item from a list or iterator
+    if not hasattr(iterable, "next"):
+        if hasattr(iterable, "__iter__"):
+            iterable = iter(iterable)
+        else:
+            raise ValueError("Object isn't iterable")
+    return next(iterable)
+
+
+def load_task_file(path):
+    with open(path) as f:
+        return yaml.safe_load(f)
+
+
+def update_recursive(data, update_data):
+    for key, value in iteritems(update_data):
+        if key not in data:
+            data[key] = value
+        else:
+            initial_value = data[key]
+            if isinstance(value, dict):
+                if not isinstance(initial_value, dict):
+                    raise ValueError("Variable %s has inconsistent types "
+                                     "(expected object)" % key)
+                update_recursive(initial_value, value)
+            elif isinstance(value, list):
+                if not isinstance(initial_value, list):
+                    raise ValueError("Variable %s has inconsistent types "
+                                     "(expected list)" % key)
+                initial_value.extend(value)
+            else:
+                data[key] = value
+
+
+def resolve_use(task_data, templates):
+    rv = {}
+    if "use" in task_data:
+        for template_name in task_data["use"]:
+            update_recursive(rv, deepcopy(templates[template_name]))
+    update_recursive(rv, task_data)
+    rv.pop("use", None)
+    return rv
+
+
+def resolve_name(task_data, default_name):
+    if "name" not in task_data:
+        task_data["name"] = default_name
+    return task_data
+
+
+def resolve_chunks(task_data):
+    if "chunks" not in task_data:
+        return [task_data]
+    rv = []
+    total_chunks = task_data["chunks"]
+    for i in range(1, total_chunks + 1):
+        chunk_data = deepcopy(task_data)
+        chunk_data["chunks"] = {"id": i,
+                                "total": total_chunks}
+        rv.append(chunk_data)
+    return rv
+
+
+def replace_vars(input_string, variables):
+    # TODO: support replacing as a non-string type?
+    variable_re = re.compile(r"(?<!\\)\${([^}]+)}")
+
+    def replacer(m):
+        var = m.group(1).split(".")
+        repl = variables
+        for part in var:
+            try:
+                repl = repl[part]
+            except Exception:
+                # Don't substitute
+                return m.group(0)
+        return str(repl)
+
+    return variable_re.sub(replacer, input_string)
+
+
+def sub_variables(data, variables):
+    if isinstance(data, six.string_types):
+        return replace_vars(data, variables)
+    if isinstance(data, list):
+        return [sub_variables(item, variables) for item in data]
+    if isinstance(data, dict):
+        return {key: sub_variables(value, variables)
+                for key, value in iteritems(data)}
+    return data
+
+
+def substitute_variables(task):
+    variables = {"vars": task.get("vars", {}),
+                 "chunks": task.get("chunks", {})}
+
+    return sub_variables(task, variables)
+
+
+def expand_maps(task):
+    name = first(task.keys())
+    if name != "$map":
+        return [task]
+
+    map_data = task["$map"]
+    if set(map_data.keys()) != set(["for", "do"]):
+        raise ValueError("$map objects must have exactly two properties named 'for' "
+                         "and 'do' (got %s)" % ("no properties" if not map_data.keys()
+                                                else ", ". join(map_data.keys())))
+    rv = []
+    for for_data in map_data["for"]:
+        do_items = map_data["do"]
+        if not isinstance(do_items, list):
+            do_items = expand_maps(do_items)
+        for do_data in do_items:
+            task_data = deepcopy(for_data)
+            if len(do_data.keys()) != 1:
+                raise ValueError("Each item in the 'do' list must be an object "
+                                 "with a single property")
+            name = first(do_data.keys())
+            update_recursive(task_data, deepcopy(do_data[name]))
+            rv.append({name: task_data})
+    return rv
+
+
+def load_tasks(tasks_data):
+    map_resolved_tasks = {}
+    tasks = []
+
+    for task in tasks_data["tasks"]:
+        if len(task.keys()) != 1:
+            raise ValueError("Each task must be an object with a single property")
+        for task in expand_maps(task):
+            if len(task.keys()) != 1:
+                raise ValueError("Each task must be an object with a single property")
+            name = first(task.keys())
+            data = task[name]
+            new_name = sub_variables(name, {"vars": data.get("vars", {})})
+            if new_name in map_resolved_tasks:
+                raise ValueError("Got duplicate task name %s" % new_name)
+            map_resolved_tasks[new_name] = substitute_variables(data)
+
+    for task_default_name, data in iteritems(map_resolved_tasks):
+        task = resolve_use(data, tasks_data["components"])
+        task = resolve_name(task, task_default_name)
+        tasks.extend(resolve_chunks(task))
+
+    tasks = [substitute_variables(task_data) for task_data in tasks]
+    return {task["name"]: task for task in tasks}
+
+
+def load_tasks_from_path(path):
+    return load_tasks(load_task_file(path))
+
+
+def run(venv, **kwargs):
+    print(json.dumps(load_tasks_from_path(os.path.join(here, "tasks", "test.yml")), indent=2))
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/tc/tasks/test.yml b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tasks/test.yml
new file mode 100644
index 0000000..3a71ab3a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tasks/test.yml
@@ -0,0 +1,354 @@
+components:
+  wpt-base:
+    provisionerId: proj-wpt
+    workerType: ci
+    schedulerId: taskcluster-github
+    deadline: "24 hours"
+    image: harjgam/web-platform-tests:0.33
+    maxRunTime: 7200
+    artifacts:
+      public/results:
+        path: /home/test/artifacts
+        type: directory
+
+  wpt-testharness:
+    chunks: 16
+    vars:
+      test-type: testharness
+
+  wpt-reftest:
+    chunks: 5
+    vars:
+      test-type: reftest
+
+  wpt-wdspec:
+    chunks: 1
+    vars:
+      test-type: wdspec
+
+  run-options:
+    options:
+      xvfb: true
+      oom-killer: true
+      hosts: true
+      install-certificates: true
+
+  wpt-run:
+    name: wpt-${vars.browser}-${vars.channel}-${vars.suite}-chunk-${chunks.id}
+    options:
+      browser:
+        - ${vars.browser}
+      channel: ${vars.channel}
+    command: >-
+      ./tools/ci/taskcluster-run.py
+      ${vars.browser}
+      --
+      --channel=${vars.channel}
+      --log-wptreport=../artifacts/wpt_report.json
+      --log-wptscreenshot=../artifacts/wpt_screenshot.txt
+      --no-fail-on-unexpected
+      --this-chunk=${chunks.id}
+      --total-chunks=${chunks.total}
+
+  trigger-master:
+    trigger:
+      branch:
+        - master
+
+  trigger-push:
+    trigger:
+      branch:
+        - triggers/${vars.browser}_${vars.channel}
+
+  trigger-daily:
+    trigger:
+      branch:
+        - epochs/daily
+
+  trigger-weekly:
+    trigger:
+      branch:
+        - epochs/weekly
+
+  trigger-pr:
+    trigger:
+      pull-request:
+
+  browser-firefox:
+    depends-on:
+      - download-firefox-${vars.channel}
+
+  browser-webkitgtk_minibrowser: {}
+
+  browser-chrome: {}
+
+  tox-python2:
+    env:
+      TOXENV: py27
+      PY_COLORS: 0
+
+  tox-python3:
+    env:
+      TOXENV: py36
+      PY_COLORS: 0
+    install:
+      - python3-pip
+
+tasks:
+  # Run full suites on push
+  - $map:
+      for:
+        - vars:
+            suite: testharness
+        - vars:
+            suite: reftest
+        - vars:
+            suite: wdspec
+      do:
+        $map:
+          for:
+            - vars:
+                browser: firefox
+                channel: nightly
+              use:
+                - trigger-master
+                - trigger-push
+            - vars:
+                browser: firefox
+                channel: beta
+              use:
+                - trigger-weekly
+                - trigger-push
+            - vars:
+                browser: firefox
+                channel: stable
+              use:
+                - trigger-daily
+                - trigger-push
+            - vars:
+                browser: chrome
+                channel: dev
+              use:
+                - trigger-master
+                - trigger-push
+            - vars:
+                browser: chrome
+                channel: beta
+              use:
+                - trigger-weekly
+                - trigger-push
+            - vars:
+                browser: chrome
+                channel: stable
+              use:
+                - trigger-daily
+                - trigger-push
+            - vars:
+                browser: webkitgtk_minibrowser
+                channel: nightly
+              use:
+                - trigger-daily
+                - trigger-push
+            - vars:
+                browser: webkitgtk_minibrowser
+                channel: stable
+              use:
+                - trigger-weekly
+                - trigger-push
+
+          do:
+            - ${vars.browser}-${vars.channel}-${vars.suite}:
+                use:
+                  - wpt-base
+                  - run-options
+                  - wpt-run
+                  - browser-${vars.browser}
+                  - wpt-${vars.suite}
+                description: >-
+                  A subset of WPT's "${vars.suite}" tests (chunk number ${chunks.id}
+                  of ${chunks.total}), run in the ${vars.channel} release of
+                  ${vars.browser}.
+
+  - $map:
+      for:
+        - vars:
+            browser: firefox
+            channel: nightly
+        - vars:
+            browser: chrome
+            channel: dev
+      do:
+        - wpt-${vars.browser}-${vars.channel}-stability:
+            use:
+              - wpt-base
+              - browser-${vars.browser}
+            description: >-
+              Verify that all tests affected by a pull request are stable
+              when executed in ${vars.browser}.
+            command: >-
+              ./tools/ci/taskcluster-run.py
+              --commit-range base_head
+              ${vars.browser}
+              --
+              --channel=${vars.channel}
+              --verify
+
+        - wpt-${vars.browser}-${vars.channel}-results:
+            use:
+              - wpt-base
+              - run-options
+              - browser-${vars.browser}
+            description: >-
+              Collect results for all tests affected by a pull request in
+              ${vars.browser}.
+            command: >-
+              ./tools/ci/taskcluster-run.py
+              --commit-range base_head
+              ${vars.browser}
+              --
+              --channel=${vars.channel}
+              --no-fail-on-unexpected
+              --log-wptreport=../artifacts/wpt_report.json
+              --log-wptscreenshot=../artifacts/wpt_screenshot.txt
+
+        - wpt-${vars.browser}-${vars.channel}-results-without-changes:
+            use:
+              - wpt-base
+              - run-options
+              - browser-${vars.browser}
+            options:
+              checkout: base_head
+            description: >-
+              Collect results for all tests affected by a pull request in
+              ${vars.browser} but without the changes in the PR.
+            command: >-
+              ./tools/ci/taskcluster-run.py
+              --commit-range task_head
+              ${vars.browser}
+              --
+              --channel=${vars.channel}
+              --no-fail-on-unexpected
+              --log-wptreport=../artifacts/wpt_report.json
+              --log-wptscreenshot=../artifacts/wpt_screenshot.txt
+  - $map:
+      for:
+        - vars:
+            channel: nightly
+        - vars:
+            channel: beta
+        - vars:
+            channel: stable
+      do:
+        download-firefox-${vars.channel}:
+          use:
+            - wpt-base
+          command: "./wpt install --download-only --destination /home/test/artifacts/ --channel=${vars.channel} firefox browser"
+
+  - lint:
+      use:
+        - wpt-base
+        - trigger-master
+        - trigger-pr
+      description: >-
+        Lint for wpt-specific requirements
+      command: "./wpt lint --all"
+
+  - update-built:
+      use:
+        - wpt-base
+        - trigger-pr
+      schedule-if:
+        run-job:
+          - update_built
+      command: "./tools/ci/ci_built_diff.sh"
+
+  - tools/ unittests (Python 2):
+      use:
+        - wpt-base
+        - trigger-pr
+        - tox-python2
+      description: >-
+        Unit tests for tools running under Python 2.7, excluding wptrunner
+      command: ./tools/ci/ci_tools_unittest.sh
+      env:
+        HYPOTHESIS_PROFILE: ci
+      schedule-if:
+        run-job:
+          - tools_unittest
+
+  - tools/ unittests (Python 3):
+      description: >-
+        Unit tests for tools running under Python 3, excluding wptrunner
+      use:
+        - wpt-base
+        - trigger-pr
+        - tox-python3
+      command: ./tools/ci/ci_tools_unittest.sh
+      env:
+        HYPOTHESIS_PROFILE: ci
+      schedule-if:
+        run-job:
+          - tools_unittest
+
+  - tools/wpt/ tests:
+      description: >-
+        Integration tests for wpt commands
+      use:
+        - wpt-base
+        - trigger-pr
+        - tox-python2
+      command: ./tools/ci/ci_wpt.sh
+      install:
+        - libnss3-tools
+      options:
+        oom-killer: true
+        browser:
+          - firefox
+          - chrome
+        channel: experimental
+        xvfb: true
+        hosts: true
+      schedule-if:
+        run-job:
+          - wpt_integration
+
+  - resources/ tests:
+      description: >-
+        Tests for testharness.js and other files in resources/
+      use:
+        - wpt-base
+        - trigger-pr
+        - tox-python2
+      command: ./tools/ci/ci_resources_unittest.sh
+      options:
+        browser:
+          - firefox
+        xvfb: true
+        hosts: true
+      schedule-if:
+        run-job:
+          - resources_unittest
+
+  - infrastructure/ tests:
+      description: >-
+        Smoketests for wptrunner
+      use:
+        - wpt-base
+        - trigger-pr
+        - tox-python2
+      command: ./tools/ci/ci_wptrunner_infrastructure.sh
+      install:
+        - libnss3-tools
+        - libappindicator1
+        - fonts-liberation
+      options:
+        oom-killer: true
+        browser:
+          - firefox
+          - chrome
+        channel: experimental
+        xvfb: true
+        hosts: false
+      schedule-if:
+        run-job:
+          - wptrunner_infrastructure
diff --git a/third_party/blink/web_tests/external/wpt/tools/taskcluster/testdata/master_push_event.json b/third_party/blink/web_tests/external/wpt/tools/ci/tc/testdata/master_push_event.json
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/tools/taskcluster/testdata/master_push_event.json
rename to third_party/blink/web_tests/external/wpt/tools/ci/tc/testdata/master_push_event.json
diff --git a/third_party/blink/web_tests/external/wpt/tools/taskcluster/testdata/pr_event.json b/third_party/blink/web_tests/external/wpt/tools/ci/tc/testdata/pr_event.json
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/tools/taskcluster/testdata/pr_event.json
rename to third_party/blink/web_tests/external/wpt/tools/ci/tc/testdata/pr_event.json
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/tc/tests/test_decision.py b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tests/test_decision.py
new file mode 100644
index 0000000..425b582
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tests/test_decision.py
@@ -0,0 +1,54 @@
+import mock
+import pytest
+
+from tools.ci.tc import decision
+from six import iteritems
+
+
+@pytest.mark.parametrize("run_jobs,tasks,expected", [
+    ([], {"task-no-schedule-if": {}}, ["task-no-schedule-if"]),
+    ([], {"task-schedule-if-no-run-job": {"schedule-if": {}}}, []),
+    (["job"],
+     {"job-present": {"schedule-if": {"run-job": ["other-job", "job"]}}},
+     ["job-present"]),
+    (["job"], {"job-missing": {"schedule-if": {"run-job": ["other-job"]}}}, []),
+    (["all"], {"job-all": {"schedule-if": {"run-job": ["other-job"]}}}, ["job-all"]),
+    (["job"],
+     {"job-1": {"schedule-if": {"run-job": ["job"]}},
+      "job-2": {"schedule-if": {"run-job": ["other-job"]}}},
+     ["job-1"]),
+])
+def test_filter_schedule_if(run_jobs, tasks, expected):
+    with mock.patch("tools.ci.tc.decision.get_run_jobs",
+                    return_value=run_jobs) as get_run_jobs:
+        assert (decision.filter_schedule_if({}, tasks) ==
+                {name: tasks[name] for name in expected})
+        get_run_jobs.call_count in (0, 1)
+
+
+@pytest.mark.parametrize("msg,expected", [
+    ("Some initial line\n\ntc-jobs:foo,bar", {"foo", "bar"}),
+    ("Some initial line\n\ntc-jobs:foo, bar", {"foo", "bar"}),
+    ("tc-jobs:foo, bar   \nbaz", {"foo", "bar"}),
+    ("tc-jobs:all", {"all"}),
+    ("", set()),
+    ("tc-jobs:foo\ntc-jobs:bar", {"foo"})])
+@pytest.mark.parametrize("event", [
+    {"commits": [{"message": "<message>"}]},
+    {"pull_request": {"body": "<message>"}}
+])
+def test_extra_jobs_pr(msg, expected, event):
+    def sub(obj):
+        """Copy obj, except if it's a string with the value <message>
+        replace it with the value of the msg argument"""
+        if isinstance(obj, dict):
+            return {key: sub(value) for (key, value) in iteritems(obj)}
+        elif isinstance(obj, list):
+            return [sub(value) for value in obj]
+        elif obj == "<message>":
+            return msg
+        return obj
+
+    event = sub(event)
+
+    assert decision.get_extra_jobs(event) == expected
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/tc/tests/test_taskgraph.py b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tests/test_taskgraph.py
new file mode 100644
index 0000000..dd8535b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tests/test_taskgraph.py
@@ -0,0 +1,146 @@
+import pytest
+import yaml
+
+from tools.ci.tc import taskgraph
+
+@pytest.mark.parametrize("data, update_data, expected", [
+    ({"a": 1}, {"b": 2}, {"a": 1, "b": 2}),
+    ({"a": 1}, {"a": 2}, {"a": 2}),
+    ({"a": [1]}, {"a": [2]}, {"a": [1, 2]}),
+    ({"a": {"b": 1, "c": 2}}, {"a": {"b": 2, "d": 3}}, {"a": {"b": 2, "c": 2, "d": 3}}),
+    ({"a": {"b": [1]}}, {"a": {"b": [2]}}, {"a": {"b": [1, 2]}}),
+]
+)
+def test_update_recursive(data, update_data, expected):
+    taskgraph.update_recursive(data, update_data)
+    assert data == expected
+
+
+def test_use():
+    data = """
+components:
+  component1:
+    a: 1
+    b: [1]
+    c: "c"
+  component2:
+    a: 2
+    b: [2]
+    d: "d"
+tasks:
+  - task1:
+      use:
+       - component1
+       - component2
+      b: [3]
+      c: "e"
+"""
+    tasks_data = yaml.safe_load(data)
+    assert taskgraph.load_tasks(tasks_data) == {
+        "task1": {
+            "a": 2,
+            "b": [1,2,3],
+            "c": "e",
+            "d": "d",
+            "name": "task1"
+        }
+    }
+
+
+def test_var():
+    data = """
+components:
+  component1:
+    a: ${vars.value}
+tasks:
+  - task1:
+      use:
+       - component1
+      vars:
+        value: 1
+"""
+    tasks_data = yaml.safe_load(data)
+    assert taskgraph.load_tasks(tasks_data) == {
+        "task1": {
+            "a": "1",
+            "vars": {"value": 1},
+            "name": "task1"
+        }
+    }
+
+
+def test_map():
+    data = """
+components: {}
+tasks:
+ - $map:
+     for:
+       - vars:
+           a: 1
+         b: [1]
+       - vars:
+           a: 2
+         b: [2]
+     do:
+       - task1-${vars.a}:
+           a: ${vars.a}
+           b: [3]
+       - task2-${vars.a}:
+           a: ${vars.a}
+           b: [4]
+"""
+    tasks_data = yaml.safe_load(data)
+    assert taskgraph.load_tasks(tasks_data) == {
+        "task1-1": {
+            "a": "1",
+            "b": [1, 3],
+            "vars": {"a": 1},
+            "name": "task1-1"
+        },
+        "task1-2": {
+            "a": "2",
+            "b": [2, 3],
+            "vars": {"a": 2},
+            "name": "task1-2"
+        },
+        "task2-1": {
+            "a": "1",
+            "b": [1, 4],
+            "vars": {"a": 1},
+            "name": "task2-1"
+        },
+        "task2-2": {
+            "a": "2",
+            "b": [2, 4],
+            "vars": {"a": 2},
+            "name": "task2-2"
+        },
+
+    }
+
+
+def test_chunks():
+    data = """
+components: {}
+tasks:
+  - task1:
+      name: task1-${chunks.id}
+      chunks: 2
+"""
+    tasks_data = yaml.safe_load(data)
+    assert taskgraph.load_tasks(tasks_data) == {
+        "task1-1": {
+            "name": "task1-1",
+            "chunks": {
+                "id": 1,
+                "total": 2
+            }
+        },
+        "task1-2": {
+            "name": "task1-2",
+            "chunks": {
+                "id": 2,
+                "total": 2
+            }
+        }
+    }
diff --git a/third_party/blink/web_tests/external/wpt/tools/ci/tc/tests/test_valid.py b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tests/test_valid.py
new file mode 100644
index 0000000..f51a633b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/tools/ci/tc/tests/test_valid.py
@@ -0,0 +1,73 @@
+import json
+import os
+
+import jsone
+import mock
+import pytest
+import requests
+import sys
+import yaml
+from jsonschema import validate
+
+here = os.path.dirname(__file__)
+root = os.path.abspath(os.path.join(here, "..", "..", "..", ".."))
+
+
+def data_path(filename):
+    return os.path.join(here, "..", "testdata", filename)
+
+
+@pytest.mark.xfail(sys.version_info.major == 2,
+                   reason="taskcluster library has an encoding bug "
+                   "https://github.com/taskcluster/json-e/issues/338")
+def test_verify_taskcluster_yml():
+    """Verify that the json-e in the .taskcluster.yml is valid"""
+    with open(os.path.join(root, ".taskcluster.yml")) as f:
+        template = yaml.safe_load(f)
+
+    events = [("pr_event.json", "github-pull-request", "Pull Request"),
+              ("master_push_event.json", "github-push", "Push to master")]
+
+    for filename, tasks_for, title in events:
+        with open(data_path(filename)) as f:
+            event = json.load(f)
+
+        context = {"tasks_for": tasks_for,
+                   "event": event,
+                   "as_slugid": lambda x: x}
+
+        jsone.render(template, context)
+
+
+def test_verify_payload():
+    """Verify that the decision task produces tasks with a valid payload"""
+    from tools.ci.tc.decision import decide
+
+    create_task_schema = requests.get(
+        "https://raw.githubusercontent.com/taskcluster/taskcluster/blob/master/services/queue/schemas/v1/create-task-request.yml")
+    create_task_schema = yaml.safe_load(create_task_schema.content)
+
+    payload_schema = requests.get("https://raw.githubusercontent.com/taskcluster/docker-worker/master/schemas/v1/payload.json").json()
+
+    jobs = ["lint",
+            "manifest_upload",
+            "resources_unittest",
+            "tools_unittest",
+            "wpt_integration",
+            "wptrunner_infrastructure",
+            "wptrunner_unittest"]
+
+    for filename in ["pr_event.json", "master_push_event.json"]:
+        with open(data_path(filename)) as f:
+            event = json.load(f)
+
+        with mock.patch("tools.ci.tc.decision.get_fetch_rev", return_value=(event["after"], None)):
+            with mock.patch("tools.ci.tc.decision.get_run_jobs", return_value=set(jobs)):
+                task_id_map = decide(event)
+        for name, (task_id, task_data) in task_id_map.items():
+            try:
+                validate(instance=task_data, schema=create_task_schema)
+                validate(instance=task_data["payload"], schema=payload_schema)
+            except Exception as e:
+                print("Validation failed for task '%s':\n%s" % (name, json.dumps(task_data, indent=2)))
+                raise e
diff --git a/third_party/blink/web_tests/external/wpt/tools/taskcluster/commands.json b/third_party/blink/web_tests/external/wpt/tools/taskcluster/commands.json
deleted file mode 100644
index dcbd896..0000000
--- a/third_party/blink/web_tests/external/wpt/tools/taskcluster/commands.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-    "tc-verify": {"path": "verify.py", "script": "run", "parser": "create_parser", "help": "Verify .taskcluster.yml file is parsable",
-                  "virtualenv": true, "install": ["json-e", "pyyaml"]}
-}
diff --git a/third_party/blink/web_tests/external/wpt/tools/taskcluster/verify.py b/third_party/blink/web_tests/external/wpt/tools/taskcluster/verify.py
deleted file mode 100644
index b37e45ec..0000000
--- a/third_party/blink/web_tests/external/wpt/tools/taskcluster/verify.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import argparse
-import json
-import os
-
-import jsone
-import yaml
-
-here = os.path.dirname(__file__)
-root = os.path.abspath(os.path.join(here, "..", ".."))
-
-
-def create_parser():
-    return argparse.ArgumentParser()
-
-
-def run(venv, **kwargs):
-    with open(os.path.join(root, ".taskcluster.yml")) as f:
-        template = yaml.safe_load(f)
-
-    events = [("pr_event.json", "github-pull-request", "Pull Request"),
-              ("master_push_event.json", "github-push", "Push to master")]
-
-    for filename, tasks_for, title in events:
-        with open(os.path.join(here, "testdata", filename)) as f:
-            event = json.load(f)
-
-        context = {"tasks_for": tasks_for,
-                   "event": event,
-                   "as_slugid": lambda x: x}
-
-        data = jsone.render(template, context)
-        heading = "Got %s tasks for %s" % (len(data["tasks"]), title)
-        print(heading)
-        print("=" * len(heading))
-        for item in data["tasks"]:
-            print(json.dumps(item, indent=2))
-        print("")
diff --git a/third_party/blink/web_tests/external/wpt/tools/tox.ini b/third_party/blink/web_tests/external/wpt/tools/tox.ini
index 7a5c1eb..e8673db 100644
--- a/third_party/blink/web_tests/external/wpt/tools/tox.ini
+++ b/third_party/blink/web_tests/external/wpt/tools/tox.ini
@@ -8,8 +8,11 @@
   pytest-cov
   mock
   hypothesis
-  # `requests` is required by `pr_preview.py`
   requests
+  taskcluster
+  pyyaml
+  json-e
+  jsonschema
 
 commands = pytest {posargs}
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/wpt/browser.py b/third_party/blink/web_tests/external/wpt/tools/wpt/browser.py
index e7d57d863..a94d550 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wpt/browser.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wpt/browser.py
@@ -48,6 +48,11 @@
         self.logger = logger
 
     @abstractmethod
+    def download(self, dest=None, channel=None):
+        """Download a package or installer for the browser"""
+        return NotImplemented
+
+    @abstractmethod
     def install(self, dest=None):
         """Install the browser."""
         return NotImplemented
@@ -116,11 +121,19 @@
 
         return "%s%s" % (self.platform, bits)
 
-    def install(self, dest=None, channel="nightly"):
-        """Install Firefox."""
+    def _get_dest(self, dest, channel):
+        if dest is None:
+            # os.getcwd() doesn't include the venv path
+            dest = os.path.join(os.getcwd(), "_venv")
 
-        import mozinstall
+        dest = os.path.join(dest, "browsers", channel)
 
+        if not os.path.exists(dest):
+            os.makedirs(dest)
+
+        return dest
+
+    def download(self, dest=None, channel="nightly"):
         product = {
             "nightly": "firefox-nightly-latest-ssl",
             "beta": "firefox-beta-latest-ssl",
@@ -136,21 +149,15 @@
         }
         os_key = (self.platform, uname[4])
 
+        if dest is None:
+            dest = self._get_dest(None, channel)
+
         if channel not in product:
             raise ValueError("Unrecognised release channel: %s" % channel)
 
         if os_key not in os_builds:
             raise ValueError("Unsupported platform: %s %s" % os_key)
 
-        if dest is None:
-            # os.getcwd() doesn't include the venv path
-            dest = os.path.join(os.getcwd(), "_venv")
-
-        dest = os.path.join(dest, "browsers", channel)
-
-        if not os.path.exists(dest):
-            os.makedirs(dest)
-
         url = "https://download.mozilla.org/?product=%s&os=%s&lang=en-US" % (product[channel],
                                                                              os_builds[os_key])
         self.logger.info("Downloading Firefox from %s" % url)
@@ -175,6 +182,18 @@
         with open(installer_path, "wb") as f:
             f.write(resp.content)
 
+        return installer_path
+
+    def install(self, dest=None, channel="nightly"):
+        """Install Firefox."""
+        import mozinstall
+
+        dest = self._get_dest(dest, channel)
+
+        filename = os.path.basename(dest)
+
+        installer_path = self.download(dest, channel)
+
         try:
             mozinstall.install(installer_path, dest)
         except mozinstall.mozinstall.InstallError:
@@ -422,7 +441,7 @@
     product = "firefox_android"
     requirements = "requirements_firefox.txt"
 
-    def install(self, dest=None, channel=None):
+    def download(self, dest=None, channel=None):
         if dest is None:
             dest = os.pwd
 
@@ -452,6 +471,9 @@
 
         return apk_path
 
+    def install(self, dest=None, channel=None):
+        return self.download(dest, channel)
+
     def install_prefs(self, binary, dest=None, channel=None):
         fx_browser = Firefox(self.logger)
         return fx_browser.install_prefs(binary, dest, channel)
@@ -478,6 +500,9 @@
     product = "chrome"
     requirements = "requirements_chrome.txt"
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -633,6 +658,9 @@
         super(ChromeAndroidBase, self).__init__(logger)
         self.device_serial = None
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -724,6 +752,9 @@
     product = "chrome_ios"
     requirements = "requirements_chrome_ios.txt"
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -757,6 +788,9 @@
         self.logger.warning("Unable to find the browser binary.")
         return None
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -826,6 +860,9 @@
     edgedriver_name = "msedgedriver"
     requirements = "requirements_edge_chromium.txt"
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -922,6 +959,9 @@
     product = "edge"
     requirements = "requirements_edge.txt"
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -953,6 +993,9 @@
     product = "ie"
     requirements = "requirements_ie.txt"
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -978,6 +1021,9 @@
     product = "safari"
     requirements = "requirements_safari.txt"
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -1037,17 +1083,33 @@
 
         return (platform, extension, decompress)
 
-    def install(self, dest=None, channel="nightly"):
-        """Install latest Browser Engine."""
+    def _get(self, channel="nightly"):
         if channel != "nightly":
             raise ValueError("Only nightly versions of Servo are available")
+
+        platform, extension, _ = self.platform_components()
+        url = "https://download.servo.org/nightly/%s/servo-latest%s" % (platform, extension)
+        return get(url)
+
+    def download(self, dest=None, channel="nightly"):
         if dest is None:
             dest = os.pwd
 
-        platform, extension, decompress = self.platform_components()
-        url = "https://download.servo.org/nightly/%s/servo-latest%s" % (platform, extension)
+        resp = self._get(dest, channel)
+        _, extension, _ = self.platform_components()
 
-        decompress(get(url).raw, dest=dest)
+        with open(os.path.join(dest, "servo-latest%s" % (extension,)), "w") as f:
+            f.write(resp.content)
+
+    def install(self, dest=None, channel="nightly"):
+        """Install latest Browser Engine."""
+        if dest is None:
+            dest = os.pwd
+
+        _, _, decompress = self.platform_components()
+
+        resp = self._get(dest, channel)
+        decompress(resp.raw, dest=dest)
         path = find_executable("servo", os.path.join(dest, "servo"))
         st = os.stat(path)
         os.chmod(path, st.st_mode | stat.S_IEXEC)
@@ -1083,6 +1145,9 @@
     product = "sauce"
     requirements = "requirements_sauce.txt"
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -1105,6 +1170,9 @@
     product = "webkit"
     requirements = "requirements_webkit.txt"
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
@@ -1168,6 +1236,9 @@
     product = "epiphany"
     requirements = "requirements_epiphany.txt"
 
+    def download(self, dest=None, channel=None):
+        raise NotImplementedError
+
     def install(self, dest=None, channel=None):
         raise NotImplementedError
 
diff --git a/third_party/blink/web_tests/external/wpt/tools/wpt/install.py b/third_party/blink/web_tests/external/wpt/tools/wpt/install.py
index 8215dfe0..24915f0 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wpt/install.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wpt/install.py
@@ -42,6 +42,8 @@
                         'the latest available development release. For WebDriver installs, '
                         'we attempt to select an appropriate, compatible, version for the '
                         'latest browser release on the selected channel.')
+    parser.add_argument('--download-only', action="store_true",
+                        help="Download the selected component but don't install it")
     parser.add_argument('-d', '--destination',
                         help='filesystem directory to place the component')
     return parser
@@ -73,21 +75,22 @@
             raise argparse.ArgumentError(None,
                                          "No --destination argument, and no default for the environment")
 
-    install(browser, kwargs["component"], destination, channel)
+    install(browser, kwargs["component"], destination, channel,
+            download_only=kwargs["download_only"])
 
 
-def install(name, component, destination, channel="nightly", logger=None):
+def install(name, component, destination, channel="nightly", logger=None, download_only=False):
     if logger is None:
         import logging
         logger = logging.getLogger("install")
 
-    if component == 'webdriver':
-        method = 'install_webdriver'
-    else:
-        method = 'install'
+    prefix = "download" if download_only else "install"
+    suffix = "_webdriver" if component == 'webdriver' else ""
+
+    method = prefix + suffix
 
     subclass = getattr(browser, name.title())
     sys.stdout.write('Now installing %s %s...\n' % (name, component))
     path = getattr(subclass(logger), method)(dest=destination, channel=channel)
     if path:
-        sys.stdout.write('Binary installed as %s\n' % (path,))
+        sys.stdout.write('Binary %s as %s\n' % ("downloaded" if download_only else "installed", path,))
diff --git a/third_party/blink/web_tests/external/wpt/tools/wpt/paths b/third_party/blink/web_tests/external/wpt/tools/wpt/paths
index 093a715..35867c4 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wpt/paths
+++ b/third_party/blink/web_tests/external/wpt/tools/wpt/paths
@@ -3,5 +3,4 @@
 tools/lint/
 tools/manifest/
 tools/serve/
-tools/taskcluster/
 tools/wpt/
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/chromium.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/chromium.py
index 5692390..56435ea 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/chromium.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/chromium.py
@@ -114,6 +114,24 @@
         # Any other status just gets returned as-is.
         return status
 
+    def _get_expected_status_from_data(self, actual_status, data):
+        """
+        Gets the expected status from a |data| dictionary.
+
+        If there is no expected status in data, the actual status is returned.
+        This is because mozlog will delete "expected" from |data| if it is the
+        same as "status". So the presence of "expected" implies that "status" is
+        unexpected. Conversely, the absence of "expected" implies the "status"
+        is expected. So we use the "expected" status if it's there or fall back
+        to the actual status if it's not.
+
+        :param str actual_status: the actual status of the test
+        :param data: a data dictionary to extract expected status from
+        :return str: the expected status
+        """
+        return (self._map_status_name(data["expected"])
+                if "expected" in data else actual_status)
+
     def suite_start(self, data):
         self.start_timestamp_seconds = (data["time"] if "time" in data
                                         else time.time())
@@ -122,13 +140,7 @@
         test_name = data["test"]
         is_unexpected = None
         actual_status = self._map_status_name(data["status"])
-        # mozlog will delete "expected" from |data| if it is the same as "status".
-        # So the presence of "expected" implies that "status" is unexpected.
-        # Conversely, the absence of "expected" implies the "status" is expected.
-        # So here we use the "expected" status if it's there or fall back to the
-        # actual status if it's not.
-        expected_status = (self._map_status_name(data["expected"])
-                           if "expected" in data else actual_status)
+        expected_status = self._get_expected_status_from_data(actual_status, data)
 
         is_unexpected = actual_status != expected_status
         if is_unexpected and test_name not in self.tests_with_subtest_fails:
@@ -139,8 +151,6 @@
                                       data["message"])
 
     def test_end(self, data):
-        expected_status = (self._map_status_name(data["expected"])
-                           if "expected" in data else "PASS")
         test_name = data["test"]
         actual_status = self._map_status_name(data["status"])
         if actual_status == "PASS" and test_name in self.tests_with_subtest_fails:
@@ -150,6 +160,7 @@
             # Clean up the test list to avoid accumulating too many.
             self.tests_with_subtest_fails.remove(test_name)
 
+        expected_status = self._get_expected_status_from_data(actual_status, data)
         if "message" in data:
             self._append_test_message(test_name, None, actual_status,
                                       expected_status, data["message"])
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
index 9bb72d95..83c128a 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
@@ -214,7 +214,7 @@
 
 
 def test_expected_subtest_failure(capfd):
-    # Tests that a expected subtest failure does not cause the test to fail
+    # Tests that an expected subtest failure does not cause the test to fail
 
     # Set up the handler.
     output = StringIO()
@@ -262,7 +262,7 @@
 
 
 def test_unexpected_subtest_pass(capfd):
-    # A test that unexpectedly passes is considered a failure condition.
+    # A subtest that unexpectedly passes is considered a failure condition.
 
     # Set up the handler.
     output = StringIO()
@@ -302,3 +302,71 @@
     assert test_obj["actual"] == "FAIL"
     # Also ensure that the formatter cleaned up its internal state
     assert "t1" not in formatter.tests_with_subtest_fails
+
+
+def test_expected_test_fail(capfd):
+    # Check that an expected test-level failure is treated as a Pass
+
+    # Set up the handler.
+    output = StringIO()
+    logger = structuredlog.StructuredLogger("test_a")
+    logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
+
+    # Run some tests with different statuses: 3 passes, 1 timeout
+    logger.suite_start(["t1"], run_info={}, time=123)
+    logger.test_start("t1")
+    logger.test_end("t1", status="ERROR", expected="ERROR")
+    logger.suite_end()
+
+    # check nothing got output to stdout/stderr
+    # (note that mozlog outputs exceptions during handling to stderr!)
+    captured = capfd.readouterr()
+    assert captured.out == ""
+    assert captured.err == ""
+
+    # check the actual output of the formatter
+    output.seek(0)
+    output_json = json.load(output)
+
+    test_obj = output_json["tests"]["t1"]
+    # The test's actual and expected status should map from "ERROR" to "FAIL"
+    assert test_obj["actual"] == "FAIL"
+    assert test_obj["expected"] == "FAIL"
+    # ..and this test should not be a regression nor unexpected
+    assert "is_regression" not in test_obj
+    assert "is_unexpected" not in test_obj
+
+
+def test_unexpected_test_fail(capfd):
+    # Check that an unexpected test-level failure is marked as unexpected and
+    # as a regression.
+
+    # Set up the handler.
+    output = StringIO()
+    logger = structuredlog.StructuredLogger("test_a")
+    logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
+
+    # Run some tests with different statuses: 3 passes, 1 timeout
+    logger.suite_start(["t1"], run_info={}, time=123)
+    logger.test_start("t1")
+    logger.test_end("t1", status="ERROR", expected="OK")
+    logger.suite_end()
+
+    # check nothing got output to stdout/stderr
+    # (note that mozlog outputs exceptions during handling to stderr!)
+    captured = capfd.readouterr()
+    assert captured.out == ""
+    assert captured.err == ""
+
+    # check the actual output of the formatter
+    output.seek(0)
+    output_json = json.load(output)
+
+    test_obj = output_json["tests"]["t1"]
+    # The test's actual and expected status should be mapped, ERROR->FAIL and
+    # OK->PASS
+    assert test_obj["actual"] == "FAIL"
+    assert test_obj["expected"] == "PASS"
+    # ..and this test should not be a regression nor unexpected
+    assert test_obj["is_regression"] is True
+    assert test_obj["is_unexpected"] is True
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/finished-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/finished-expected.txt
deleted file mode 100644
index 8e06783..0000000
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Animation/finished-expected.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-This is a testharness.js-based test.
-PASS Test pausing then playing does not change the finished promise
-PASS Test restarting a finished animation
-PASS Test restarting a reversed finished animation
-PASS Test redundant finishing of animation
-PASS Finished promise does not resolve when paused
-PASS Finished promise does not resolve when pause-pending
-PASS The finished promise is fulfilled with its Animation
-PASS finished promise is rejected when an animation is canceled by calling cancel()
-PASS canceling an already-finished animation replaces the finished promise
-PASS Test finished promise changes for animation duration changes
-PASS Test finished promise changes when playbackRate == 0
-PASS Test finished promise resolves when reaching to the natural boundary.
-PASS Test finished promise changes when a prior finished promise resolved and the animation falls out finished state
-PASS Test no new finished promise generated when finished state is checked asynchronously
-PASS Test new finished promise generated when finished state is checked synchronously
-PASS Test synchronous finished promise resolved even if finished state is changed soon
-PASS Test synchronous finished promise resolved even if asynchronous finished promise happens just before synchronous promise
-PASS Test finished promise is not resolved when the animation falls out finished state immediately
-FAIL Test finished promise is not resolved once the animation falls out finished state even though the current finished promise is generated soon after animation state became finished assert_unreached: Animation.finished should not be resolved Reached unreachable code
-PASS Finished promise should be resolved after the ready promise is resolved
-PASS Finished promise should be rejected after the ready promise is rejected
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
index e9e75a3..6297bcd 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-navigated-expected.txt
@@ -88,7 +88,6 @@
 PASS window.cached_performance_timing.unloadEventEnd is 0
 PASS window.cached_performance_timing.unloadEventStart is 0
 PASS window.cached_personalbar.visible is false
-PASS window.cached_scheduler_currentTaskQueue.priority is 'default'
 PASS window.cached_screen.availHeight is 0
 PASS window.cached_screen.availLeft is 0
 PASS window.cached_screen.availTop is 0
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
index 7b9693a..9a0d9d1 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-and-gced-expected.txt
@@ -88,7 +88,6 @@
 PASS window.cached_performance_timing.unloadEventEnd is 0
 PASS window.cached_performance_timing.unloadEventStart is 0
 PASS window.cached_personalbar.visible is false
-PASS window.cached_scheduler_currentTaskQueue.priority is 'default'
 PASS window.cached_screen.availHeight is 0
 PASS window.cached_screen.availLeft is 0
 PASS window.cached_screen.availTop is 0
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
index d8ef365..9d08ff4 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-properties-after-frame-removed-expected.txt
@@ -88,7 +88,6 @@
 PASS window.cached_performance_timing.unloadEventEnd is 0
 PASS window.cached_performance_timing.unloadEventStart is 0
 PASS window.cached_personalbar.visible is false
-PASS window.cached_scheduler_currentTaskQueue.priority is 'default'
 PASS window.cached_screen.availHeight is 0
 PASS window.cached_screen.availLeft is 0
 PASS window.cached_screen.availTop is 0
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
index 96bf24c..dd82a73f 100644
--- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -217,7 +217,6 @@
 PASS oldChildWindow.performance.timing.unloadEventStart is newChildWindow.performance.timing.unloadEventStart
 PASS oldChildWindow.personalbar.visible is newChildWindow.personalbar.visible
 PASS oldChildWindow.portalHost is newChildWindow.portalHost
-PASS oldChildWindow.scheduler.currentTaskQueue.priority is newChildWindow.scheduler.currentTaskQueue.priority
 PASS oldChildWindow.screen.availHeight is newChildWindow.screen.availHeight
 PASS oldChildWindow.screen.availLeft is newChildWindow.screen.availLeft
 PASS oldChildWindow.screen.availTop is newChildWindow.screen.availTop
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cookies-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cookies-expected.txt
index 54e78a9..30ccb31 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cookies-expected.txt
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cookies-expected.txt
@@ -1,5 +1,7 @@
 Tests that cookies are read and written.
 
+Running test: testPageContext
+
 Initial: 0 cookies
 Cookies as seen on server: "HTTP_COOKIE: <not set>\n"
 
@@ -16,3 +18,13 @@
 Post-clear: 0 cookies
 Cookies as seen on server: "HTTP_COOKIE: <not set>\n"
 
+Running test: testInvalidContext
+{
+    error : {
+        code : -32602
+        message : Failed to find browser context for id invalid
+    }
+    id : <number>
+    sessionId : <string>
+}
+
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cookies.js b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cookies.js
index 2e095c5..81f2441 100644
--- a/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cookies.js
+++ b/third_party/blink/web_tests/http/tests/inspector-protocol/storage/cookies.js
@@ -1,9 +1,9 @@
 (async function(testRunner) {
-  const {dp} = await testRunner.startBlank(
+  const {dp, page} = await testRunner.startBlank(
       `Tests that cookies are read and written.`);
 
-  async function logCookies(title) {
-    var data = (await dp.Storage.getCookies()).result;
+  async function logCookies(title, browserContextId) {
+    var data = (await dp.Storage.getCookies({ browserContextId })).result;
     testRunner.log(`\n${title}: ${data.cookies.length} cookies`);
     data.cookies.sort((a, b) => a.name.localeCompare(b.name));
     for (var cookie of data.cookies) {
@@ -40,21 +40,29 @@
   }
 
 
-  await logCookies('Initial');
+  testRunner.runTestSuite([
+    async function testPageContext() {
+      await logCookies('Initial');
 
-  await setCookieViaFetch();
-  await logCookies('Post-fetch');
+      await setCookieViaFetch();
+      await logCookies('Post-fetch');
 
-  const cookies = [
-    {url: 'http://127.0.0.1', name: 'foo', value: 'bar1'},
-    {url: 'http://127.0.0.1', name: 'foo', value: 'second bar2'},
-    {url: 'http://127.0.0.1', name: 'foo2', value: 'bar1'}
-  ];
-  await dp.Storage.setCookies({cookies});
-  await logCookies('Post-set');
+      const cookies = [
+        {url: 'http://127.0.0.1', name: 'foo', value: 'bar1'},
+        {url: 'http://127.0.0.1', name: 'foo', value: 'second bar2'},
+        {url: 'http://127.0.0.1', name: 'foo2', value: 'bar1'}
+      ];
+      await dp.Storage.setCookies({cookies});
+      await logCookies('Post-set');
 
-  await dp.Storage.clearCookies();
-  await logCookies('Post-clear');
+      await dp.Storage.clearCookies();
+      await logCookies('Post-clear');
+    },
 
-  testRunner.completeTest();
+    async function testInvalidContext() {
+      var data = (await dp.Storage.getCookies({ browserContextId: 'invalid' }));
+      testRunner.log(data);
+    }
+  ]);
+
 })
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 8274a64..1edbb01 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index f604e04..8344972 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 241d4d3..0056109 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index b2d8646..e1f4302 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
index f22cb76b..f3813770 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
index 61133f9..7e22a7c4 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
index 1fc2e2a6..b6cb3a0 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
index 13016f03..cb04afe 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
index 056f85a..61e78cff 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
index 909632e..833fcf1 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
index df3116e5..155c9d34 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
index 08756b2..6afd9449 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
index 0eefe84..f9529d3 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 041de2b..f0cf2da 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index 3e06b0f..3c86c2fe 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 72d3108..12f31a4f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index bd9866d..fbc5354 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
index 33c94fb..190f927 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
index 2d6c6c56..6c3a211e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
index 9767958..c58408a0 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
index f67a113..37db129 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
index d7f9e80..620b45a3 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
index 04fb4db..5363b4d8 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
index 7905adc..08d74bf 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
index 93ed024..ab52c9e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
index 86ddea5..8163cd79 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 0390c8f..109b1c015 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index a221ae1..14ad2959 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 0390c8f..109b1c015 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index a221ae1..14ad2959 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 618df27..797e4b9 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index 9adbe0e..a8ed0908f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
index 147bad1..b31ee51 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
index 2720d54..63fc1dd8 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
index 773130a..3b453f6 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
index 841fde1..eb6ef7a 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
index b0af6439..be6da2bf 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
index c21cf46..fdcee64 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
index 1df98e6..a29b01bb 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
index d63c63e..a620e53 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
index 1de01e7..b55e288 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index cbcc93f..c0ba1d03 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index 1c9626e..d3eef40 100644
--- a/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-retina/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index cbcc93f..c0ba1d03 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index 1c9626e..d3eef40 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 354b980..bc61f1c 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index dbd069be..0bc3ff1 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
index d6d7f329..31807e2c 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
index 7f633ea6..ddd8cb8 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
index 5950e65..651a881 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
index df2993e..de5474be 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
index 228c8bf2..94338d0 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
index 52cbfff..ea5f1880 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
index 79e7b64..09953e9 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
index a5352036..5785258 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
index ed8d4f2..b6f9cf6 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index b2f7868..b4c099b 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index d1cc316..b0457aef 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 8ee9324..c85963a 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index 339ac3b..308b16b8 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
index 3103ac29..d42bee8 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
index a9cd0bd..eddde61 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-12-AM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
index 33a0ffb..4796ef4 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-12-PM-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
index 1f368e18..27727052 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-24-hour-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
index 8dc4b121..89dfd5b 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-arrowdown-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
index bdd87e9..8b1fe0c0 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-arrowup-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
index 72381ba..00b29fd 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
index 106ca74..81a5390 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-milliseconds-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
index 7fe13ee..825fcfe1b 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/time-picker/time-picker-appearance-seconds-pm-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 188a180..1f7c2586 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
index d1cc316..b0457aef 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/time-picker/time-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
index 4b599d7..98e9b122 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/color-scheme/datetimelocal-picker/datetimelocal-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
index 3c3c4c2..3cd1bd7 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/datetimelocal-picker/datetimelocal-picker-step2-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
index 4d252654..ec90cb2 100644
--- a/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
+++ b/third_party/blink/web_tests/platform/win7/virtual/controls-refresh/time-picker/time-picker-appearance-ko-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 425c6a7..92e642a8 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -7718,9 +7718,7 @@
     setter zoomAndPan
 interface Scheduler
     attribute @@toStringTag
-    getter currentTaskQueue
     method constructor
-    method getTaskQueue
     method postTask
 interface Scheduling
     attribute @@toStringTag
@@ -8097,13 +8095,6 @@
     method constructor
     method getTags
     method register
-interface Task
-    attribute @@toStringTag
-    getter priority
-    getter result
-    getter status
-    method cancel
-    method constructor
 interface TaskAttributionTiming : PerformanceEntry
     attribute @@toStringTag
     getter containerId
@@ -8112,12 +8103,14 @@
     getter containerType
     method constructor
     method toJSON
-interface TaskQueue
+interface TaskController : AbortController
+    attribute @@toStringTag
+    method constructor
+    method setPriority
+interface TaskSignal : AbortSignal
     attribute @@toStringTag
     getter priority
     method constructor
-    method postTask
-    method take
 interface Text : CharacterData
     attribute @@toStringTag
     getter assignedSlot
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/move_delayed_task.html b/third_party/blink/web_tests/wpt_internal/scheduler/change_delayed_task_priority.html
similarity index 81%
rename from third_party/blink/web_tests/wpt_internal/scheduler/move_delayed_task.html
rename to third_party/blink/web_tests/wpt_internal/scheduler/change_delayed_task_priority.html
index 2adfcd2..62d6b17 100644
--- a/third_party/blink/web_tests/wpt_internal/scheduler/move_delayed_task.html
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/change_delayed_task_priority.html
@@ -14,17 +14,19 @@
 async_test(t => {
   let now = performance.now();
 
+  let tc = new TaskController('low');
+
   scheduler.postTask(t.step_func(() => {
     assert_equals(++taskCount, 1);
-    scheduler.getTaskQueue('immediate').take(taskToMove);
+    tc.setPriority('immediate');
   }), { priority: 'immediate', delay: 10 });
 
-  taskToMove = scheduler.postTask(t.step_func_done(() => {
+  scheduler.postTask(t.step_func_done(() => {
     assert_equals(++taskCount, 2);
 
     let elapsed = performance.now() - now;
     assert_greater_than_equal(elapsed, 20);
-  }), { priority: 'low', delay: 20 });
+  }), { signal: tc.signal, delay: 20 });
 
 }, 'Tests delay when moving a delayed task');
 
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/change_task_priority.html b/third_party/blink/web_tests/wpt_internal/scheduler/change_task_priority.html
new file mode 100644
index 0000000..1507ff930b4
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/change_task_priority.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>Scheduling API: Setting TaskController.priority</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  let result = '';
+  let task_controllers = [];
+
+  for (let i = 0; i < 5; i++) {
+    let tc = new TaskController('idle');
+    scheduler.postTask(() => {
+      result += i.toString();
+    }, { signal: tc.signal });
+    task_controllers.push(tc);
+  }
+
+  task_controllers[2].setPriority('high');
+  assert_equals(task_controllers[2].signal.priority, 'high');
+
+  scheduler.postTask(t.step_func_done(() => {
+    assert_equals('20134', result);
+  }), { priority: 'idle' });
+
+}, 'Test modifying TaskController priority');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/current_task_queue.html b/third_party/blink/web_tests/wpt_internal/scheduler/current_task_queue.html
deleted file mode 100644
index 2c51b50..0000000
--- a/third_party/blink/web_tests/wpt_internal/scheduler/current_task_queue.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!doctype html>
-<title>Scheduling API: scheduler.currentTaskQueue</title>
-<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
-<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<script>
-'use strict';
-
-async_test(t => {
-  // Initially, the default TaskQueue should be set to the global default task queue.
-  let defaultTaskQueue = scheduler.getTaskQueue('default');
-  assert_equals(scheduler.currentTaskQueue, defaultTaskQueue);
-
-  [ "immediate", "high", "default", "low", "idle" ].forEach((priority) => {
-    scheduler.postTask(t.step_func(() => {
-      assert_equals(scheduler.currentTaskQueue, scheduler.getTaskQueue(priority));
-    }), { priority });
-  });
-
-  // Test that the current task queue is the default global tq for non-scheduled tasks.
-  // Schedule this at idle so it comes after the previous tests.
-  scheduler.postTask(t.step_func(() => {
-    setTimeout(t.step_func_done(() => {
-      assert_equals(scheduler.currentTaskQueue, defaultTaskQueue);
-    }), 0);
-  }), { priority: 'idle' });
-
-}, 'Tests that scheduler.currentTaskQueue is correctly set');
-
-</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/move_tasks.html b/third_party/blink/web_tests/wpt_internal/scheduler/move_tasks.html
deleted file mode 100644
index 4f40fef..0000000
--- a/third_party/blink/web_tests/wpt_internal/scheduler/move_tasks.html
+++ /dev/null
@@ -1,32 +0,0 @@
-<!doctype html>
-<title>Scheduling API: TaskQueue.move</title>
-<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
-<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<script>
-'use strict';
-
-async_test(t => {
-  let result = '';
-  let tasks = [];
-
-  for (let i = 0; i < 5; i++) {
-    let task = scheduler.postTask(() => {
-      result += i.toString();
-    }, { priority: 'idle' });
-    tasks.push(task);
-  }
-
-  scheduler.getTaskQueue('high').take(tasks[2]);
-  assert_equals(tasks[2].priority, 'high');
-
-  scheduler.postTask(() => {
-    assert_equals('20134', result);
-    t.done();
-  }, { priority: 'idle' });
-
-}, 'Test moving tasks between task queues using TaskQueue.take');
-
-</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/post_task_then_detach.html b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_then_detach.html
new file mode 100644
index 0000000..da1e39e
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_then_detach.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>Scheduling API: postTask</title>
+<link rel="author" title="Nate Chapin" href="mailto:japhet@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="i" src="support/post_task_then_detach_iframe.html"></iframe>
+<script>
+'use strict';
+
+onmessage = () => { assert_unreached("task should not have run"); };
+
+async_test(t => {
+  // Give the task time to run if it hadn't been detached.
+  scheduler.postTask(t.step_func_done(() => {}), { priority: "idle" }); 
+}, 'Test scheduler.postTask() from an iframe that is removed before the task runs');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/post_task_with_abort_signal.html b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_with_abort_signal.html
new file mode 100644
index 0000000..f6d128c
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_with_abort_signal.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>Scheduling API: postTask and AbortSignal</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  let result = "fail";
+  let ac = new AbortController;
+  scheduler.postTask(() => {}, { signal: ac.signal }).then(
+      () => { assert_unreached('This task should have been aborted'); },
+      () => { result = "pass"; });
+  ac.abort();
+
+  // The task should be aborted.
+  scheduler.postTask(t.step_func_done(() => {
+    assert_equals(result, "pass");
+  }));
+}, 'Test that scheduler.postTask() accepts an AbortSignal that is not also a TaskSignal');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/post_task_with_signal_and_priority.html b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_with_signal_and_priority.html
new file mode 100644
index 0000000..27c5129
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_with_signal_and_priority.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<title>Scheduling API: Global Task Queues</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  var result = "fail";
+  let tc = new TaskController("low");
+  scheduler.postTask(() => { result = "pass"; }, { priority : "high", signal: tc.signal });
+
+  // Since the above task should be run at high priority, it should execute
+  // before this default priority task.
+  scheduler.postTask(t.step_func_done(() => {
+    assert_equals(result, "pass");
+  }));
+}, 'Test when scheduler.postTask() is passed both a signal and a priority');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/post_task_with_signal_from_detached_iframe.html b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_with_signal_from_detached_iframe.html
new file mode 100644
index 0000000..0b9416e7
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_with_signal_from_detached_iframe.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<title>Scheduling API: postTask</title>
+<link rel="author" title="Nate Chapin" href="mailto:japhet@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="i" src="support/create_and_pass_signal.html"></iframe>
+<script>
+'use strict';
+
+var iframe_controller;
+function passController(controller) {
+  iframe_controller = controller;
+  i.remove();
+}
+
+async_test(t => {
+  window.onload = function() {
+    setTimeout(() => {
+      scheduler.postTask(() => {}, { signal: iframe_controller.signal }).then(
+          () => assert_unreached("Promise should be rejected"),
+          t.step_func_done());
+    }, 0);
+  };
+}, 'Test scheduler.postTask() with a signal from a detached iframe');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/global_task_queues.html b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_without_signals.html
similarity index 71%
rename from third_party/blink/web_tests/wpt_internal/scheduler/global_task_queues.html
rename to third_party/blink/web_tests/wpt_internal/scheduler/post_task_without_signals.html
index 08e68bbc..8c6d1d1 100644
--- a/third_party/blink/web_tests/wpt_internal/scheduler/global_task_queues.html
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/post_task_without_signals.html
@@ -17,14 +17,10 @@
       yield priorities[i];
   }
 
-  function testTaskQueue(priority) {
-    let tq = scheduler.getTaskQueue(priority);
-    assert_equals(tq.priority, priority);
-
-    let task = tq.postTask(t.step_func(() => {
+  function testPriority(priority) {
+    let task = scheduler.postTask(t.step_func(() => {
       nextTaskQueue();
-    }));
-    assert_equals(task.priority, priority);
+    }), { priority: priority });
   }
 
   let nextPriority = priorityGenerator();
@@ -35,13 +31,13 @@
       t.done();
       return;
     }
-    testTaskQueue(next.value);
+    testPriority(next.value);
   }
 
   // Schedule a task to kick things off.
   scheduler.postTask(t.step_func(() => {
     nextTaskQueue();
   }));
-}, 'Basic functionality for the global TaskQueues returned by scheduler.taskQueue');
+}, 'Basic functionality for scheduler.postTask() without using a task signal');
 
 </script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/support/create_and_pass_signal.html b/third_party/blink/web_tests/wpt_internal/scheduler/support/create_and_pass_signal.html
new file mode 100644
index 0000000..e36c798b
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/support/create_and_pass_signal.html
@@ -0,0 +1,5 @@
+<body>
+<script>
+parent.passController(new TaskController());
+</script>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/support/post_task_then_detach_iframe.html b/third_party/blink/web_tests/wpt_internal/scheduler/support/post_task_then_detach_iframe.html
new file mode 100644
index 0000000..737da320
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/support/post_task_then_detach_iframe.html
@@ -0,0 +1,8 @@
+<body>
+<script>
+scheduler.postTask(p => {
+  p.postMessage("fail", "*");
+}, window.parent);
+frameElement.remove();
+</script>
+</body>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_cancellation.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_cancellation.html
deleted file mode 100644
index 0420411..0000000
--- a/third_party/blink/web_tests/wpt_internal/scheduler/task_cancellation.html
+++ /dev/null
@@ -1,46 +0,0 @@
-<!doctype html>
-<title>Scheduling API: Task Cancellation</title>
-<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
-<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<script>
-'use strict';
-
-async_test(t => {
-  let result = '';
-  let tasks = [];
-
-  for (let i = 0; i < 5; i++) {
-    let task = scheduler.postTask(() => {
-      result += i.toString();
-    });
-    tasks.push(task);
-  }
-
-  tasks[2].cancel();
-  assert_equals(tasks[2].status, 'canceled');
-
-  scheduler.postTask(t.step_func(() => {
-    assert_equals(result, '0134');
-  }));
-
-  // Check that canceling running, completed, or canceled tasks is a no-op.
-  let task = scheduler.postTask(t.step_func_done(() => {
-    assert_equals(task.status, 'running');
-    task.cancel();
-    assert_equals(task.status, 'running');
-
-    assert_equals(tasks[2].status, 'canceled');
-    tasks[2].cancel();
-    assert_equals(tasks[2].status, 'canceled');
-
-    assert_equals(tasks[0].status, 'completed');
-    tasks[0].cancel();
-    assert_equals(tasks[0].status, 'completed');
-  }));
-
-}, 'Test canceling a task');
-
-</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_controller_abort.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_controller_abort.html
new file mode 100644
index 0000000..cb004cc
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_controller_abort.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<title>Scheduling API: Task Cancellation</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  let result = '';
+  let task_controllers = [];
+
+  for (let i = 0; i < 5; i++) {
+    let tc = new TaskController("default");
+    let task = scheduler.postTask(() => {
+      result += i.toString();
+    }, { signal: tc.signal });
+    if (i == 2) {
+      task.then(() => {
+        assert_unreached('This task should have been aborted');
+      });
+    } else {
+      task.catch(() => {
+        assert_unreached('This task should complete');
+      });
+    }
+    task_controllers.push(tc);
+  }
+
+  task_controllers[2].abort();
+  scheduler.postTask(t.step_func(() => {
+    assert_equals(result, '0134');
+  }));
+
+  let final_task_tc = new TaskController("default");
+  // Check that canceling running, completed, or canceled tasks is a no-op.
+  scheduler.postTask(t.step_func_done(() => {
+    final_task_tc.abort();
+    task_controllers[2].abort();
+    task_controllers[0].abort();
+  }), { signal: final_task_tc.signal });
+
+}, 'Test canceling a task');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_controller_abort_with_custom_priority.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_controller_abort_with_custom_priority.html
new file mode 100644
index 0000000..556beaf1
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_controller_abort_with_custom_priority.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>Scheduling API: TaskController.abort()</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  let result = 0;
+  let tc = new TaskController("default");
+
+  scheduler.postTask(() => {}, { signal: tc.signal }).then(
+      () => { assert_unreached('This task should have been aborted'); },
+      () => { result++; });
+  scheduler.postTask(() => {}, { priority: "idle", signal: tc.signal }).then(
+      () => { assert_unreached('This task should have been aborted'); },
+      () => { result++; });
+  tc.abort();
+
+  scheduler.postTask(t.step_func_done(() => {
+    assert_equals(result, 2);
+  }), { priority: "idle" });
+
+}, 'Test that when scheduler.postTask() is given both a signal and priority. the signal abort is honored');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_controller_setPriority.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_controller_setPriority.html
new file mode 100644
index 0000000..7499058
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_controller_setPriority.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>Scheduling API: TaskController.setPriority()</title>
+<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
+<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<script>
+'use strict';
+
+async_test(t => {
+  let result = '';
+  let tc = new TaskController("default");
+
+  for (let i = 0; i < 5; i++) {
+    let task = scheduler.postTask(() => {
+      result += i.toString();
+    }, { signal: tc.signal });
+  }
+
+  scheduler.postTask(() => { result += "5"; }, { priority : "high" });
+  scheduler.postTask(() => { result += "6"; }, { priority : "default" });
+  scheduler.postTask(() => { result += "7"; }, { priority : "low" });
+
+  tc.setPriority("idle");
+
+  scheduler.postTask(t.step_func_done(() => {
+    assert_equals(result, '56701234');
+  }), { priority: "idle" });
+
+}, 'Test that TaskController.setPriority() changes the priority of all associated tasks');
+
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_lifecycle.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_lifecycle.html
deleted file mode 100644
index c2c09215..0000000
--- a/third_party/blink/web_tests/wpt_internal/scheduler/task_lifecycle.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<!doctype html>
-<title>Scheduling API: Task Lifecycle</title>
-<link rel="author" title="Scott Haseley" href="mailto:shaseley@chromium.org">
-<link rel="help" href="https://github.com/WICG/main-thread-scheduling">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-
-<script>
-'use strict';
-
-async_test(t => {
-  let task = scheduler.postTask(t.step_func(() => {
-    assert_equals(task.status, 'running');
-    scheduler.postTask(t.step_func_done(() => {
-      assert_equals(task.status, 'completed');
-    }));
-  }));
-  assert_equals(task.priority, 'default');
-  assert_equals(task.status, 'pending');
-
-}, 'Test scheduler.postTask Task lifecycle');
-
-</script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_access_result_after_task_runs.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_access_result_after_task_runs.html
index 1c67eec..be94a4b7 100644
--- a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_access_result_after_task_runs.html
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_access_result_after_task_runs.html
@@ -10,11 +10,10 @@
 
 async_test(t => {
   (function() {
-    let task = scheduler.postTask(t.step_func(() => {
+    let task_promise = scheduler.postTask(t.step_func(() => {
       // This task will run after |task| finishes.
       scheduler.postTask(t.step_func(() => {
-        assert_equals(task.status,  'completed');
-        task.result.then(t.step_func_done((res) => {
+        task_promise.then(t.step_func_done((res) => {
           assert_equals(res, 1234);
         }));
       }));
@@ -22,6 +21,6 @@
     }));
   })();
 
-}, 'Test task.result is resolved properly when accessed after the task runs');
+}, 'Test task result is resolved properly when accessed after the task runs');
 
 </script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_access_result_before_task_runs.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_access_result_before_task_runs.html
index b04a0e2..f5a39eb 100644
--- a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_access_result_before_task_runs.html
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_access_result_before_task_runs.html
@@ -10,13 +10,10 @@
 
 async_test(t => {
   (function() {
-    let task = scheduler.postTask(() => 1234);
-    task.result.then(t.step_func_done((res) => {
-      assert_equals(task.status, 'completed');
+    scheduler.postTask(() => 1234).then(t.step_func_done((res) => {
       assert_equals(res, 1234);
     }));
   })();
-
-}, 'Test task.result is resolved properly when accessed before the task runs');
+}, 'Test task result is resolved properly when accessed before the task runs');
 
 </script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_cancel_after_accessing_result.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_cancel_after_accessing_result.html
index 2c0fd99c..d642071 100644
--- a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_cancel_after_accessing_result.html
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_cancel_after_accessing_result.html
@@ -10,15 +10,16 @@
 
 async_test(t => {
   (function() {
-    let task = scheduler.postTask(() => 1234);
-    task.result.then(t.step_func((res) => {
-      assert_true(false, 'task.result should not be fulfilled when the task is canceled.');
+    let tc = new TaskController("default");
+    let task_promise = scheduler.postTask(() => 1234, { signal: tc.signal });
+    task_promise.then(t.step_func((res) => {
+      assert_true(false, 'task promise should not be fulfilled when the task is canceled.');
     }))
     .catch(t.step_func_done((e) => {
       assert_equals(e.name, 'AbortError');
     }));
-    task.cancel();
+    tc.abort();
   })();
-}, 'Test task.result is rejected properly when the task is canceled after accessing task.result');
+}, 'Test task result is rejected properly when the task is canceled after accessing the result');
 
 </script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_cancel_before_accessing_result.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_cancel_before_accessing_result.html
index 5624281..e95b8510 100644
--- a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_cancel_before_accessing_result.html
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_cancel_before_accessing_result.html
@@ -10,15 +10,16 @@
 
 async_test(t => {
   (function() {
-    let task = scheduler.postTask(() => 1234);
-    task.cancel();
-    task.result.then(t.step_func((res) => {
-      assert_true(false, 'task.result should not be fulfilled when the task is canceled.');
+    let tc = new TaskController("default");
+    let task_promise = scheduler.postTask(() => 1234, { signal: tc.signal });
+    tc.abort();
+    task_promise.then(t.step_func((res) => {
+      assert_true(false, 'task promise should not be fulfilled when the task is canceled.');
     }))
     .catch(t.step_func_done((e) => {
       assert_equals(e.name, 'AbortError');
     }));
   })();
-}, 'Test task.result is rejected properly when the task is canceled before accessing task.result');
+}, 'Test task result is rejected properly when the task is canceled before accessing the result');
 
 </script>
diff --git a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_error.html b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_error.html
index 556256a..004900a 100644
--- a/third_party/blink/web_tests/wpt_internal/scheduler/task_result_error.html
+++ b/third_party/blink/web_tests/wpt_internal/scheduler/task_result_error.html
@@ -14,15 +14,15 @@
 
 async_test(t => {
   (function() {
-    let task = scheduler.postTask(() => { throw Error('Failed'); });
-    task.result.then(t.step_func((res) => {
-      assert_true(false, 'task.result should not be fulfilled when the task throws an error.');
+    let task_promise = scheduler.postTask(() => { throw Error('Failed'); });
+    task_promise.then(t.step_func((res) => {
+      assert_true(false, 'task promise should not be fulfilled when the task throws an error.');
     }))
     .catch(t.step_func_done((e) => {
       assert_equals(e.name, 'Error');
       assert_equals(e.message, 'Failed');
     }));
   })();
-}, 'Test task.result is rejected properly when the task throws an error');
+}, 'Test task promise is rejected properly when the task throws an error');
 
 </script>
diff --git a/tools/binary_size/libsupersize/caspian/BUILD.gn b/tools/binary_size/libsupersize/caspian/BUILD.gn
index ba2eb43..dc48257 100644
--- a/tools/binary_size/libsupersize/caspian/BUILD.gn
+++ b/tools/binary_size/libsupersize/caspian/BUILD.gn
@@ -19,6 +19,10 @@
     "file_format.h",
     "function_signature.cc",
     "function_signature.h",
+    "grouped_path.cc",
+    "grouped_path.h",
+    "lens.cc",
+    "lens.h",
     "model.cc",
     "model.h",
     "tree_builder.cc",
@@ -40,6 +44,8 @@
   sources = [
     "diff_test.cc",
     "function_signature_test.cc",
+    "grouped_path_test.cc",
+    "lens_test.cc",
     "tree_builder_test.cc",
   ]
   deps = [
diff --git a/tools/binary_size/libsupersize/caspian/caspian_web.cc b/tools/binary_size/libsupersize/caspian/caspian_web.cc
index 92b878c..bb95479 100644
--- a/tools/binary_size/libsupersize/caspian/caspian_web.cc
+++ b/tools/binary_size/libsupersize/caspian/caspian_web.cc
@@ -17,6 +17,7 @@
 #include "third_party/re2/src/re2/re2.h"
 #include "tools/binary_size/libsupersize/caspian/diff.h"
 #include "tools/binary_size/libsupersize/caspian/file_format.h"
+#include "tools/binary_size/libsupersize/caspian/lens.h"
 #include "tools/binary_size/libsupersize/caspian/model.h"
 #include "tools/binary_size/libsupersize/caspian/tree_builder.h"
 
@@ -143,6 +144,8 @@
     filters.push_back([](const BaseSymbol& sym) -> bool {
       return sym.IsTemplate() && sym.IsNative();
     });
+  } else if (!strcmp(group_by, "generated_type")) {
+    lens = std::make_unique<GeneratedLens>();
   } else {
     // TODO(jaspercb): Support group by generated path type.
     std::cerr << "Unsupported group_by=" << group_by << std::endl;
diff --git a/tools/binary_size/libsupersize/caspian/grouped_path.cc b/tools/binary_size/libsupersize/caspian/grouped_path.cc
new file mode 100644
index 0000000..6330f885
--- /dev/null
+++ b/tools/binary_size/libsupersize/caspian/grouped_path.cc
@@ -0,0 +1,74 @@
+// 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 "tools/binary_size/libsupersize/caspian/grouped_path.h"
+
+#include <stdint.h>
+
+#include <tuple>
+
+namespace {
+
+// Returns |s| with the last |sep| + [suffix] removed, or "" if |sep| is not
+// found.
+std::string_view RemoveLastSegment(std::string_view s, char sep) {
+  size_t sep_idx = s.find_last_of(sep);
+  return s.substr(0, (sep_idx == std::string_view::npos) ? 0 : sep_idx);
+}
+
+}  // namespace
+
+namespace caspian {
+
+GroupedPath::GroupedPath() = default;
+GroupedPath::GroupedPath(const GroupedPath& other) = default;
+GroupedPath::GroupedPath(std::string_view group_in, std::string_view path_in)
+    : group(group_in), path(path_in) {}
+GroupedPath::~GroupedPath() = default;
+
+std::string_view GroupedPath::ShortName(char group_separator) const {
+  if (path.empty()) {
+    // If there's no group separator, return entire group name;
+    return group.substr(group.find_last_of(group_separator) + 1);
+  }
+  // If there's no path separator, return entire path name.
+  return path.substr(path.find_last_of('/') + 1);
+}
+
+GroupedPath GroupedPath::Parent(char group_separator) const {
+  if (path.empty()) {
+    return GroupedPath{RemoveLastSegment(group, group_separator), path};
+  }
+  return GroupedPath{group, RemoveLastSegment(path, '/')};
+}
+
+bool GroupedPath::IsTopLevelPath() const {
+  return std::string_view::npos == path.find_first_of('/');
+}
+
+bool GroupedPath::operator==(const GroupedPath& other) const {
+  return group == other.group && path == other.path;
+}
+
+std::string GroupedPath::ToString() const {
+  std::string ret;
+  ret.reserve(size());
+  ret += group;
+  if (!group.empty() && !path.empty()) {
+    ret += "/";
+  }
+  ret += path;
+  return ret;
+}
+
+bool GroupedPath::operator<(const GroupedPath& other) const {
+  return std::tie(group, path) < std::tie(other.group, other.path);
+}
+
+std::ostream& operator<<(std::ostream& os, const GroupedPath& path) {
+  return os << "GroupedPath(group=\"" << path.group << "\", path=\""
+            << path.path << "\")";
+}
+
+}  // namespace caspian
diff --git a/tools/binary_size/libsupersize/caspian/grouped_path.h b/tools/binary_size/libsupersize/caspian/grouped_path.h
new file mode 100644
index 0000000..277962a5
--- /dev/null
+++ b/tools/binary_size/libsupersize/caspian/grouped_path.h
@@ -0,0 +1,60 @@
+// 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 TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_GROUPED_PATH_H_
+#define TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_GROUPED_PATH_H_
+
+#include <stddef.h>
+
+#include <functional>
+#include <ostream>
+#include <string>
+#include <string_view>
+
+namespace caspian {
+struct GroupedPath {
+  // TreeNode id_paths can be grouped by component or template, for example
+  // "Blink>JavaScript/v8/natives_blob.bin/assets" has the base path
+  // "v8/natives_blob.bin/assets" and has been grouped to the component
+  // "Blink>JavaScript". This is a lightweight utility class for managing this
+  // two-level path structure without wasteful string allocations.
+  GroupedPath();
+  GroupedPath(const GroupedPath& other);
+  GroupedPath(std::string_view group_in, std::string_view path_in);
+  ~GroupedPath();
+
+  // Returns |ToString.size()| without actually creating the string.
+  size_t size() const {
+    size_t sep_size = (group.empty() || path.empty()) ? 0 : 1;  // For '/'.
+    return group.size() + path.size() + sep_size;
+  }
+
+  std::string_view ShortName(char group_separator) const;
+  GroupedPath Parent(char group_separator) const;
+  bool IsTopLevelPath() const;
+  bool empty() const { return path.empty() && group.empty(); }
+  bool operator==(const GroupedPath& other) const;
+  std::string ToString() const;
+
+  bool operator<(const GroupedPath& other) const;
+
+  std::string_view group;
+  std::string_view path;
+};
+
+std::ostream& operator<<(std::ostream& os, const GroupedPath& path);
+
+}  // namespace caspian
+
+namespace std {
+template <>
+struct hash<caspian::GroupedPath> {
+  std::size_t operator()(const caspian::GroupedPath& grouped_path) const
+      noexcept {
+    return std::hash<std::string_view>{}(grouped_path.group) ^
+           std::hash<std::string_view>{}(grouped_path.path);
+  }
+};
+}  // namespace std
+#endif  // TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_GROUPED_PATH_H_
diff --git a/tools/binary_size/libsupersize/caspian/grouped_path_test.cc b/tools/binary_size/libsupersize/caspian/grouped_path_test.cc
new file mode 100644
index 0000000..77f41ac
--- /dev/null
+++ b/tools/binary_size/libsupersize/caspian/grouped_path_test.cc
@@ -0,0 +1,95 @@
+// 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 "tools/binary_size/libsupersize/caspian/grouped_path.h"
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/binary_size/libsupersize/caspian/model.h"
+
+namespace caspian {
+namespace {
+
+void CheckParentChain(std::vector<GroupedPath> paths, char sep) {
+  for (unsigned int i = 1; i < paths.size(); i++) {
+    EXPECT_EQ(paths[i], paths[i - 1].Parent(sep));
+  }
+}
+
+}  // namespace
+
+TEST(PathTest, TestBasic) {
+  std::vector<GroupedPath> paths{{"group", "foo/bar"},
+                                 {"group", "foo"},
+                                 {"group", ""},
+                                 {"", ""},
+                                 {"", ""}};
+  CheckParentChain(paths, '>');
+}
+
+TEST(PathTest, TestEmptyGroup) {
+  std::vector<GroupedPath> paths{
+      {"", "foo/bar/baz"}, {"", "foo/bar"}, {"", "foo"}, {"", ""}};
+  CheckParentChain(paths, '>');
+}
+
+TEST(PathTest, TestComponent) {
+  std::vector<GroupedPath> paths{
+      {"A>B>C", "foo"}, {"A>B>C", ""}, {"A>B", ""}, {"A", ""}, {"", ""}};
+  CheckParentChain(paths, '>');
+}
+
+TEST(PathTest, TestGroupPaths) {
+  std::vector<GroupedPath> paths{
+      {"a/b/c", "foo"}, {"a/b/c", ""}, {"a/b", ""}, {"a", ""}, {"", ""}};
+  CheckParentChain(paths, '/');
+}
+
+TEST(PathTest, TestNoSplitOnAngleBracketInPath) {
+  std::vector<GroupedPath> paths{
+      {"a/b/c", "operator>"}, {"a/b/c", ""}, {"a/b", ""}, {"a", ""}, {"", ""}};
+  CheckParentChain(paths, '/');
+}
+
+TEST(PathTest, TestNoSplitOnAngleBracketInGroup) {
+  std::vector<GroupedPath> paths{{"operator<>(foo)", ""}, {"", ""}};
+  CheckParentChain(paths, '/');
+}
+
+TEST(PathTest, TestIsTopLevelPath) {
+  EXPECT_TRUE((GroupedPath{"operator<>(foo)", "operator>"}.IsTopLevelPath()));
+  EXPECT_FALSE((GroupedPath{"", "a/b"}.IsTopLevelPath()));
+  EXPECT_TRUE((GroupedPath{"", "a"}.IsTopLevelPath()));
+  EXPECT_FALSE((GroupedPath{"a", "b/c"}.IsTopLevelPath()));
+
+  EXPECT_TRUE((GroupedPath{"foo", ""}.IsTopLevelPath()));
+  EXPECT_TRUE((GroupedPath{"", ""}.IsTopLevelPath()));
+}
+
+TEST(PathTest, TestComparison) {
+  EXPECT_TRUE((GroupedPath{"a", "b/c"}) < (GroupedPath{"a", "b/d"}));
+  EXPECT_FALSE((GroupedPath{"a", "b/c"}) < (GroupedPath{"a", "b/b"}));
+
+  EXPECT_FALSE((GroupedPath{"a", "b/c"}) < (GroupedPath{"a", "b/c"}));
+  EXPECT_TRUE((GroupedPath{"a", "b/c"}) < (GroupedPath{"a", "b/c/d"}));
+
+  EXPECT_TRUE((GroupedPath{"b", "c/c"}) < (GroupedPath{"c", "b/b"}));
+  EXPECT_FALSE((GroupedPath{"b", "a/c"}) < (GroupedPath{"a", "b/b"}));
+}
+
+TEST(PathTest, TestShortname) {
+  EXPECT_EQ("Blink", (GroupedPath{"Blink", ""}).ShortName('>'));
+  EXPECT_EQ("Foo", (GroupedPath{"Blink>Foo", ""}).ShortName('>'));
+
+  EXPECT_EQ("template<>", (GroupedPath{"a/template<>", ""}).ShortName('/'));
+
+  EXPECT_EQ("Bar", (GroupedPath{"Blink>Foo", "Bar"}).ShortName('>'));
+  EXPECT_EQ("c", (GroupedPath{"a", "b/c"}).ShortName('>'));
+  EXPECT_EQ("c", (GroupedPath{"a", "b/c"}).ShortName('>'));
+}
+
+}  // namespace caspian
diff --git a/tools/binary_size/libsupersize/caspian/lens.cc b/tools/binary_size/libsupersize/caspian/lens.cc
new file mode 100644
index 0000000..7011e4f
--- /dev/null
+++ b/tools/binary_size/libsupersize/caspian/lens.cc
@@ -0,0 +1,117 @@
+// 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 "tools/binary_size/libsupersize/caspian/lens.h"
+
+#include <string>
+
+#include "third_party/re2/src/re2/re2.h"
+#include "tools/binary_size/libsupersize/caspian/model.h"
+
+namespace {
+
+constexpr const char* kNoComponent = "(No component)";
+
+bool PartialMatch(std::string_view view, const RE2& regex) {
+  re2::StringPiece piece(view.data(), view.size());
+  return RE2::PartialMatch(piece, regex);
+}
+
+bool PartialMatch(const char* string, const RE2& regex) {
+  if (string) {
+    return PartialMatch(std::string_view(string), regex);
+  }
+  return false;
+}
+
+}  // namespace
+
+namespace caspian {
+
+std::string_view IdPathLens::ParentName(const BaseSymbol& symbol) {
+  return "";
+}
+
+std::string_view ComponentLens::ParentName(const BaseSymbol& symbol) {
+  std::string component;
+  if (symbol.Component() && *symbol.Component()) {
+    return symbol.Component();
+  }
+  return kNoComponent;
+}
+
+std::string_view TemplateLens::ParentName(const BaseSymbol& symbol) {
+  return symbol.Name();
+}
+
+std::string_view GeneratedLens::ParentName(const BaseSymbol& symbol) {
+  static LazyRE2 register_jni_regex = {
+      R"(Register.*JNIEnv\*\)|RegisteredMethods$)"};
+  if (PartialMatch(symbol.FullName(), *register_jni_regex)) {
+    return "RegisterJNI";
+  }
+
+  static LazyRE2 gl_bindings_autogen_regex = {"gl_bindings_autogen"};
+  if (PartialMatch(symbol.SourcePath(), *gl_bindings_autogen_regex) ||
+      PartialMatch(symbol.ObjectPath(), *gl_bindings_autogen_regex)) {
+    return "gl_bindings_autogen";
+  }
+  if (!symbol.IsGeneratedSource()) {
+    return "Not generated";
+  }
+
+  static LazyRE2 java_protobuf_regex = {R"(Proto\.java$)"};
+  if (PartialMatch(symbol.SourcePath(), *java_protobuf_regex)) {
+    return "Java Protocol Buffers";
+  }
+
+  static LazyRE2 cc_protobuf_regex = {R"(/protobuf/|\.pbzero\.o$|\.pb\.o$)"};
+  if (PartialMatch(symbol.ObjectPath(), *cc_protobuf_regex)) {
+    return "C++ Protocol Buffers";
+  }
+
+  static LazyRE2 mojo_regex = {".mojom|^mojo/|^mojo::"};
+  if (PartialMatch(symbol.ObjectPath(), *mojo_regex)) {
+    return "Mojo";
+  }
+
+  static LazyRE2 dev_tools_regex = {R"(\b(?:protocol|devtools)\b)"};
+  if (PartialMatch(symbol.SourcePath(), *dev_tools_regex)) {
+    return "DevTools";
+  }
+
+  static LazyRE2 blink_bindings_regex = {R"((?:blink|WebKit)/.*bindings)"};
+  if (PartialMatch(symbol.ObjectPath(), *blink_bindings_regex)) {
+    return "Blink (bindings)";
+  }
+
+  static LazyRE2 blink_regex = {"WebKit|blink/"};
+  if (PartialMatch(symbol.ObjectPath(), *blink_regex)) {
+    return "Blink (other)";
+  }
+
+  static LazyRE2 v8_builtins = {"embedded.S$"};
+  if (PartialMatch(symbol.ObjectPath(), *v8_builtins)) {
+    return "V8 Builtins";
+  }
+
+  static LazyRE2 prepopulated_engines_regex = {"prepopulated_engines"};
+  if (PartialMatch(symbol.ObjectPath(), *prepopulated_engines_regex)) {
+    return "Metrics-related code";
+  }
+
+  static LazyRE2 gpu_driver_regex = {"gpu_driver_bug_list"};
+  if (PartialMatch(symbol.ObjectPath(), *gpu_driver_regex)) {
+    return "gpu_driver_bug_list_autogen.cc";
+  }
+
+  static LazyRE2 components_policy_regex = {"components/policy"};
+  if (PartialMatch(symbol.ObjectPath(), *components_policy_regex)) {
+    return "components/policy";
+  }
+
+  return "Generated (other)";
+}
+
+}  // namespace caspian
diff --git a/tools/binary_size/libsupersize/caspian/lens.h b/tools/binary_size/libsupersize/caspian/lens.h
new file mode 100644
index 0000000..f3bf6fc
--- /dev/null
+++ b/tools/binary_size/libsupersize/caspian/lens.h
@@ -0,0 +1,40 @@
+// 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 TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_LENS_H_
+#define TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_LENS_H_
+
+#include <string_view>
+
+namespace caspian {
+class BaseSymbol;
+
+class BaseLens {
+ public:
+  virtual ~BaseLens() = default;
+  virtual std::string_view ParentName(const BaseSymbol& symbol) = 0;
+};
+
+class IdPathLens : public BaseLens {
+ public:
+  std::string_view ParentName(const BaseSymbol& symbol) override;
+};
+
+class ComponentLens : public BaseLens {
+ public:
+  std::string_view ParentName(const BaseSymbol& symbol) override;
+};
+
+class TemplateLens : public BaseLens {
+ public:
+  std::string_view ParentName(const BaseSymbol& symbol) override;
+};
+
+class GeneratedLens : public BaseLens {
+ public:
+  std::string_view ParentName(const BaseSymbol& symbol) override;
+};
+}  // namespace caspian
+
+#endif  // TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_LENS_H_
diff --git a/tools/binary_size/libsupersize/caspian/lens_test.cc b/tools/binary_size/libsupersize/caspian/lens_test.cc
new file mode 100644
index 0000000..c3bad03
--- /dev/null
+++ b/tools/binary_size/libsupersize/caspian/lens_test.cc
@@ -0,0 +1,82 @@
+// 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 "tools/binary_size/libsupersize/caspian/lens.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "tools/binary_size/libsupersize/caspian/model.h"
+
+namespace caspian {
+
+TEST(LensTest, TestGeneratedLensRegisterJNI) {
+  Symbol sym;
+  sym.full_name_ =
+      "base::android::JNI_TraceEvent_RegisterEnabledObserver(_JNIEnv*)";
+  sym.section_id_ = SectionId::kText;
+  sym.source_path_ = "a/b/c.java";
+  EXPECT_EQ("RegisterJNI", GeneratedLens().ParentName(sym));
+}
+
+TEST(LensTest, TestGeneratedLensGlBindings) {
+  Symbol sym;
+  sym.full_name_ = "gl::TraceGLApi::glCopyTexImage2DFn(unsigned int, int, int)";
+  sym.section_id_ = SectionId::kText;
+  sym.source_path_ = "a/b/gl_bindings_autogen_gl.cc";
+  EXPECT_EQ("gl_bindings_autogen", GeneratedLens().ParentName(sym));
+}
+
+TEST(LensTest, TestGeneratedLensNotGenerated) {
+  Symbol sym;
+  sym.full_name_ = "NotAGeneratedSymbol";
+  sym.section_id_ = SectionId::kText;
+  sym.source_path_ = "a/b/c.cc";
+  EXPECT_EQ("Not generated", GeneratedLens().ParentName(sym));
+}
+
+TEST(LensTest, TestGeneratedLensJavaProto) {
+  Symbol sym;
+  sym.section_id_ = SectionId::kDex;
+  sym.source_path_ = "a/b/FooProto.java";
+  sym.flags_ |= SymbolFlag::kGeneratedSource;
+  EXPECT_EQ("Java Protocol Buffers", GeneratedLens().ParentName(sym));
+}
+
+TEST(LensTest, TestGeneratedLensCppProto) {
+  Symbol sym;
+  sym.section_id_ = SectionId::kDex;
+  sym.object_path_ = "a/b/sync.pb.o";
+  sym.flags_ |= SymbolFlag::kGeneratedSource;
+  EXPECT_EQ("C++ Protocol Buffers", GeneratedLens().ParentName(sym));
+}
+
+TEST(LensTest, TestGeneratedLensMojo) {
+  Symbol sym;
+  sym.section_id_ = SectionId::kText;
+  sym.object_path_ = "a.mojom";
+  sym.flags_ |= SymbolFlag::kGeneratedSource;
+  EXPECT_EQ("Mojo", GeneratedLens().ParentName(sym));
+}
+
+TEST(LensTest, TestGeneratedLensDevTools) {
+  Symbol sym;
+  sym.section_id_ = SectionId::kText;
+  sym.flags_ |= SymbolFlag::kGeneratedSource;
+
+  sym.source_path_ = "a/b/protocol/Foo.cpp";
+  EXPECT_EQ("DevTools", GeneratedLens().ParentName(sym));
+
+  sym.source_path_ = "a/b/devtools/Foo.cpp";
+  EXPECT_EQ("DevTools", GeneratedLens().ParentName(sym));
+}
+
+TEST(LensTest, TestGeneratedLensBlinkBindings) {
+  Symbol sym;
+  sym.section_id_ = SectionId::kText;
+  sym.flags_ |= SymbolFlag::kGeneratedSource;
+
+  sym.object_path_ = "blink/foo/bindings/bar";
+  EXPECT_EQ("Blink (bindings)", GeneratedLens().ParentName(sym));
+}
+
+}  // namespace caspian
diff --git a/tools/binary_size/libsupersize/caspian/model.cc b/tools/binary_size/libsupersize/caspian/model.cc
index 3574f1fa..41fd0c4 100644
--- a/tools/binary_size/libsupersize/caspian/model.cc
+++ b/tools/binary_size/libsupersize/caspian/model.cc
@@ -5,11 +5,8 @@
 #include "tools/binary_size/libsupersize/caspian/model.h"
 
 #include <algorithm>
-#include <deque>
-#include <functional>
 #include <iostream>
 #include <list>
-#include <string>
 #include <tuple>
 #include <unordered_map>
 
@@ -317,10 +314,10 @@
 DeltaSizeInfo& DeltaSizeInfo::operator=(const DeltaSizeInfo&) = default;
 
 void TreeNode::WriteIntoJson(
-    Json::Value* out,
     int depth,
     std::function<bool(const TreeNode* const& l, const TreeNode* const& r)>
-        compare_func) {
+        compare_func,
+    Json::Value* out) {
   if (symbol) {
     if (symbol->IsDex()) {
       (*out)["idPath"] = std::string(symbol->FullName());
@@ -328,39 +325,45 @@
       (*out)["idPath"] = std::string(symbol->TemplateName());
       (*out)["fullName"] = std::string(symbol->FullName());
     }
+    if (symbol->SourcePath()) {
+      (*out)["srcPath"] = symbol->SourcePath();
+    }
+    if (symbol->Component()) {
+      (*out)["component"] = symbol->Component();
+    }
   } else {
-    (*out)["idPath"] = std::string(this->id_path);
+    (*out)["idPath"] = id_path.ToString();
   }
-  (*out)["shortNameIndex"] = this->short_name_index;
+  (*out)["shortNameIndex"] = short_name_index;
   std::string type;
   if (container_type != ContainerType::kSymbol) {
     type += static_cast<char>(container_type);
   }
-  SectionId biggest_section = this->node_stats.ComputeBiggestSection();
+  SectionId biggest_section = node_stats.ComputeBiggestSection();
   type += static_cast<char>(biggest_section);
   (*out)["type"] = type;
 
-  (*out)["size"] = this->size;
-  (*out)["flags"] = this->flags;
-  this->node_stats.WriteIntoJson(&(*out)["childStats"]);
+  (*out)["size"] = size;
+  (*out)["flags"] = flags;
+  node_stats.WriteIntoJson(&(*out)["childStats"]);
 
   const size_t kMaxChildNodesToExpand = 1000;
-  if (this->children.size() > kMaxChildNodesToExpand) {
+  if (children.size() > kMaxChildNodesToExpand) {
     // When the tree is very flat, don't expand child nodes to avoid cost of
     // sending thousands of children and grandchildren to renderer.
     depth = 0;
   }
 
-  if (depth < 0 && this->children.size() > 1) {
+  if (depth < 0 && children.size() > 1) {
     (*out)["children"] = Json::Value();  // null
   } else {
     (*out)["children"] = Json::Value(Json::arrayValue);
     // Reorder children for output.
     // TODO: Support additional compare functions.
-    std::sort(this->children.begin(), this->children.end(), compare_func);
-    for (unsigned int i = 0; i < this->children.size(); i++) {
-      this->children[i]->WriteIntoJson(&(*out)["children"][i], depth - 1,
-                                       compare_func);
+    std::sort(children.begin(), children.end(), compare_func);
+    for (unsigned int i = 0; i < children.size(); i++) {
+      children[i]->WriteIntoJson(depth - 1, compare_func,
+                                 &(*out)["children"][i]);
     }
   }
 }
@@ -389,7 +392,7 @@
 
 void NodeStats::WriteIntoJson(Json::Value* out) const {
   (*out) = Json::Value(Json::objectValue);
-  for (const auto kv : this->child_stats) {
+  for (const auto kv : child_stats) {
     const std::string sectionId = std::string(1, static_cast<char>(kv.first));
     const Stat stats = kv.second;
     (*out)[sectionId] = Json::Value(Json::objectValue);
diff --git a/tools/binary_size/libsupersize/caspian/model.h b/tools/binary_size/libsupersize/caspian/model.h
index 641e5ae..f8523b0e 100644
--- a/tools/binary_size/libsupersize/caspian/model.h
+++ b/tools/binary_size/libsupersize/caspian/model.h
@@ -12,11 +12,13 @@
 #include <deque>
 #include <functional>
 #include <map>
+#include <ostream>
 #include <string>
 #include <string_view>
 #include <vector>
 
 #include "third_party/jsoncpp/source/include/json/json.h"
+#include "tools/binary_size/libsupersize/caspian/grouped_path.h"
 
 // Copied from representation in tools/binary_size/libsupersize/models.py
 
@@ -52,6 +54,20 @@
   kRemoved = 3,
 };
 
+class SymbolFlag {
+ public:
+  static const int32_t kAnonymous = 1;
+  static const int32_t kStartup = 2;
+  static const int32_t kUnlikely = 4;
+  static const int32_t kRel = 8;
+  static const int32_t kRelLocal = 16;
+  static const int32_t kGeneratedSource = 32;
+  static const int32_t kClone = 64;
+  static const int32_t kHot = 128;
+  static const int32_t kCovered = 256;
+  static const int32_t kUncompressed = 512;
+};
+
 class Symbol;
 
 class BaseSymbol {
@@ -90,7 +106,11 @@
     return aliases ? aliases->size() : 1;
   }
 
-  bool IsTemplate() const { return Name().size() != TemplateName().size(); }
+  bool IsTemplate() const {
+    // Because of the way these are derived from |FullName|, they have the
+    // same contents if and only if they have the same length.
+    return Name().size() != TemplateName().size();
+  }
 
   bool IsOverhead() const { return FullName().substr(0, 10) == "Overhead: "; }
 
@@ -121,6 +141,10 @@
     return !full_name.empty() && full_name[0] == '"';
   }
 
+  bool IsGeneratedSource() const {
+    return Flags() & SymbolFlag::kGeneratedSource;
+  }
+
   bool IsNameUnique() const {
     return !(IsStringLiteral() || IsOverhead() ||
              (!FullName().empty() && FullName()[0] == '*') ||
@@ -290,7 +314,7 @@
 struct NodeStats {
   NodeStats();
   ~NodeStats();
-  NodeStats(const BaseSymbol& symbol);
+  explicit NodeStats(const BaseSymbol& symbol);
   void WriteIntoJson(Json::Value* out) const;
   NodeStats& operator+=(const NodeStats& other);
   SectionId ComputeBiggestSection() const;
@@ -305,10 +329,9 @@
 
   using CompareFunc =
       std::function<bool(const TreeNode* const& l, const TreeNode* const& r)>;
+  void WriteIntoJson(int depth, CompareFunc compare_func, Json::Value* out);
 
-  void WriteIntoJson(Json::Value* out, int depth, CompareFunc compare_func);
-
-  std::string_view id_path;
+  GroupedPath id_path;
   const char* src_path = nullptr;
   const char* component = nullptr;
   float size = 0.0f;
diff --git a/tools/binary_size/libsupersize/caspian/tree_builder.cc b/tools/binary_size/libsupersize/caspian/tree_builder.cc
index 6ba84d1..a03f61c 100644
--- a/tools/binary_size/libsupersize/caspian/tree_builder.cc
+++ b/tools/binary_size/libsupersize/caspian/tree_builder.cc
@@ -7,65 +7,17 @@
 #include <algorithm>
 #include <iostream>
 #include <map>
+#include <utility>
 
 namespace caspian {
 
 namespace {
 /** Name used by a directory created to hold symbols with no name. */
+constexpr const char kComponentSep = '>';
+constexpr const char kPathSep = '/';
 constexpr const char* kNoName = "(No path)";
-constexpr const char* kNoComponent = "(No component)";
-const char kPathSep = '/';
-const char kComponentSep = '>';
-
-size_t LastSeparatorIndex(std::string_view str, char sep, char othersep) {
-  size_t sep_idx = str.find_last_of(sep);
-  size_t path_idx = str.find_last_of(othersep);
-  if (sep_idx != std::string_view::npos && path_idx != std::string_view::npos) {
-    return std::max(sep_idx, path_idx);
-  } else if (sep_idx == std::string_view::npos) {
-    return path_idx;
-  } else {
-    return sep_idx;
-  }
-}
-
-std::string_view DirName(std::string_view path, char sep, char othersep) {
-  size_t sep_idx = LastSeparatorIndex(path, sep, othersep);
-  return sep_idx != std::string_view::npos ? path.substr(0, sep_idx)
-                                           : std::string_view();
-}
 }  // namespace
 
-std::string_view IdPathLens::ParentName(
-    const BaseSymbol& symbol,
-    std::deque<std::string>* owned_strings) {
-  const char* source_path = symbol.SourcePath();
-  return source_path && *source_path ? source_path : symbol.ObjectPath();
-}
-
-std::string_view ComponentLens::ParentName(
-    const BaseSymbol& symbol,
-    std::deque<std::string>* owned_strings) {
-  std::string component;
-  if (symbol.Component() && *symbol.Component()) {
-    component = symbol.Component();
-  } else {
-    component = kNoComponent;
-  }
-  owned_strings->push_back(component + std::string(1, kComponentSep) +
-                           std::string(symbol.SourcePath()));
-  return owned_strings->back();
-}
-
-std::string_view TemplateLens::ParentName(
-    const BaseSymbol& symbol,
-    std::deque<std::string>* owned_strings) {
-  owned_strings->push_back(std::string(symbol.Name()) + std::string(1, '/') +
-                           std::string(symbol.SourcePath()));
-  return owned_strings->back();
-  // return symbol.Name();
-}
-
 TreeBuilder::TreeBuilder(SizeInfo* size_info) {
   symbols_.reserve(size_info->raw_symbols.size());
   for (const Symbol& sym : size_info->raw_symbols) {
@@ -94,95 +46,123 @@
 
   // Initialize tree root.
   root_.container_type = ContainerType::kDirectory;
-  owned_strings_.emplace_back(1, sep_);
-  root_.id_path = owned_strings_.back();
-  _parents[""] = &root_;
+  root_.id_path = GroupedPath{"", ""};
+  _parents[root_.id_path] = &root_;
 
-  std::unordered_map<std::string_view, std::vector<const BaseSymbol*>>
-      symbols_by_source_path;
+  std::unordered_map<GroupedPath, std::vector<const BaseSymbol*>>
+      symbols_by_grouped_path;
   for (const BaseSymbol* sym : symbols_) {
     if (ShouldIncludeSymbol(*sym)) {
-      std::string_view key = lens_->ParentName(*sym, &owned_strings_);
-      symbols_by_source_path[key].push_back(sym);
+      GroupedPath key = GroupedPath{
+          lens_->ParentName(*sym),
+          sym->SourcePath() ? sym->SourcePath() : sym->ObjectPath()};
+      symbols_by_grouped_path[key].push_back(sym);
     }
   }
-  for (const auto& pair : symbols_by_source_path) {
+  for (const auto& pair : symbols_by_grouped_path) {
     AddFileEntry(pair.first, pair.second);
   }
 }
 
 namespace {
 bool CompareAbsSize(const TreeNode* const& l, const TreeNode* const& r) {
+  // Sort nodes by size in descending order.
+  // Sort nodes with same size in alphabetically ascending order.
   float l_size = abs(l->size);
   float r_size = abs(r->size);
-  if (l_size == r_size) {
-    return l->id_path < r->id_path;
-  }
-  return abs(l->size) > abs(r->size);
+  return (l_size != r_size) ? abs(l->size) > abs(r->size)
+                            : l->id_path < r->id_path;
 }
 
 bool CompareCount(const TreeNode* const& l, const TreeNode* const& r) {
+  // Sort nodes by size in descending order.
+  // Sort nodes with same count in alphabetically ascending order.
+  // Particularly relevant for method count mode, where we get a lot of dex
+  // symbols with exactly the same size.
   int32_t l_count = l->node_stats.SumCount();
   int32_t r_count = r->node_stats.SumCount();
-  if (l_count == r_count) {
-    return l->id_path < r->id_path;
-  }
-  return l_count > r_count;
+  return (l_count != r_count) ? l_count > r_count : l->id_path < r->id_path;
 }
 }  // namespace
 
+TreeNode* TreeBuilder::Find(std::string_view path) {
+  std::vector<std::string_view> id_paths;
+  while (!path.empty()) {
+    size_t idx = (sep_ == kComponentSep) ? path.find_first_of("/>")
+                                         : path.find_first_of(kPathSep);
+
+    id_paths.push_back(path.substr(0, idx));
+    if (idx == std::string_view::npos) {
+      break;
+    }
+    path = path.substr(idx + 1);
+  }
+
+  TreeNode* node = &root_;
+  for (std::string_view id_path : id_paths) {
+    TreeNode* old_node = node;
+    node = nullptr;
+    for (auto* child : old_node->children) {
+      if (child->id_path.ShortName(sep_) == id_path) {
+        node = child;
+        break;
+      }
+    }
+    if (node == nullptr) {
+      return nullptr;
+    }
+  }
+  return node;
+}
+
 Json::Value TreeBuilder::Open(const char* path) {
   // Returns a string that can be parsed to a JS object.
   static std::string result;
-  const auto node = _parents.find(path);
 
   TreeNode::CompareFunc node_sort_func =
       method_count_mode_ ? &CompareCount : &CompareAbsSize;
 
-  if (node != _parents.end()) {
-    Json::Value v;
-    node->second->WriteIntoJson(&v, 1, node_sort_func);
-    return v;
-  } else {
+  TreeNode* node = Find(path);
+  if (node == nullptr) {
     std::cerr << "Tried to open nonexistent node with path: " << path
               << std::endl;
     exit(1);
   }
+  Json::Value v;
+  node->WriteIntoJson(1, node_sort_func, &v);
+  return v;
 }
 
-void TreeBuilder::AddFileEntry(const std::string_view source_path,
+void TreeBuilder::AddFileEntry(GroupedPath grouped_path,
                                const std::vector<const BaseSymbol*>& symbols) {
   // Creates a single file node with a child for each symbol in that file.
-  TreeNode* file_node = nullptr;
 
   // In legacy .size files, unattributed .dex symbols symbols aggregated and
   // attributed to a path which is actually a directory. Therefore it's
-  // possible that a TreeNode has already been created for |source_path|. This
+  // possible that a TreeNode has already been created for |grouped_path|. This
   // is made slightly more complicated by the fact that _parents[""] is root,
   // but we do want to create a a new (No path) file entry.
-  const auto node = _parents.find(source_path);
-  if (node == _parents.end() || source_path.empty()) {
+
+  TreeNode* file_node = _parents[grouped_path];
+  if (file_node == nullptr || grouped_path.path.empty()) {
     file_node = new TreeNode();
     file_node->container_type = ContainerType::kFile;
 
-    if (source_path.empty()) {
-      file_node->id_path = kNoName;
-    } else {
-      file_node->id_path = source_path;
+    file_node->id_path = grouped_path;
+    if (file_node->id_path.path.empty()) {
+      file_node->id_path.path = kNoName;
     }
 
     file_node->short_name_index =
-        LastSeparatorIndex(file_node->id_path, sep_, kPathSep) + 1;
+        file_node->id_path.size() - file_node->id_path.ShortName(sep_).size();
     _parents[file_node->id_path] = file_node;
-  } else {
-    file_node = node->second;
   }
 
   // Create symbol nodes.
   for (const BaseSymbol* sym : symbols) {
     TreeNode* symbol_node = new TreeNode();
     symbol_node->container_type = ContainerType::kSymbol;
-    symbol_node->id_path = sym->FullName();
+    symbol_node->id_path = GroupedPath{"", sym->FullName()};
     symbol_node->size = sym->Pss();
     symbol_node->node_stats = NodeStats(*sym);
     symbol_node->symbol = sym;
@@ -198,19 +178,14 @@
 }
 
 TreeNode* TreeBuilder::GetOrMakeParentNode(TreeNode* child_node) {
-  std::string_view parent_path;
-  if (child_node->id_path.empty()) {
-    parent_path = kNoName;
-  } else {
-    parent_path = DirName(child_node->id_path, sep_, kPathSep);
-  }
+  GroupedPath parent_path = child_node->id_path.Parent(sep_);
 
   TreeNode*& parent = _parents[parent_path];
   if (parent == nullptr) {
     parent = new TreeNode();
     parent->id_path = parent_path;
     parent->short_name_index =
-        LastSeparatorIndex(parent_path, sep_, kPathSep) + 1;
+        parent->id_path.size() - parent->id_path.ShortName(sep_).size();
     parent->container_type = ContainerTypeFromChild(child_node->id_path);
   }
   if (child_node->parent != parent) {
@@ -224,6 +199,7 @@
     std::cerr << "Child " << child->id_path << " already attached to parent "
               << child->parent->id_path << std::endl;
     std::cerr << "Cannot be attached to " << parent->id_path << std::endl;
+    std::cerr << child->parent << " " << parent << std::endl;
     exit(1);
   }
 
@@ -240,17 +216,13 @@
 }
 
 ContainerType TreeBuilder::ContainerTypeFromChild(
-    std::string_view child_id_path) const {
+    GroupedPath child_path) const {
   // When grouping by component, id paths use '>' separators for components and
   // '/' separators for the file tree - e.g. Blink>third_party/blink/common...
   // We know that Blink is a component because its children have the form
   // Blink>third_party rather than Blink/third_party.
-  size_t idx = LastSeparatorIndex(child_id_path, sep_, kPathSep);
-  if (idx == std::string_view::npos || child_id_path[idx] == kPathSep) {
-    return ContainerType::kDirectory;
-  } else {
-    return ContainerType::kComponent;
-  }
+  return child_path.IsTopLevelPath() ? ContainerType::kComponent
+                                     : ContainerType::kDirectory;
 }
 
 bool TreeBuilder::ShouldIncludeSymbol(const BaseSymbol& symbol) const {
@@ -268,7 +240,7 @@
       node->node_stats.child_stats.count(SectionId::kDex) ||
       node->node_stats.child_stats.count(SectionId::kDexMethod);
   // Don't try to merge dex symbols for catch-all symbols under (No path).
-  const bool is_no_path = node->id_path == kNoName;
+  const bool is_no_path = node->id_path.path == kNoName;
 
   if (!is_file_node || !has_dex || is_no_path || node->children.empty()) {
     return;
@@ -283,24 +255,22 @@
     // the classname, such as "android.support.v7.widget.toolbar".
     // Method names contain the classname followed by the method definition,
     // like "android.support.v7.widget.toolbar void onMeasure(int, int)".
-    const size_t split_index = child->id_path.find_first_of(' ');
+    const size_t split_index = child->id_path.path.find_first_of(' ');
     // No return type / field type means it's a class node.
     const bool is_class_node =
-        child->id_path.find_first_of(' ', child->short_name_index) ==
+        child->id_path.path.find_first_of(' ', child->short_name_index) ==
         std::string_view::npos;
     const bool has_class_prefix =
         is_class_node || split_index != std::string_view::npos;
 
     if (has_class_prefix) {
       const std::string_view class_id_path =
-          split_index == std::string_view::npos
-              ? child->id_path
-              : child->id_path.substr(0, split_index);
+          child->id_path.path.substr(0, split_index);
 
       // Strip package from the node name for classes in .java files since the
       // directory tree already shows it.
       int short_name_index = child->short_name_index;
-      size_t java_idx = node->id_path.find(".java");
+      size_t java_idx = node->id_path.path.find(".java");
       if (java_idx != std::string_view::npos) {
         size_t dot_idx = class_id_path.find_last_of('.');
         short_name_index += dot_idx + 1;
@@ -309,7 +279,7 @@
       TreeNode*& class_node = java_class_containers[class_id_path];
       if (class_node == nullptr) {
         class_node = new TreeNode();
-        class_node->id_path = class_id_path;
+        class_node->id_path = GroupedPath{child->id_path.group, class_id_path};
         class_node->src_path = node->src_path;
         class_node->component = node->component;
         class_node->short_name_index = short_name_index;
diff --git a/tools/binary_size/libsupersize/caspian/tree_builder.h b/tools/binary_size/libsupersize/caspian/tree_builder.h
index 8ee40f7..e7d2db4 100644
--- a/tools/binary_size/libsupersize/caspian/tree_builder.h
+++ b/tools/binary_size/libsupersize/caspian/tree_builder.h
@@ -6,61 +6,38 @@
 #define TOOLS_BINARY_SIZE_LIBSUPERSIZE_CASPIAN_TREE_BUILDER_H_
 
 #include <deque>
+#include <functional>
 #include <memory>
 #include <string>
 #include <string_view>
 #include <unordered_map>
 #include <vector>
 
+#include "tools/binary_size/libsupersize/caspian/lens.h"
 #include "tools/binary_size/libsupersize/caspian/model.h"
 
 namespace caspian {
-class BaseLens {
- public:
-  virtual ~BaseLens() = default;
-  virtual std::string_view ParentName(
-      const BaseSymbol& symbol,
-      std::deque<std::string>* owned_strings) = 0;
-};
-
-class IdPathLens : public BaseLens {
- public:
-  std::string_view ParentName(const BaseSymbol& symbol,
-                              std::deque<std::string>* owned_strings) override;
-};
-
-class ComponentLens : public BaseLens {
- public:
-  std::string_view ParentName(const BaseSymbol& symbol,
-                              std::deque<std::string>* owned_strings) override;
-};
-
-class TemplateLens : public BaseLens {
- public:
-  std::string_view ParentName(const BaseSymbol& symbol,
-                              std::deque<std::string>* owned_strings) override;
-};
-
 class TreeBuilder {
  public:
-  TreeBuilder(SizeInfo* size_info);
-  TreeBuilder(DeltaSizeInfo* size_info);
+  explicit TreeBuilder(SizeInfo* size_info);
+  explicit TreeBuilder(DeltaSizeInfo* size_info);
   ~TreeBuilder();
   void Build(std::unique_ptr<BaseLens> lens,
              char separator,
              bool method_count_mode,
              std::vector<std::function<bool(const BaseSymbol&)>> filters);
+  TreeNode* Find(std::string_view path);
   Json::Value Open(const char* path);
 
  private:
-  void AddFileEntry(const std::string_view source_path,
+  void AddFileEntry(GroupedPath source_path,
                     const std::vector<const BaseSymbol*>& symbols);
 
   TreeNode* GetOrMakeParentNode(TreeNode* child_node);
 
   void AttachToParent(TreeNode* child, TreeNode* parent);
 
-  ContainerType ContainerTypeFromChild(std::string_view child_id_path) const;
+  ContainerType ContainerTypeFromChild(GroupedPath child_path) const;
 
   bool ShouldIncludeSymbol(const BaseSymbol& symbol) const;
 
@@ -69,9 +46,7 @@
   void JoinDexMethodClasses(TreeNode* node);
 
   TreeNode root_;
-  // TODO: A full hash table might be overkill here - could walk tree to find
-  // node.
-  std::unordered_map<std::string_view, TreeNode*> _parents;
+  std::unordered_map<GroupedPath, TreeNode*> _parents;
 
   // Contained TreeNode hold lightweight string_views to fields in SizeInfo.
   // If grouping by component, this isn't possible: TreeNode id_paths are not
diff --git a/tools/binary_size/libsupersize/caspian/tree_builder_test.cc b/tools/binary_size/libsupersize/caspian/tree_builder_test.cc
index 96193d06..a87a60e9 100644
--- a/tools/binary_size/libsupersize/caspian/tree_builder_test.cc
+++ b/tools/binary_size/libsupersize/caspian/tree_builder_test.cc
@@ -17,8 +17,11 @@
 #include "tools/binary_size/libsupersize/caspian/model.h"
 
 namespace caspian {
+
 namespace {
 
+using FilterList = std::vector<std::function<bool(const BaseSymbol&)>>;
+
 void MakeSymbol(SizeInfo* info,
                 SectionId section_id,
                 int32_t size,
@@ -44,6 +47,7 @@
   sym.size_info_ = info;
   info->raw_symbols.push_back(sym);
 }
+}  // namespace
 
 std::unique_ptr<SizeInfo> CreateSizeInfo() {
   std::unique_ptr<SizeInfo> info = std::make_unique<SizeInfo>();
@@ -63,30 +67,30 @@
   }
 }
 
-TEST(DiffTest, TestIdPathLens) {
+TEST(TreeBuilderTest, TestIdPathLens) {
   std::unique_ptr<SizeInfo> size_info = CreateSizeInfo();
 
   TreeBuilder builder(size_info.get());
-  std::vector<std::function<bool(const BaseSymbol&)>> filters;
+  FilterList filters;
   builder.Build(std::make_unique<IdPathLens>(), '/', false, filters);
   CheckAllTreeNodesFindable(builder, builder.Open(""));
-  EXPECT_EQ(builder.Open("")["type"].asString(), "Dt");
+  EXPECT_EQ("Dt", builder.Open("")["type"].asString());
 }
 
-TEST(DiffTest, TestComponentLens) {
+TEST(TreeBuilderTest, TestComponentLens) {
   std::unique_ptr<SizeInfo> size_info = CreateSizeInfo();
 
   TreeBuilder builder(size_info.get());
-  std::vector<std::function<bool(const BaseSymbol&)>> filters;
+  FilterList filters;
   builder.Build(std::make_unique<ComponentLens>(), '>', false, filters);
   CheckAllTreeNodesFindable(builder, builder.Open(""));
-  EXPECT_EQ(builder.Open("A")["type"].asString(), "Ct");
-  EXPECT_EQ(builder.Open("A")["size"].asInt(), 20);
-  EXPECT_EQ(builder.Open("B")["type"].asString(), "Ct");
-  EXPECT_EQ(builder.Open("B")["size"].asInt(), 30);
+  EXPECT_EQ("Ct", builder.Open("A")["type"].asString());
+  EXPECT_EQ(20, builder.Open("A")["size"].asInt());
+  EXPECT_EQ("Ct", builder.Open("B")["type"].asString());
+  EXPECT_EQ(30, builder.Open("B")["size"].asInt());
 }
 
-TEST(DiffTest, TestTemplateLens) {
+TEST(TreeBuilderTest, TestTemplateLens) {
   std::unique_ptr<SizeInfo> size_info = std::make_unique<SizeInfo>();
   MakeSymbol(size_info.get(), SectionId::kText, 20, "a/b/c", "A",
              "base::internal::Invoker<base::internal::BindState<void "
@@ -99,19 +103,65 @@
              "base::WeakPtr<autofill_assistant::Controller>, unsigned int>, "
              "void ()>::RunOnce(base::internal::BindStateBase*)");
 
-  for (Symbol& sym : size_info->raw_symbols) {
-    sym.size_info_ = size_info.get();
-  }
-
   TreeBuilder builder(size_info.get());
-  std::vector<std::function<bool(const BaseSymbol&)>> filters;
+  FilterList filters;
   builder.Build(std::make_unique<TemplateLens>(), '/', false, filters);
   CheckAllTreeNodesFindable(builder, builder.Open(""));
   EXPECT_EQ(
-      builder.Open("base::internal::Invoker<>::RunOnce")["type"].asString(),
-      "Dt");
-  EXPECT_EQ(builder.Open("base::internal::Invoker<>::RunOnce")["size"].asInt(),
-            50);
+      "Ct",
+      builder.Open("base::internal::Invoker<>::RunOnce")["type"].asString());
+  EXPECT_EQ(50,
+            builder.Open("base::internal::Invoker<>::RunOnce")["size"].asInt());
 }
-}  // namespace
+
+TEST(TreeBuilderTest, TestNoNameUnderGroup) {
+  std::unique_ptr<SizeInfo> size_info = std::make_unique<SizeInfo>();
+  MakeSymbol(size_info.get(), SectionId::kText, 20, "", "A>B>C", "SymbolName");
+
+  TreeBuilder builder(size_info.get());
+  FilterList filters;
+  builder.Build(std::make_unique<ComponentLens>(), '>', false, filters);
+  CheckAllTreeNodesFindable(builder, builder.Open(""));
+  EXPECT_EQ("A>B>C/(No path)",
+            builder.Open("A>B>C")["children"][0]["idPath"].asString());
+}
+
+TEST(TreeBuilderTest, TestJoinDexMethodClasses) {
+  std::unique_ptr<SizeInfo> size_info = std::make_unique<SizeInfo>();
+  MakeSymbol(size_info.get(), SectionId::kDex, 30,
+             "chrome/android/features/start_surface/public/java/src/org/"
+             "chromium/chrome/features/start_surface/StartSurface.java",
+             "Mobile",
+             "org.chromium.chrome.features.start_surface.StartSurface$"
+             "OverviewModeObserver android.graphics.Bitmap a()");
+  MakeSymbol(size_info.get(), SectionId::kDex, 20,
+             "chrome/android/features/start_surface/public/java/src/org/"
+             "chromium/chrome/features/start_surface/StartSurface.java",
+             "Mobile",
+             "org.chromium.chrome.features.start_surface.StartSurface$"
+             "OverviewModeObserver <init>(android.graphics.Bitmap)");
+
+  TreeBuilder builder(size_info.get());
+  FilterList filters;
+  builder.Build(std::make_unique<ComponentLens>(), '>', false, filters);
+  CheckAllTreeNodesFindable(builder, builder.Open(""));
+
+  Json::Value class_symbol = (builder.Open(
+      "Mobile/chrome/android/features/start_surface/public/java/src/"
+      "org/chromium/chrome/features/start_surface/StartSurface.java"))
+      ["children"][0];
+
+  EXPECT_EQ(
+      "org.chromium.chrome.features.start_surface.StartSurface$"
+      "OverviewModeObserver",
+      class_symbol["idPath"].asString());
+  EXPECT_EQ(2u, class_symbol["children"].size());
+
+  Json::Value dex_symbol = class_symbol["children"][0];
+  EXPECT_EQ(0u, dex_symbol["children"].size());
+
+  const std::string id_path = dex_symbol["idPath"].asString();
+  const int short_name_index = dex_symbol["shortNameIndex"].asInt();
+  EXPECT_EQ("android.graphics.Bitmap a()", id_path.substr(short_name_index));
+}
 }  // namespace caspian
diff --git a/tools/binary_size/libsupersize/static/infocard-ui.js b/tools/binary_size/libsupersize/static/infocard-ui.js
index 49fd6f8..a8b8c09 100644
--- a/tools/binary_size/libsupersize/static/infocard-ui.js
+++ b/tools/binary_size/libsupersize/static/infocard-ui.js
@@ -80,13 +80,14 @@
      */
     _updatePaths(node) {
       let pathFragment;
+      // srcPath is set only for leaf nodes.
       if (node.srcPath) {
         pathFragment = dom.createFragment([
             dom.textElement('span', 'Path: ', 'symbol-name-info'),
             document.createTextNode(node.srcPath),
             document.createElement('br'),
             dom.textElement('span', 'Component: ', 'symbol-name-info'),
-            document.createTextNode(node.componet || '(No component)'),
+            document.createTextNode(node.component || '(No component)'),
         ]);
       } else {
         const path = node.idPath.slice(0, node.shortNameIndex);
diff --git a/tools/binary_size/libsupersize/static/viewer.html b/tools/binary_size/libsupersize/static/viewer.html
index e07fc68..cc628fd 100644
--- a/tools/binary_size/libsupersize/static/viewer.html
+++ b/tools/binary_size/libsupersize/static/viewer.html
@@ -126,6 +126,10 @@
         <input type="radio" id="template" name="group_by" value="template">
         <label class="radio-label" for="template">Template</label>
       </div>
+      <div class="radio-wrapper">
+        <input type="radio" id="generated_type" name="group_by" value="generated_type">
+        <label class="radio-label" for="generated_type">Generated type</label>
+      </div>
     </fieldset>
 
     <fieldset>
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 24e6bc29..31afdb9 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1635,6 +1635,7 @@
     'gpu_tests_android_release_bot_minimal_symbols_arm64_fastbuild': [
       'android', 'release_bot', 'minimal_symbols', 'arm64',
       'resource_whitelisting', 'static_angle', 'android_fastbuild', 'webview_google',
+      'android_no_proguard',
     ],
 
     'gpu_tests_android_release_trybot': [
@@ -1653,6 +1654,7 @@
     'gpu_tests_android_release_trybot_arm64_resource_whitelisting_fastbuild': [
       'android', 'release_trybot', 'arm64', 'static_angle',
       'resource_whitelisting', 'android_fastbuild', 'webview_google',
+      'android_no_proguard',
     ],
 
     'gpu_tests_android_release_trybot_arm64_resource_whitelisting_java_coverage': [
@@ -2081,6 +2083,15 @@
                   'disable_android_lint=true'),
     },
 
+    # TODO(https://crbug.com/1020714): This is temporary. We'd like to run a
+    # smoke test on android_binary_sizes to ensure coverage of proguard, at
+    # which point we can merge this into android_fastbuild. Until then, only
+    # disable proguard on a few bots to gather metrics on the effect on build
+    # times.
+    'android_no_proguard': {
+      'gn_args': 'is_java_debug=true',
+    },
+
     'android_without_codecs': {
       'gn_args': 'target_os="android"',
     },
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 11830fa..ea23018a 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -22189,6 +22189,42 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="TabsSuggestions.Close.Accepted">
+  <owner>yusufo@chromium.org</owner>
+  <owner>davidjm@chromium.org</owner>
+  <description>
+    The tab close suggestions were considered and accepted by the user (with
+    some possible editing).
+  </description>
+</action>
+
+<action name="TabsSuggestions.Close.Dismissed">
+  <owner>yusufo@chromium.org</owner>
+  <owner>davidjm@chromium.org</owner>
+  <description>
+    The tab close suggestions were considered and dismissed by the user (with
+    some possible editing).
+  </description>
+</action>
+
+<action name="TabsSuggestions.Close.SuggestionsReview.Accepted">
+  <owner>yusufo@chromium.org</owner>
+  <owner>davidjm@chromium.org</owner>
+  <description>
+    The tab close suggestions were considered by the user by following through
+    the tab suggestions offered in the snackbar.
+  </description>
+</action>
+
+<action name="TabsSuggestions.Close.SuggestionsReview.Dismissed">
+  <owner>yusufo@chromium.org</owner>
+  <owner>davidjm@chromium.org</owner>
+  <description>
+    The tab close suggestions were not considered at all by the user by
+    dismissing the snackbar indicating tab suggestions are available.
+  </description>
+</action>
+
 <action name="TabStrip.UndoCloseTab">
   <owner>yusufo@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6dde5d1..0f942b5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -18815,6 +18815,7 @@
   <int value="645" label="ClickToCallEnabled"/>
   <int value="646"
       label="DeviceLoginScreenShowAccessibilityOptionsInSystemTrayMenu"/>
+  <int value="647" label="PrinterTypeDenyList"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 7144ae9c..7beae27 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -95,6 +95,16 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.CaptionSettingsLoadedFromPrefs"
+    enum="BooleanEnabled" expires_after="M85">
+  <owner>evliu@google.com</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    Whether custom caption settings were loaded from the user preferences.
+    Recorded when WebKit preferences are overridden.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.CrosAlwaysShowA11yMenu" enum="BooleanEnabled"
     expires_after="M82">
   <owner>dmazzoni@chromium.org</owner>
@@ -585,6 +595,16 @@
   </summary>
 </histogram>
 
+<histogram name="Accessibility.VTTContainsStyleBlock" enum="BooleanEnabled"
+    expires_after="M85">
+  <owner>evliu@google.com</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    Whether a VTT file contains an embedded style block. Recorded when a VTT
+    file is parsed.
+  </summary>
+</histogram>
+
 <histogram name="Accessibility.Win.AnimationsEnabled" enum="BooleanEnabled"
     expires_after="2019-11-02">
   <owner>dmazzoni@chromium.org</owner>
@@ -7905,6 +7925,19 @@
   <summary>Gestures supported by the in-app hotseat.</summary>
 </histogram>
 
+<histogram name="Ash.HotseatTransition.AnimationSmoothness" units="%"
+    expires_after="2020-05-21">
+<!-- Name completed by histogram suffixes
+     name="HotseatTransitionType" -->
+
+  <owner>anasalazar@chromium.com</owner>
+  <owner>newcomer@chromium.com</owner>
+  <summary>
+    Tracks the animation smoothness for the transitions of the hoseat to and
+    from the shown state.
+  </summary>
+</histogram>
+
 <histogram name="Ash.ImmersiveFullscreen.WindowType" enum="WindowType"
     expires_after="M77">
   <owner>kuscher@google.com</owner>
@@ -152928,6 +152961,22 @@
   </summary>
 </histogram>
 
+<histogram name="Tabs.Suggestions.Close.NumSuggestionsChanged" units="count"
+    expires_after="2020-11-01">
+  <owner>yusufo@chromium.org</owner>
+  <owner>davidjm@chromium.org</owner>
+  <summary>
+    The user is presented with recommendations on which Tabs they could close.
+    The user has the option to: 1) Remove a Tab from the recommendation 2) Add a
+    Tab which wasn't in the recommendation to the recommendation
+
+    This metric records the sum of the number of times the user does 1) or 2)
+    and is a measure of how useful the recommendation is. This metric is
+    recorded after the user accepts a recommendation - potentially with edits by
+    performing 1) or 2).
+  </summary>
+</histogram>
+
 <histogram name="Tabs.SwitchFromCloseLatency" units="ms">
   <owner>dtrainor@chromium.org</owner>
   <summary>
@@ -168583,6 +168632,16 @@
   </summary>
 </histogram>
 
+<histogram name="Windows.ApplockerRunning" enum="Boolean" expires_after="M83">
+  <owner>forshaw@chromium.org</owner>
+  <owner>wfh@chromium.org</owner>
+  <summary>
+    Records if the APPID driver is running which indicates Applocker is enabled
+    and running. This does not guarantee that Applocker is configured to enforce
+    any rules. Reported once per browser session, on startup.
+  </summary>
+</histogram>
+
 <histogram name="Windows.ComputeNativeWindowOcclusionTime" units="microseconds"
     expires_after="M83">
   <owner>fdoray@chromium.org</owner>
@@ -175302,6 +175361,14 @@
       name="Apps.ScrollableShelf.Drag.PresentationTime.MaxLatency"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="HotseatTransitionType" separator=".">
+  <suffix name="TransitionFromShownHotseat"
+      label="Transition from the shown hotseat"/>
+  <suffix name="TransitionToShownHotseat"
+      label="Transition to the shown hotseat"/>
+  <affected-histogram name="Ash.HotseatTransition.AnimationSmoothness"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="HstsState" separator=".">
   <suffix name="HSTSNotEnabled" label="The HSTS is not enabled."/>
   <suffix name="WithHSTSEnabled" label="The HSTS is enabled."/>
diff --git a/tools/perf/benchmarks/system_health_smoke_test.py b/tools/perf/benchmarks/system_health_smoke_test.py
index 0778b56b..73c92d8 100644
--- a/tools/perf/benchmarks/system_health_smoke_test.py
+++ b/tools/perf/benchmarks/system_health_smoke_test.py
@@ -292,6 +292,13 @@
   return benchmark_class().CreateStorySet(options=None)
 
 
+def _should_skip_story(benchmark_class, story):
+  # Per crbug.com/1019383 we don't have many device cycles to work with on
+  # Android, so let's just run the most important stories.
+  return (benchmark_class.Name() == 'system_health.memory_mobile' and
+      'health_check' not in story.tags)
+
+
 def validate_smoke_test_name_versions():
   benchmark_classes = GetSystemHealthBenchmarksToSmokeTest()
   assert benchmark_classes, 'This list should never be empty'
@@ -300,11 +307,7 @@
     stories_set = _create_story_set(benchmark_class)
 
     for story_to_smoke_test in stories_set.stories:
-      # TODO: combine the tag check logic in _create_story_set
-      # Per crbug.com/1019383 we don't have many device cycles to work with on
-      # Android, so let's just run the most important stories.
-      if (benchmark_class.Name() == 'system_health.memory_mobile' and
-          'health_check' not in story_to_smoke_test.tags):
+      if _should_skip_story(benchmark_class, story_to_smoke_test):
         continue
       names_stories_to_smoke_tests.append(
           benchmark_class.Name() + '/' + story_to_smoke_test.name)
@@ -339,21 +342,19 @@
   for benchmark_class in benchmark_classes:
     stories_set = _create_story_set(benchmark_class)
 
+    remote_story_names = []
+    for story_to_smoke_test in stories_set:
+      if _should_skip_story(benchmark_class, story_to_smoke_test):
+        continue
+      if not story_to_smoke_test.is_local:
+        remote_story_names.append(story_to_smoke_test)
+      suite.addTest(
+          _GenerateSmokeTestCase(benchmark_class, story_to_smoke_test))
     # Prefetch WPR archive needed by the stories set to avoid race condition
     # when fetching them when tests are run in parallel.
     # See crbug.com/700426 for more details.
-    story_names = [s.name for s in stories_set if not s.is_local]
     stories_set.wpr_archive_info.DownloadArchivesIfNeeded(
-        story_names=story_names)
-
-    for story_to_smoke_test in stories_set:
-      # Per crbug.com/1019383 we don't have many device cycles to work with on
-      # Android, so let's just run the most important stories.
-      if (benchmark_class.Name() == 'system_health.memory_mobile' and
-          'health_check' not in story_to_smoke_test.tags):
-        continue
-      suite.addTest(
-          _GenerateSmokeTestCase(benchmark_class, story_to_smoke_test))
+        story_names=remote_story_names)
 
   return suite
 
diff --git a/ui/base/ui_features.gni b/ui/base/ui_features.gni
index 213fdef..c3c4127 100644
--- a/ui/base/ui_features.gni
+++ b/ui/base/ui_features.gni
@@ -6,7 +6,7 @@
 
 declare_args() {
   # Optional system library.
-  use_xkbcommon = false
+  use_xkbcommon = use_ozone && is_linux && !is_chromecast
 
   # Whether the platform provides a native accessibility toolkit.
   has_native_accessibility = use_atk || is_win || is_mac
diff --git a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
index adf6543..f36ec10 100644
--- a/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
+++ b/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc
@@ -203,7 +203,7 @@
   }
   // Asks Wayland to create a wl_buffer based on the |file| fd.
   buffer_manager_->CreateDmabufBasedBuffer(
-      widget_, std::move(fd), GetBufferSize(), strides, offsets, modifiers,
+      std::move(fd), GetBufferSize(), strides, offsets, modifiers,
       gbm_bo_->GetFormat(), plane_count, GetUniqueId());
 }
 
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
index 3cdc97d..51e82d6 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
@@ -81,7 +81,6 @@
 }
 
 void WaylandBufferManagerGpu::CreateDmabufBasedBuffer(
-    gfx::AcceleratedWidget widget,
     base::ScopedFD dmabuf_fd,
     gfx::Size size,
     const std::vector<uint32_t>& strides,
@@ -96,14 +95,13 @@
   io_thread_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&WaylandBufferManagerGpu::CreateDmabufBasedBufferInternal,
-                     base::Unretained(this), widget, std::move(dmabuf_fd),
+                     base::Unretained(this), std::move(dmabuf_fd),
                      std::move(size), std::move(strides), std::move(offsets),
                      std::move(modifiers), current_format, planes_count,
                      buffer_id));
 }
 
 void WaylandBufferManagerGpu::CreateShmBasedBuffer(
-    gfx::AcceleratedWidget widget,
     base::ScopedFD shm_fd,
     size_t length,
     gfx::Size size,
@@ -114,7 +112,7 @@
   io_thread_runner_->PostTask(
       FROM_HERE,
       base::BindOnce(&WaylandBufferManagerGpu::CreateShmBasedBufferInternal,
-                     base::Unretained(this), widget, std::move(shm_fd), length,
+                     base::Unretained(this), std::move(shm_fd), length,
                      std::move(size), buffer_id));
 }
 
@@ -160,7 +158,6 @@
 }
 
 void WaylandBufferManagerGpu::CreateDmabufBasedBufferInternal(
-    gfx::AcceleratedWidget widget,
     base::ScopedFD dmabuf_fd,
     gfx::Size size,
     const std::vector<uint32_t>& strides,
@@ -172,14 +169,12 @@
   DCHECK(io_thread_runner_->BelongsToCurrentThread());
   DCHECK(remote_host_);
   remote_host_->CreateDmabufBasedBuffer(
-      widget,
       mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(dmabuf_fd))),
       size, strides, offsets, modifiers, current_format, planes_count,
       buffer_id);
 }
 
 void WaylandBufferManagerGpu::CreateShmBasedBufferInternal(
-    gfx::AcceleratedWidget widget,
     base::ScopedFD shm_fd,
     size_t length,
     gfx::Size size,
@@ -187,8 +182,8 @@
   DCHECK(io_thread_runner_->BelongsToCurrentThread());
   DCHECK(remote_host_);
   remote_host_->CreateShmBasedBuffer(
-      widget, mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(shm_fd))),
-      length, size, buffer_id);
+      mojo::WrapPlatformHandle(mojo::PlatformHandle(std::move(shm_fd))), length,
+      size, buffer_id);
 }
 
 void WaylandBufferManagerGpu::CommitBufferInternal(
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
index 892597e..e998717 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
+++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.h
@@ -75,8 +75,7 @@
   // ui/ozone/public/mojom/wayland/wayland_connection.mojom.
   //
   // Asks Wayland to create generic dmabuf-based wl_buffer.
-  void CreateDmabufBasedBuffer(gfx::AcceleratedWidget widget,
-                               base::ScopedFD dmabuf_fd,
+  void CreateDmabufBasedBuffer(base::ScopedFD dmabuf_fd,
                                gfx::Size size,
                                const std::vector<uint32_t>& strides,
                                const std::vector<uint32_t>& offsets,
@@ -86,8 +85,7 @@
                                uint32_t buffer_id);
 
   // Asks Wayland to create a shared memory based wl_buffer.
-  void CreateShmBasedBuffer(gfx::AcceleratedWidget widget,
-                            base::ScopedFD shm_fd,
+  void CreateShmBasedBuffer(base::ScopedFD shm_fd,
                             size_t length,
                             gfx::Size size,
                             uint32_t buffer_id);
@@ -126,8 +124,7 @@
       gfx::BufferFormat buffer_format) const;
 
  private:
-  void CreateDmabufBasedBufferInternal(gfx::AcceleratedWidget widget,
-                                       base::ScopedFD dmabuf_fd,
+  void CreateDmabufBasedBufferInternal(base::ScopedFD dmabuf_fd,
                                        gfx::Size size,
                                        const std::vector<uint32_t>& strides,
                                        const std::vector<uint32_t>& offsets,
@@ -135,8 +132,7 @@
                                        uint32_t current_format,
                                        uint32_t planes_count,
                                        uint32_t buffer_id);
-  void CreateShmBasedBufferInternal(gfx::AcceleratedWidget widget,
-                                    base::ScopedFD shm_fd,
+  void CreateShmBasedBufferInternal(base::ScopedFD shm_fd,
                                     size_t length,
                                     gfx::Size size,
                                     uint32_t buffer_id);
diff --git a/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc b/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
index a9d59f4..fe1040e 100644
--- a/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
+++ b/ui/ozone/platform/wayland/gpu/wayland_canvas_surface.cc
@@ -78,9 +78,8 @@
         base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
             std::move(shm_region));
     base::subtle::ScopedFDPair fd_pair = platform_shm.PassPlatformHandle();
-    buffer_manager_->CreateShmBasedBuffer(widget_, std::move(fd_pair.fd),
-                                          checked_length.ValueOrDie(), size,
-                                          buffer_id_);
+    buffer_manager_->CreateShmBasedBuffer(
+        std::move(fd_pair.fd), checked_length.ValueOrDie(), size, buffer_id_);
 
     sk_surface_ = SkSurface::MakeRasterDirect(
         SkImageInfo::MakeN32Premul(size.width(), size.height()),
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
index 8bc4711..7434e70 100644
--- a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
+++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
@@ -9,6 +9,7 @@
 
 #include "base/i18n/number_formatting.h"
 #include "base/message_loop/message_loop_current.h"
+#include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/trace_event/trace_event.h"
 #include "mojo/public/cpp/system/platform_handle.h"
@@ -66,6 +67,10 @@
   bool CommitBuffer(uint32_t buffer_id, const gfx::Rect& damage_region) {
     DCHECK(!pending_buffer_);
 
+    // The window has already been destroyed.
+    if (!window_)
+      return true;
+
     WaylandBuffer* buffer = GetBuffer(buffer_id);
     if (!buffer) {
       // Get the anonymous_wl_buffer aka the buffer that has not been attached
@@ -134,7 +139,9 @@
     // But if the buffer 2 is destroyed, the buffer release callback never
     // comes for that buffer. Thus, if there is a submitted buffer, notify
     // the client about successful swap.
-    if (buffer && !buffer->released && submitted_buffer_)
+    // If the window has already been destroyed, no need to complete the
+    // submission.
+    if (buffer && !buffer->released && submitted_buffer_ && window_)
       CompleteSubmission();
 
     if (prev_submitted_buffer_ == buffer)
@@ -174,11 +181,15 @@
 
     prev_submitted_buffer_ = nullptr;
     submitted_buffer_ = nullptr;
+    pending_buffer_ = nullptr;
 
     connection_->ScheduleFlush();
   }
 
   void ResetSurfaceContents() {
+    if (!window_)
+      return;
+
     wl_surface_attach(window_->surface(), nullptr, 0, 0);
     wl_surface_commit(window_->surface());
 
@@ -195,12 +206,17 @@
     return !!buffer;
   }
 
+  bool HasBuffers() const { return !buffers_.empty(); }
+
+  void OnWindowRemoved() { window_ = nullptr; }
+  bool HasWindow() const { return !!window_; }
+
  private:
   using PresentationFeedbackQueue = base::queue<
       std::pair<uint32_t, wl::Object<struct wp_presentation_feedback>>>;
 
   bool CommitBufferInternal(WaylandBuffer* buffer) {
-    DCHECK(buffer);
+    DCHECK(buffer && window_);
     DCHECK(!pending_buffer_);
 
     // Once the BufferRelease is called, the buffer will be released.
@@ -237,6 +253,8 @@
   }
 
   void AttachAndDamageBuffer(WaylandBuffer* buffer) {
+    DCHECK(window_);
+
     gfx::Rect pending_damage_region = std::move(buffer->damage_region);
     // If the size of the damage region is empty, wl_surface_damage must be
     // supplied with the actual size of the buffer, which is going to be
@@ -252,9 +270,13 @@
     wl_surface_attach(surface, buffer->wl_buffer.get(), 0, 0);
   }
 
-  void CommitSurface() { wl_surface_commit(window_->surface()); }
+  void CommitSurface() {
+    DCHECK(window_);
+    wl_surface_commit(window_->surface());
+  }
 
   void SetupFrameCallback() {
+    DCHECK(window_);
     static const wl_callback_listener frame_listener = {
         &Surface::FrameCallbackDone};
 
@@ -264,6 +286,7 @@
   }
 
   void SetupPresentationFeedback(uint32_t buffer_id) {
+    DCHECK(window_);
     // Set up presentation feedback.
     if (!connection_->presentation())
       return;
@@ -366,6 +389,10 @@
 
     prev_submitted_buffer_ = submitted_buffer_;
     submitted_buffer_ = nullptr;
+
+    if (!window_)
+      return;
+
     // We can now complete the latest submission. We had to wait for this
     // release because SwapCompletionCallback indicates to the client that the
     // previous buffer is available for reuse.
@@ -396,7 +423,9 @@
       return;
     }
 
-    buffer_manager_->OnPresentation(window_->GetWidget(), buffer_id, feedback);
+    if (window_)
+      buffer_manager_->OnPresentation(window_->GetWidget(), buffer_id,
+                                      feedback);
   }
 
   // wp_presentation_feedback_listener
@@ -441,7 +470,7 @@
   }
 
   void ProcessPendingBuffer() {
-    if (!pending_buffer_)
+    if (!pending_buffer_ || !window_)
       return;
 
     auto* buffer = pending_buffer_;
@@ -453,7 +482,7 @@
   // WaylandWindow.
 
   // Non-owned. The window this helper surface stores and submits buffers for.
-  const WaylandWindow* const window_;
+  const WaylandWindow* window_;
 
   // Non-owned pointer to the connection.
   WaylandConnection* const connection_;
@@ -515,8 +544,12 @@
 
 void WaylandBufferManagerHost::OnWindowRemoved(WaylandWindow* window) {
   DCHECK(window);
-  auto ret = surfaces_.erase(window->GetWidget());
-  DCHECK(ret);
+  auto it = surfaces_.find(window->GetWidget());
+  DCHECK(it != surfaces_.end());
+  if (it->second->HasBuffers())
+    it->second->OnWindowRemoved();
+  else
+    surfaces_.erase(it);
 }
 
 void WaylandBufferManagerHost::SetTerminateGpuCallback(
@@ -565,7 +598,6 @@
 }
 
 void WaylandBufferManagerHost::CreateDmabufBasedBuffer(
-    gfx::AcceleratedWidget widget,
     mojo::ScopedHandle dmabuf_fd,
     const gfx::Size& size,
     const std::vector<uint32_t>& strides,
@@ -584,9 +616,9 @@
 
   // Validate data and ask surface to create a buffer associated with the
   // |buffer_id|.
-  if (!ValidateDataFromGpu(widget, fd, size, strides, offsets, modifiers,
-                           format, planes_count, buffer_id) ||
-      !CreateBuffer(widget, size, buffer_id)) {
+  if (!ValidateDataFromGpu(fd, size, strides, offsets, modifiers, format,
+                           planes_count, buffer_id) ||
+      !CreateBuffer(size, buffer_id)) {
     TerminateGpuProcess();
     return;
   }
@@ -594,7 +626,7 @@
   // Create wl_buffer associated with the internal Buffer.
   auto callback =
       base::BindOnce(&WaylandBufferManagerHost::OnCreateBufferComplete,
-                     weak_factory_.GetWeakPtr(), widget, buffer_id);
+                     weak_factory_.GetWeakPtr(), buffer_id);
   if (connection_->zwp_dmabuf()) {
     connection_->zwp_dmabuf()->CreateBuffer(std::move(fd), size, strides,
                                             offsets, modifiers, format,
@@ -610,12 +642,10 @@
   }
 }
 
-void WaylandBufferManagerHost::CreateShmBasedBuffer(
-    gfx::AcceleratedWidget widget,
-    mojo::ScopedHandle shm_fd,
-    uint64_t length,
-    const gfx::Size& size,
-    uint32_t buffer_id) {
+void WaylandBufferManagerHost::CreateShmBasedBuffer(mojo::ScopedHandle shm_fd,
+                                                    uint64_t length,
+                                                    const gfx::Size& size,
+                                                    uint32_t buffer_id) {
   DCHECK(base::MessageLoopCurrentForUI::IsSet());
   DCHECK(error_message_.empty());
 
@@ -624,15 +654,15 @@
 
   base::ScopedFD fd = mojo::UnwrapPlatformHandle(std::move(shm_fd)).TakeFD();
   // Validate data and create a buffer associated with the |buffer_id|.
-  if (!ValidateDataFromGpu(widget, fd, length, size, buffer_id) ||
-      !CreateBuffer(widget, size, buffer_id)) {
+  if (!ValidateDataFromGpu(fd, length, size, buffer_id) ||
+      !CreateBuffer(size, buffer_id)) {
     TerminateGpuProcess();
     return;
   }
 
   // Create a shm based wl_buffer and attach it to the created buffer.
   auto buffer = connection_->shm()->CreateBuffer(std::move(fd), length, size);
-  OnCreateBufferComplete(widget, buffer_id, std::move(buffer));
+  OnCreateBufferComplete(buffer_id, std::move(buffer));
 
   connection_->ScheduleFlush();
 }
@@ -647,14 +677,17 @@
 
   DCHECK(error_message_.empty());
 
-  if (ValidateDataFromGpu(widget, buffer_id)) {
+  if (widget == gfx::kNullAcceleratedWidget) {
+    error_message_ = "Invalid widget.";
+  } else if (ValidateBufferIdFromGpu(buffer_id)) {
     Surface* surface = GetSurface(widget);
-    if (!surface) {
+    if (!surface)
+      return;
+
+    if (!surface->CommitBuffer(buffer_id, damage_region)) {
       error_message_ =
-          "Surface does not exist or the accelerated widget is invalid.";
-    } else if (!surface->CommitBuffer(buffer_id, damage_region)) {
-      error_message_ = "Buffer with " + NumberToString(buffer_id) +
-                       " id does not exist or failed to be created.";
+          base::StrCat({"Buffer with ", NumberToString(buffer_id),
+                        " id does not exist or failed to be created."});
     }
   }
 
@@ -670,25 +703,39 @@
                "Buffer id", buffer_id);
 
   DCHECK(error_message_.empty());
-
-  // It can be a buffer without a widget assigned.
-  if (widget == gfx::kNullAcceleratedWidget) {
-    auto it = anonymous_buffers_.find(buffer_id);
-    if (it != anonymous_buffers_.end()) {
-      anonymous_buffers_.erase(it);
-      return;
-    }
-  } else {
-    Surface* surface = GetSurface(widget);
-    // On browser shutdown, the surface might have already been destroyed.
-    if (!surface || surface->DestroyBuffer(buffer_id) == 1u)
-      return;
+  if (!ValidateBufferIdFromGpu(buffer_id)) {
+    TerminateGpuProcess();
+    return;
   }
 
-  error_message_ =
-      "Buffer with " + NumberToString(buffer_id) + " id does not exist";
+  // We allow creating buffers and attaching them to surfaces later. Thus, we
+  // must pay attention to the following things during the destruction of the
+  // buffers.
+  // 1) the |widget| is basically a hint where we must search for buffers. If no
+  // such surface exists (has already been destroyed), check if the buffer still
+  // has been stored in the |anonymous_buffers_|.
+  // 2) if the |widget| is null, always search a buffer with the |buffer_id| in
+  // the |anonymous_buffers_|.
+
+  uint32_t destroyed_count = 0u;
+
+  Surface* surface = GetSurface(widget);
+  if (surface) {
+    destroyed_count = surface->DestroyBuffer(buffer_id);
+    if (!surface->HasBuffers() && !surface->HasWindow())
+      surfaces_.erase(widget);
+  }
+
+  // Ensure that we can't destroy more than 1 buffer. This can be 0 as well
+  // if no buffers are destroyed.
+  DCHECK_LE(destroyed_count, 1u);
+
+  if (destroyed_count == 1u || DestroyAnonymousBuffer(buffer_id))
+    return;
+
+  error_message_ = base::StrCat(
+      {"Buffer with ", NumberToString(buffer_id), " id does not exist"});
   TerminateGpuProcess();
-  return;
 }
 
 void WaylandBufferManagerHost::ResetSurfaceContents(
@@ -708,37 +755,24 @@
   return buffer;
 }
 
-bool WaylandBufferManagerHost::CreateBuffer(gfx::AcceleratedWidget& widget,
-                                            const gfx::Size& size,
+bool WaylandBufferManagerHost::CreateBuffer(const gfx::Size& size,
                                             uint32_t buffer_id) {
-  WaylandBufferManagerHost::Surface* surface = GetSurface(widget);
-  // The buffer was created anonymously, proceed with storing it into a temp
-  // storage.
-  if (!surface) {
-    DCHECK(widget == gfx::kNullAcceleratedWidget);
-
-    bool buffer_exists = false;
-    // If the buffer was created anonymously, first check if any of the surfaces
-    // has already had a buffer with the same id.
-    for (auto const& surface : surfaces_) {
-      buffer_exists = surface.second->BufferExists(buffer_id);
-      if (buffer_exists)
-        break;
+  // First check if any of the surfaces has already had a buffer with the same
+  // id.
+  for (auto const& surface : surfaces_) {
+    if (surface.second->BufferExists(buffer_id)) {
+      error_message_ = base::StrCat(
+          {"A buffer with id= ", NumberToString(buffer_id), " already exists"});
+      return false;
     }
-
-    if (!buffer_exists) {
-      auto result = anonymous_buffers_.emplace(
-          buffer_id, std::make_unique<WaylandBuffer>(size, buffer_id));
-      if (result.second)
-        return true;
-    }
-  } else if (surface->CreateBuffer(size, buffer_id)) {
-    return true;
   }
 
-  error_message_ =
-      "A buffer with id= " + NumberToString(buffer_id) + " already exists";
-  return false;
+  auto result = anonymous_buffers_.emplace(
+      buffer_id, std::make_unique<WaylandBuffer>(size, buffer_id));
+  if (!result.second)
+    error_message_ = base::StrCat(
+        {"A buffer with id= ", NumberToString(buffer_id), " already exists"});
+  return result.second;
 }
 
 WaylandBufferManagerHost::Surface* WaylandBufferManagerHost::GetSurface(
@@ -748,7 +782,6 @@
 }
 
 bool WaylandBufferManagerHost::ValidateDataFromGpu(
-    const gfx::AcceleratedWidget& widget,
     const base::ScopedFD& fd,
     const gfx::Size& size,
     const std::vector<uint32_t>& strides,
@@ -757,7 +790,7 @@
     uint32_t format,
     uint32_t planes_count,
     uint32_t buffer_id) {
-  if (!ValidateDataFromGpu(widget, buffer_id))
+  if (!ValidateBufferIdFromGpu(buffer_id))
     return false;
 
   std::string reason;
@@ -772,11 +805,11 @@
 
   if (planes_count != strides.size() || planes_count != offsets.size() ||
       planes_count != modifiers.size()) {
-    reason = "Number of strides(" + NumberToString(strides.size()) +
-             ")/offsets(" + NumberToString(offsets.size()) + ")/modifiers(" +
-             NumberToString(modifiers.size()) +
-             ") does not correspond to the number of planes(" +
-             NumberToString(planes_count) + ")";
+    reason = base::StrCat({"Number of strides(", NumberToString(strides.size()),
+                           ")/offsets(", NumberToString(offsets.size()),
+                           ")/modifiers(", NumberToString(modifiers.size()),
+                           ") does not correspond to the number of planes(",
+                           NumberToString(planes_count), ")"});
   }
 
   for (auto stride : strides) {
@@ -794,12 +827,10 @@
   return true;
 }
 
-bool WaylandBufferManagerHost::ValidateDataFromGpu(
-    const gfx::AcceleratedWidget& widget,
-    uint32_t buffer_id) {
+bool WaylandBufferManagerHost::ValidateBufferIdFromGpu(uint32_t buffer_id) {
   std::string reason;
   if (buffer_id < 1)
-    reason = "Invalid buffer id: " + NumberToString(buffer_id);
+    reason = base::StrCat({"Invalid buffer id: ", NumberToString(buffer_id)});
 
   if (!reason.empty()) {
     error_message_ = std::move(reason);
@@ -810,12 +841,11 @@
 }
 
 bool WaylandBufferManagerHost::ValidateDataFromGpu(
-    const gfx::AcceleratedWidget& widget,
     const base::ScopedFD& fd,
     size_t length,
     const gfx::Size& size,
     uint32_t buffer_id) {
-  if (!ValidateDataFromGpu(widget, buffer_id))
+  if (!ValidateBufferIdFromGpu(buffer_id))
     return false;
 
   std::string reason;
@@ -837,13 +867,8 @@
 }
 
 void WaylandBufferManagerHost::OnCreateBufferComplete(
-    gfx::AcceleratedWidget widget,
     uint32_t buffer_id,
     wl::Object<struct wl_buffer> new_buffer) {
-  // It can be an anonymously created buffer that will be attached to a surface
-  // later.
-  Surface* surface_ptr = nullptr;
-  if (widget == gfx::kNullAcceleratedWidget) {
     auto it = anonymous_buffers_.find(buffer_id);
     // It might have already been destroyed or stored by any of the surfaces.
     if (it != anonymous_buffers_.end()) {
@@ -851,19 +876,14 @@
     } else {
       for (auto& surface : surfaces_) {
         if (surface.second->BufferExists(buffer_id)) {
-          surface_ptr = surface.second.get();
+          surface.second.get()->AttachWlBuffer(buffer_id,
+                                               std::move(new_buffer));
           break;
         }
       }
     }
-  } else {
-    surface_ptr = GetSurface(widget);
-  }
-
-  // A surface can have been destroyed if it does not have buffers left.
-  if (!surface_ptr)
-    return;
-  surface_ptr->AttachWlBuffer(buffer_id, std::move(new_buffer));
+    // There is no need for the buffer anymore. Let it go out of the scope and
+    // be destroyed.
 }
 
 void WaylandBufferManagerHost::OnSubmission(
@@ -892,4 +912,13 @@
   // The GPU process' failure results in calling ::OnChannelDestroyed.
 }
 
+bool WaylandBufferManagerHost::DestroyAnonymousBuffer(uint32_t buffer_id) {
+  auto it = anonymous_buffers_.find(buffer_id);
+  if (it == anonymous_buffers_.end())
+    return false;
+
+  anonymous_buffers_.erase(it);
+  return true;
+}
+
 }  // namespace ui
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
index 58f848e..1f455154 100644
--- a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
+++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.h
@@ -117,8 +117,7 @@
   // Called by the GPU and asks to import a wl_buffer based on a gbm file
   // descriptor using zwp_linux_dmabuf protocol. Check comments in the
   // ui/ozone/public/mojom/wayland/wayland_connection.mojom.
-  void CreateDmabufBasedBuffer(gfx::AcceleratedWidget widget,
-                               mojo::ScopedHandle dmabuf_fd,
+  void CreateDmabufBasedBuffer(mojo::ScopedHandle dmabuf_fd,
                                const gfx::Size& size,
                                const std::vector<uint32_t>& strides,
                                const std::vector<uint32_t>& offsets,
@@ -129,8 +128,7 @@
   // Called by the GPU and asks to import a wl_buffer based on a shared memory
   // file descriptor using wl_shm protocol. Check comments in the
   // ui/ozone/public/mojom/wayland/wayland_connection.mojom.
-  void CreateShmBasedBuffer(gfx::AcceleratedWidget widget,
-                            mojo::ScopedHandle shm_fd,
+  void CreateShmBasedBuffer(mojo::ScopedHandle shm_fd,
                             uint64_t length,
                             const gfx::Size& size,
                             uint32_t buffer_id) override;
@@ -160,16 +158,13 @@
   // presentation callbacks for that window's surface.
   class Surface;
 
-  bool CreateBuffer(gfx::AcceleratedWidget& widget,
-                    const gfx::Size& size,
-                    uint32_t buffer_id);
+  bool CreateBuffer(const gfx::Size& size, uint32_t buffer_id);
 
   Surface* GetSurface(gfx::AcceleratedWidget widget) const;
 
   // Validates data sent from GPU. If invalid, returns false and sets an error
   // message to |error_message_|.
-  bool ValidateDataFromGpu(const gfx::AcceleratedWidget& widget,
-                           const base::ScopedFD& file,
+  bool ValidateDataFromGpu(const base::ScopedFD& file,
                            const gfx::Size& size,
                            const std::vector<uint32_t>& strides,
                            const std::vector<uint32_t>& offsets,
@@ -177,18 +172,15 @@
                            uint32_t format,
                            uint32_t planes_count,
                            uint32_t buffer_id);
-  bool ValidateDataFromGpu(const gfx::AcceleratedWidget& widget,
-                           uint32_t buffer_id);
-  bool ValidateDataFromGpu(const gfx::AcceleratedWidget& widget,
-                           const base::ScopedFD& file,
+  bool ValidateBufferIdFromGpu(uint32_t buffer_id);
+  bool ValidateDataFromGpu(const base::ScopedFD& file,
                            size_t length,
                            const gfx::Size& size,
                            uint32_t buffer_id);
 
   // Callback method. Receives a result for the request to create a wl_buffer
   // backend by dmabuf file descriptor from ::CreateBuffer call.
-  void OnCreateBufferComplete(gfx::AcceleratedWidget widget,
-                              uint32_t buffer_id,
+  void OnCreateBufferComplete(uint32_t buffer_id,
                               wl::Object<struct wl_buffer> new_buffer);
 
   // Tells the |buffer_manager_gpu_ptr_| the result of a swap call and provides
@@ -203,6 +195,8 @@
   // Terminates the GPU process on invalid data received
   void TerminateGpuProcess();
 
+  bool DestroyAnonymousBuffer(uint32_t buffer_id);
+
   base::flat_map<gfx::AcceleratedWidget, std::unique_ptr<Surface>> surfaces_;
 
   // Set when invalid data is received from the GPU process.
diff --git a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
index fdcd3ec..778add08 100644
--- a/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_buffer_manager_unittest.cc
@@ -127,7 +127,6 @@
 
   void CreateDmabufBasedBufferAndSetTerminateExpecation(
       bool fail,
-      gfx::AcceleratedWidget widget,
       uint32_t buffer_id,
       base::ScopedFD fd = base::ScopedFD(),
       const gfx::Size& size = kDefaultSize,
@@ -141,15 +140,14 @@
 
     SetTerminateCallbackExpectationAndDestroyChannel(&callback_, fail);
     buffer_manager_gpu_->CreateDmabufBasedBuffer(
-        widget, std::move(fd), kDefaultSize, strides, offsets, modifiers,
-        format, planes_count, buffer_id);
+        std::move(fd), kDefaultSize, strides, offsets, modifiers, format,
+        planes_count, buffer_id);
 
     Sync();
   }
 
   void CreateShmBasedBufferAndSetTerminateExpecation(
       bool fail,
-      gfx::AcceleratedWidget widget,
       uint32_t buffer_id,
       const gfx::Size& size = kDefaultSize,
       size_t length = 0) {
@@ -157,7 +155,7 @@
 
     if (!length)
       length = size.width() * size.height() * 4;
-    buffer_manager_gpu_->CreateShmBasedBuffer(widget, MakeFD(), length, size,
+    buffer_manager_gpu_->CreateShmBasedBuffer(MakeFD(), length, size,
                                               buffer_id);
 
     Sync();
@@ -189,6 +187,21 @@
     }
   }
 
+  std::unique_ptr<WaylandWindow> CreateWindow() {
+    testing::Mock::VerifyAndClearExpectations(&delegate_);
+    auto new_window =
+        std::make_unique<WaylandWindow>(&delegate_, connection_.get());
+    PlatformWindowInitProperties properties;
+    properties.bounds = gfx::Rect(0, 0, 800, 600);
+    properties.type = PlatformWindowType::kWindow;
+    EXPECT_TRUE(new_window->Initialize(std::move(properties)));
+
+    Sync();
+
+    EXPECT_NE(new_window->GetWidget(), gfx::kNullAcceleratedWidget);
+    return new_window;
+  }
+
   MockTerminateGpuCallback callback_;
   WaylandBufferManagerHost* manager_host_;
 
@@ -200,12 +213,11 @@
   constexpr uint32_t kDmabufBufferId = 1;
 
   EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
-  const gfx::AcceleratedWidget widget = window_->GetWidget();
 
-  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
                                                    kDmabufBufferId);
-  DestroyBufferAndSetTerminateExpectation(widget, kDmabufBufferId,
-                                          false /*fail*/);
+  DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
+                                          kDmabufBufferId, false /*fail*/);
 }
 
 TEST_P(WaylandBufferManagerTest, VerifyModifiers) {
@@ -242,11 +254,10 @@
   }
 
   EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
-  const gfx::AcceleratedWidget widget = window_->GetWidget();
 
   CreateDmabufBasedBufferAndSetTerminateExpecation(
-      false /*fail*/, widget, kDmabufBufferId, base::ScopedFD(), kDefaultSize,
-      {1}, {2}, {kFormatModiferLinear}, kFourccFormatR8, 1);
+      false /*fail*/, kDmabufBufferId, base::ScopedFD(), kDefaultSize, {1}, {2},
+      {kFormatModiferLinear}, kFourccFormatR8, 1);
 
   Sync();
 
@@ -254,17 +265,19 @@
   EXPECT_EQ(params_vector.size(), 1u);
   EXPECT_EQ(params_vector[0]->modifier_hi_, kFormatModiferLinear >> 32);
   EXPECT_EQ(params_vector[0]->modifier_lo_, kFormatModiferLinear & UINT32_MAX);
+
+  // Clean up.
+  DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
+                                          kDmabufBufferId, false /*fail*/);
 }
 
 TEST_P(WaylandBufferManagerTest, CreateShmBasedBuffers) {
   constexpr uint32_t kShmBufferId = 1;
 
-  const gfx::AcceleratedWidget widget = window_->GetWidget();
+  CreateShmBasedBufferAndSetTerminateExpecation(false /*fail*/, kShmBufferId);
 
-  CreateShmBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
-                                                kShmBufferId);
-  // The state is reset now and there are no buffers to destroy.
-  DestroyBufferAndSetTerminateExpectation(widget, kShmBufferId, false /*fail*/);
+  DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
+                                          kShmBufferId, false /*fail*/);
 }
 
 TEST_P(WaylandBufferManagerTest, ValidateDataFromGpu) {
@@ -288,39 +301,14 @@
       {true, kDefaultSize, 1, {1}, {2}, {6}, DRM_FORMAT_R8},
   };
 
-  const gfx::AcceleratedWidget widget = window_->GetWidget();
   for (const auto& bad : kBadInputs) {
     EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(0);
     base::ScopedFD dummy;
     CreateDmabufBasedBufferAndSetTerminateExpecation(
-        true /*fail*/, widget, bad.buffer_id,
+        true /*fail*/, bad.buffer_id,
         bad.has_file ? MakeFD() : std::move(dummy), bad.size, bad.strides,
         bad.offsets, bad.modifiers, bad.format, bad.planes_count);
   }
-
-  constexpr uint32_t kBufferId = 1;
-
-  // Create a buffer so it gets registered with the given ID.
-  // This must be the only buffer that is asked to be created.
-  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
-  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
-                                                   kBufferId);
-
-  // It must be impossible to create a buffer with the same id.
-  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(0);
-  CreateDmabufBasedBufferAndSetTerminateExpecation(true /*fail*/, widget,
-                                                   kBufferId);
-
-  // Create the buffer again and try to destroy it.
-  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
-  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
-                                                   kBufferId);
-
-  // The destruction of the previously created buffer must be ok.
-  DestroyBufferAndSetTerminateExpectation(widget, kBufferId, false /*fail*/);
-
-  // Destroying non-existing buffer triggers the termination callback.
-  DestroyBufferAndSetTerminateExpectation(widget, kBufferId, true /*fail*/);
 }
 
 TEST_P(WaylandBufferManagerTest, CreateAndDestroyBuffer) {
@@ -330,32 +318,117 @@
   const gfx::AcceleratedWidget widget = window_->GetWidget();
 
   // This section tests that it is impossible to create buffers with the same
-  // id.
+  // id if they haven't been assigned to any surfaces yet.
   {
     EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
-    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
+    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
                                                      kBufferId1);
-    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
+    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
                                                      kBufferId2);
 
     // Can't create buffer with existing id.
-    CreateDmabufBasedBufferAndSetTerminateExpecation(true /*fail*/, widget,
-                                                     kBufferId2);
-    // Can't destroy buffer with non-existing id (the manager cleared the state
-    // after the previous failure).
-    DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, true /*fail*/);
+    CreateDmabufBasedBufferAndSetTerminateExpecation(true /*fail*/, kBufferId2);
   }
 
-  // This section tests that it is impossible to destroy buffers with
-  // non-existing ids (for example, if the have already been destroyed).
+  // ... impossible to create buffers with the same id if one of them
+  // has already been attached to a surface.
   {
     EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
-    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
-                                                     kBufferId2);
-    DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
-    // Can't destroy the same buffer twice (non-existing id).
-    DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, true /*fail*/);
+    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
+                                                     kBufferId1);
+
+    buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds());
+
+    CreateDmabufBasedBufferAndSetTerminateExpecation(true /*fail*/, kBufferId1);
   }
+
+  // ... impossible to destroy non-existing buffer.
+  {
+    // Either it is attached...
+    DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, true /*fail*/);
+
+    // Or not attached.
+    DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
+                                            kBufferId1, true /*fail*/);
+  }
+
+  // ... impossible to try to destroy an attached buffer if the widget is not
+  // specified.
+  {
+    EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
+                                                     kBufferId1);
+
+    buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds());
+
+    DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
+                                            kBufferId1, true /*fail*/);
+  }
+
+  // Still can destroy the buffer even if it has not been attached to any
+  // widgets.
+  {
+    EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
+                                                     kBufferId1);
+    DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+  }
+
+  // ... impossible to destroy buffers twice.
+  {
+    EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(3);
+    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
+                                                     kBufferId1);
+    // Attach to a surface.
+    buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds());
+
+    // Created non-attached buffer as well.
+    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
+                                                     kBufferId2);
+
+    DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+    // Can't destroy the buffer with non-existing id (the manager cleared the
+    // state after the previous failure).
+    DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, true /*fail*/);
+
+    // Non-attached buffer must have been also destroyed (we can't destroy it
+    // twice) if there was a failure.
+    DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
+                                            kBufferId2, true /*fail*/);
+
+    // Create and destroy non-attached buffer twice.
+    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
+                                                     kBufferId2);
+    DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
+                                            kBufferId2, false /*fail*/);
+    DestroyBufferAndSetTerminateExpectation(gfx::kNullAcceleratedWidget,
+                                            kBufferId2, true /*fail*/);
+  }
+}
+
+TEST_P(WaylandBufferManagerTest, CommitBufferNonExistingBufferId) {
+  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, 1u);
+
+  // Can't commit for non-existing buffer id.
+  SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/);
+  buffer_manager_gpu_->CommitBuffer(window_->GetWidget(), 5u,
+                                    window_->GetBounds());
+
+  Sync();
+}
+
+TEST_P(WaylandBufferManagerTest, CommitBufferNullWidget) {
+  constexpr uint32_t kBufferId = 1;
+  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId);
+
+  // Can't commit for non-existing widget.
+  SetTerminateCallbackExpectationAndDestroyChannel(&callback_, true /*fail*/);
+  buffer_manager_gpu_->CommitBuffer(gfx::kNullAcceleratedWidget, kBufferId,
+                                    window_->GetBounds());
+
+  Sync();
 }
 
 TEST_P(WaylandBufferManagerTest, EnsureCorrectOrderOfCallbacks) {
@@ -370,10 +443,8 @@
 
   auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
   EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(2);
-  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
-                                                   kBufferId1);
-  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
-                                                   kBufferId2);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2);
 
   Sync();
 
@@ -461,6 +532,9 @@
   mock_surface->ReleasePrevAttachedBuffer();
 
   Sync();
+
+  DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+  DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
 }
 
 TEST_P(WaylandBufferManagerTest, TestCommitBufferConditions) {
@@ -473,7 +547,7 @@
   auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
   EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
 
-  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
                                                    kDmabufBufferId);
 
   // Part 1: the surface mustn't have a buffer attached until
@@ -505,7 +579,7 @@
   // sent by the server.
 
   EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
-  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
                                                    kDmabufBufferId2);
 
   ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
@@ -530,84 +604,11 @@
   mock_surface->SendFrameCallback();
 
   Sync();
-}
 
-TEST_P(WaylandBufferManagerTest, HandleAnonymousBuffers) {
-  constexpr uint32_t kBufferId1 = 1;
-  constexpr uint32_t kBufferId2 = 2;
-
-  const gfx::AcceleratedWidget widget = window_->GetWidget();
-  constexpr gfx::AcceleratedWidget null_widget = gfx::kNullAcceleratedWidget;
-
-  // This section tests that it is impossible to create buffers with the same
-  // id regardless of the passed widget.
-  {
-    EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
-    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
-                                                     null_widget, kBufferId1);
-    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
-                                                     kBufferId2);
-
-    // Can't create buffer with existing id.
-    CreateDmabufBasedBufferAndSetTerminateExpecation(true /*fail*/, null_widget,
-                                                     kBufferId2);
-    // Can't destroy buffer with non-existing id (the manager cleared the
-    // state after the previous failure).
-    DestroyBufferAndSetTerminateExpectation(null_widget, kBufferId2,
-                                            true /*fail*/);
-  }
-
-  // Tests that can't destroy anonymous buffer with the same id as the
-  // previous non-anonymous buffer and other way round.
-  {
-    EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(2);
-    // Same id for anonymous and non-anonymous.
-    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, widget,
-                                                     kBufferId1);
-    DestroyBufferAndSetTerminateExpectation(null_widget, kBufferId1,
-                                            true /*fail*/);
-
-    // Same id for non-anonymous and anonymous.
-    CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/,
-                                                     null_widget, kBufferId1);
-    DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, true
-                                            /*fail*/);
-  }
-
-  // This section tests that it is impossible to destroy buffers with
-  // non-existing ids (for example, if the have already been destroyed) for
-  // anonymous buffers.
-  {
-    EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
-    CreateDmabufBasedBufferAndSetTerminateExpecation(false
-                                                     /*fail*/,
-                                                     null_widget, kBufferId1);
-    DestroyBufferAndSetTerminateExpectation(null_widget, kBufferId1, false
-                                            /*fail*/);
-    // Can't destroy the same buffer twice (non-existing id).
-    DestroyBufferAndSetTerminateExpectation(null_widget, kBufferId1, true
-                                            /*fail*/);
-  }
-
-  // Makes sure the anonymous buffer can be attached to a surface and
-  // destroyed.
-  {
-    EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
-    CreateDmabufBasedBufferAndSetTerminateExpecation(false
-                                                     /*fail*/,
-                                                     null_widget, kBufferId1);
-
-    buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, window_->GetBounds());
-
-    // Now, we must be able to destroy this buffer with widget provided. That
-    // is, if the buffer has been attached to a surface, it can be destroyed.
-    DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false
-                                            /*fail*/);
-
-    // And now test we can't destroy the same buffer providing a null widget.
-    DestroyBufferAndSetTerminateExpectation(null_widget, kBufferId1, true
-                                            /*fail*/);
-  }
+  DestroyBufferAndSetTerminateExpectation(widget, kDmabufBufferId,
+                                          false /*fail*/);
+  DestroyBufferAndSetTerminateExpectation(widget, kDmabufBufferId2,
+                                          false /*fail*/);
 }
 
 // The buffer that is not originally attached to any of the surfaces,
@@ -627,8 +628,7 @@
 
   auto* linux_dmabuf = server_.zwp_linux_dmabuf_v1();
   EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
-  CreateDmabufBasedBufferAndSetTerminateExpecation(
-      false /*fail*/, gfx::kNullAcceleratedWidget, kBufferId1);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1);
 
   Sync();
 
@@ -661,8 +661,7 @@
   // Now synchronously create a second buffer and commit it. The release
   // callback must be setup and OnSubmission must be called.
   EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
-  CreateDmabufBasedBufferAndSetTerminateExpecation(
-      false /*fail*/, gfx::kNullAcceleratedWidget, kBufferId2);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2);
 
   Sync();
 
@@ -690,8 +689,7 @@
   // released once the buffer is committed and processed (that is, it must be
   // able to setup a buffer release callback).
   EXPECT_CALL(*linux_dmabuf, CreateParams(_, _, _)).Times(1);
-  CreateDmabufBasedBufferAndSetTerminateExpecation(
-      false /*fail*/, gfx::kNullAcceleratedWidget, kBufferId3);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId3);
 
   Sync();
 
@@ -719,6 +717,124 @@
   mock_surface->ReleasePrevAttachedBuffer();
 
   Sync();
+
+  DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+  DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
+  DestroyBufferAndSetTerminateExpectation(widget, kBufferId3, false /*fail*/);
+}
+
+TEST_P(WaylandBufferManagerTest, DestroyBufferForDestroyedWindow) {
+  constexpr uint32_t kBufferId = 1;
+
+  auto temp_window = CreateWindow();
+  auto widget = temp_window->GetWidget();
+
+  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId);
+
+  Sync();
+
+  buffer_manager_gpu_->CommitBuffer(widget, kBufferId,
+                                    temp_window->GetBounds());
+
+  Sync();
+
+  temp_window.reset();
+  DestroyBufferAndSetTerminateExpectation(widget, kBufferId, false /*fail*/);
+}
+
+TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionSingleBuffer) {
+  constexpr uint32_t kBufferId = 1;
+
+  auto temp_window = CreateWindow();
+  auto widget = temp_window->GetWidget();
+  auto bounds = temp_window->GetBounds();
+
+  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId);
+
+  ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
+                                               false /* fail */);
+
+  Sync();
+
+  MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
+
+  // All the other expectations must come in order.
+  ::testing::InSequence sequence;
+  EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(0);
+  EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+
+  temp_window.reset();
+
+  buffer_manager_gpu_->CommitBuffer(widget, kBufferId, bounds);
+
+  Sync();
+
+  DestroyBufferAndSetTerminateExpectation(widget, kBufferId, false /*fail*/);
+}
+
+TEST_P(WaylandBufferManagerTest, DestroyedWindowNoSubmissionMultipleBuffers) {
+  constexpr uint32_t kBufferId1 = 1;
+  constexpr uint32_t kBufferId2 = 2;
+
+  auto temp_window = CreateWindow();
+  auto widget = temp_window->GetWidget();
+  auto bounds = temp_window->GetBounds();
+
+  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId1);
+  ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
+                                               false /* fail */);
+
+  Sync();
+
+  MockSurfaceGpu mock_surface_gpu(buffer_manager_gpu_.get(), widget);
+
+  // All the other expectations must come in order.
+  ::testing::InSequence sequence;
+  EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(1);
+  EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(1);
+
+  buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds);
+
+  Sync();
+
+  auto* mock_surface = server_.GetObject<wl::MockSurface>(widget);
+  mock_surface->SendFrameCallback();
+
+  Sync();
+
+  EXPECT_CALL(*server_.zwp_linux_dmabuf_v1(), CreateParams(_, _, _)).Times(1);
+  CreateDmabufBasedBufferAndSetTerminateExpecation(false /*fail*/, kBufferId2);
+  ProcessCreatedBufferResourcesWithExpectation(1u /* expected size */,
+                                               false /* fail */);
+
+  Sync();
+
+  EXPECT_CALL(mock_surface_gpu,
+              OnSubmission(kBufferId2, gfx::SwapResult::SWAP_ACK))
+      .Times(1);
+  EXPECT_CALL(mock_surface_gpu, OnPresentation(kBufferId2, _)).Times(1);
+
+  buffer_manager_gpu_->CommitBuffer(widget, kBufferId2, bounds);
+
+  Sync();
+
+  mock_surface->ReleasePrevAttachedBuffer();
+
+  Sync();
+
+  EXPECT_CALL(mock_surface_gpu, OnSubmission(_, _)).Times(0);
+  EXPECT_CALL(mock_surface_gpu, OnPresentation(_, _)).Times(0);
+  temp_window.reset();
+
+  buffer_manager_gpu_->CommitBuffer(widget, kBufferId1, bounds);
+
+  Sync();
+
+  DestroyBufferAndSetTerminateExpectation(widget, kBufferId1, false /*fail*/);
+  DestroyBufferAndSetTerminateExpectation(widget, kBufferId2, false /*fail*/);
 }
 
 INSTANTIATE_TEST_SUITE_P(XdgVersionV5Test,
diff --git a/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom b/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom
index 9b5bf1a7..3808188b 100644
--- a/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom
+++ b/ui/ozone/public/mojom/wayland/wayland_buffer_manager.mojom
@@ -22,18 +22,20 @@
   // The following two methods are used either for hardware accelerated
   // rendering or for the software rendering.
   //
-  // If hardware accelerated rendering path is taken, this methnod can be used
-  // to ask Wayland to create a wl_buffer based on the |dmabuf_fd| descriptor
-  // for the WaylandWindow, which has the following |widget|. The |size|
-  // is the size of the buffer, the |strides|, |offsets| and |modifiers|
-  // are the descriptions of the drm buffer object. The |format| describes
-  // the buffer format (check gfx::BufferFormat) in fourcc form. The
-  // |planes_count| says how many planes the buffer, backed by the |file|
-  // descriptor has. And the |buffer_id| is a unique id for the buffer, which
-  // is used to identify imported wl_buffers on the browser process side and
-  // map them with the buffer objects on the gpu process side.
-  CreateDmabufBasedBuffer(gfx.mojom.AcceleratedWidget widget,
-                          handle dmabuf_fd,
+  // If the hardware accelerated rendering path is taken, this method can be
+  // used to ask Wayland to create a wl_buffer based on the |dmabuf_fd|
+  // descriptor. The |size| is the size of the buffer, the |strides|,
+  // |offsets| and |modifiers| are the descriptions of the drm buffer object.
+  // The |format| describes the buffer format (check gfx::BufferFormat) in
+  // fourcc form. The |planes_count| says how many planes the buffer, backed
+  // by the |file| descriptor has. And the |buffer_id| is a unique id for the
+  // buffer, which is used to identify imported wl_buffers on the browser
+  // process side and map them with the buffer objects on the gpu process side.
+  // The buffer will be associated with an AcceleratedWidget as soon as the
+  // very first CommitBuffer request comes from viz to browser process.
+  // If the buffer has been committed at least once, it is not possible to
+  // reassign it to another AcceleratedWidget.
+  CreateDmabufBasedBuffer(handle dmabuf_fd,
                           gfx.mojom.Size size,
                           array<uint32> strides,
                           array<uint32> offsets,
@@ -46,8 +48,11 @@
   // Wayland to create a wl_buffer based on the |shm_fd| descriptor.
   // The |length| is the length of the shared memory, |size|
   // is the size of buffer and |buffer_id| is the id of the buffer.
-  CreateShmBasedBuffer(gfx.mojom.AcceleratedWidget widget,
-                       handle shm_fd,
+  // The buffer will be associated with an AcceleratedWidget as soon as the
+  // very first CommitBuffer request comes from viz to browser process. If
+  // the buffer has been committed at least once, it is not possible to
+  // reassign it to another AcceleratedWidget.
+  CreateShmBasedBuffer(handle shm_fd,
                        uint64 length,
                        gfx.mojom.Size size,
                        uint32 buffer_id);
@@ -57,11 +62,14 @@
   // Destroys a wl_buffer created by WaylandConnection based on the |buffer_id|
   // for the WaylandWindow, which has the following |widget|. The |buffer_id|
   // is the unique id of the buffer objects being destroyed on the browser
-  // process side.
+  // process side. If the buffer with |buffer_id| has never been assigned to an
+  // AcceleratedWidget, it can be destroyed by passing a null widget
+  // with a correct buffer id. Providing wrong pair of the |widget| and the
+  // |buffer_id| will result in the termination of the GPU process.
   DestroyBuffer(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id);
 
   // Attaches a wl_buffer to a WaylandWindow's surface with the following
-  // |widget|. The |damage_region| describes changed the region of the buffer.
+  // |widget|. The |damage_region| describes the changed region of the buffer.
   // The |buffer_id| is a unique id for the buffer, which is used to
   // identify imported wl_buffers on the browser process side mapped with
   // the ones on the gpu process.
diff --git a/weblayer/browser/android/javatests/BUILD.gn b/weblayer/browser/android/javatests/BUILD.gn
index 280be58..7af13a3 100644
--- a/weblayer/browser/android/javatests/BUILD.gn
+++ b/weblayer/browser/android/javatests/BUILD.gn
@@ -12,6 +12,7 @@
   min_sdk_version = 21
   deps = [
     "//base:base_java_test_support",
+    "//components/safe_browsing/android:safe_browsing_java",
     "//content/public/test/android:content_java_test_support",
     "//net/android:net_java_test_support",
     "//third_party/android_support_test_runner:runner_java",
diff --git a/weblayer/common/features.cc b/weblayer/common/features.cc
index 2164695..69ac12b 100644
--- a/weblayer/common/features.cc
+++ b/weblayer/common/features.cc
@@ -11,7 +11,7 @@
 
 // Safebrowsing support for weblayer.
 const base::Feature kWebLayerSafeBrowsing{"WebLayerSafeBrowsing",
-                                          base::FEATURE_DISABLED_BY_DEFAULT};
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
 
 }  // namespace features
 }  // namespace weblayer
diff --git a/weblayer/test/BUILD.gn b/weblayer/test/BUILD.gn
index 84225e5..56536ebe 100644
--- a/weblayer/test/BUILD.gn
+++ b/weblayer/test/BUILD.gn
@@ -31,6 +31,7 @@
       ":weblayer_browsertests_manifest",
       "//base:base_java",
       "//base:base_java_test_support",
+      "//components/safe_browsing/android:safe_browsing_java",
       "//content/public/android:content_java",
       "//content/public/test/android:content_java_test_support",
       "//testing/android/native_test:native_test_java",