diff --git a/DEPS b/DEPS
index 581d0a3..4fe8fae 100644
--- a/DEPS
+++ b/DEPS
@@ -185,7 +185,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'f59495ddfa9efdfa59907712753408a92a00b362',
+  'v8_revision': 'bd2510d9d872670cf7295ade5fbda6f617dff0ba',
   # 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.
@@ -193,15 +193,15 @@
   # 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': 'bf8bd80526735b56a0038f97734cc9d436245adb',
+  'angle_revision': '27db24581e9ef6a309ac43d9a5e89f1d9bf161c4',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '368d39c30c4279505f8d0de2436b8dac2d98f88e',
+  'swiftshader_revision': 'a7e42ba7f855d8adcbca8482a803dc2b0f06fc6f',
   # 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': 'f793e3abc4ecaea52b9f1e960df3505233e466d6',
+  'pdfium_revision': '9346b7c0ed1a2adbb1c25095822cdfc08dbfa0c0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -244,7 +244,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'ae4bbcda1a91e3836fb32d778560360be3c895ee',
+  'catapult_revision': 'ce9e11f02436651b9336bee1b70f09dd75d015a0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -904,7 +904,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '797d74a266bb5ffaa3882dd6a19432d586be776c',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '12f8d69f1288fe1d9ba6c169279d0ac5f652e8b4',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1449,7 +1449,7 @@
     Var('chromium_git') + '/external/smhasher.git' + '@' + 'e87738e57558e0ec472b2fc3a643b838e5b6e88f',
 
   'src/third_party/snappy/src':
-    Var('chromium_git') + '/external/github.com/google/snappy.git' + '@' + '156cd8939c5fba7fa68ae08db843377ecc07b4b5',
+    Var('chromium_git') + '/external/github.com/google/snappy.git' + '@' + 'f5acee902c4d2110f671455460172cb6d3bf5b73',
 
   'src/third_party/sqlite4java': {
       'packages': [
@@ -1498,7 +1498,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '88d715c9115a5ce65c0bf660209dfeee9131ccd0',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '5c35f2fb1bc2325f0cd23db52d73db75fccc89c9',
+    Var('webrtc_git') + '/src.git' + '@' + '3b19b2734385359704c193df054041599e30e9f2',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1568,7 +1568,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ac0b770408f5d80f82a06e6429dccd6dc40a79c1',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@d9ef631c223af00b0554afbca7c7a86098ba3a8d',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 75b4b35..28ea164 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1217,6 +1217,19 @@
     'media_gpu': {
       'filepath': 'media/gpu/',
     },
+    'media_gpu_cros': {
+      'filepath': 'media/gpu/chromeos'\
+                  '|media/gpu/vaapi'\
+                  '|media/gpu/v4l2'\
+                  '|media/gpu/.*\.(cc|h)$'\
+                  '|media/mojo/(clients|mojom|test|services)/.*accelerator.*'
+    },
+    'media_gpu_vaapi': {
+      'filepath': 'media/gpu/vaapi',
+    },
+    'media_gpu_win': {
+      'filepath': 'media/gpu/windows/'
+    },
     'media_mojo': {
       'filepath': 'media/mojo/'
     },
@@ -1246,9 +1259,6 @@
                   '|chrome/test/media_router/' \
                   '|chrome/utility/media_router/'
     },
-    'media_win': {
-      'filepath': 'media/gpu/windows/'
-    },
     'message_loop': {
       'filepath': 'base/message_'
     },
@@ -1857,9 +1867,6 @@
     'usb': {
       'filepath': '/usb/',
     },
-    'vaapi': {
-      'filepath': 'media/gpu/vaapi',
-    },
     'version_assembly': {
       'filepath': 'chrome/app/version_assembly',
     },
@@ -2519,6 +2526,9 @@
     'media_galleries': ['thestig@chromium.org',
                         'tommycli@chromium.org'],
     'media_gpu': ['hiroh+watch@chromium.org'],
+    'media_gpu_cros': ['media-cros-reviews@chromium.org'],
+    'media_gpu_vaapi': ['vaapi-reviews@chromium.org'],
+    'media_gpu_win': ['media-win-reviews@chromium.org'],
     'media_mojo': ['alokp+watch@chromium.org',
                    'xhwang+watch@chromium.org'],
     'media_recorder': ['emircan+watch+mediarecorder@chromium.org',
@@ -2529,7 +2539,6 @@
     'media_router': ['mfoltz+watch@chromium.org',
                      'pthatcher+watch@chromium.org',
                      'takumif+watch@chromium.org'],
-    'media_win': ['media-win-reviews@chromium.org'],
     'message_loop': ['sadrul@chromium.org'],
     'metrics': ['asvitkine+watch@chromium.org'],
     'metrics_perf': ['cwp-reviews+chromium@google.com'],
@@ -2760,7 +2769,6 @@
     'unified_consent': ['wfh+watch@chromuium.org'],
     'usb': ['mattreynolds+watch@chromium.org',
             'odejesush+watch@chromium.org'],
-    'vaapi': ['vaapi-reviews@chromium.org'],
     'version_assembly': ['caitkp+watch@chromium.org',
                          'gab+watch@chromium.org'],
     'video': ['posciak+watch@chromium.org'],
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 5d43175..3fd093a 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -511,15 +511,12 @@
   // Show the app list after signing in in tablet mode. For metrics, the app
   // list is not considered shown since the browser window is shown over app
   // list upon login.
-  if (!presenter_.GetView()) {
-    Show(GetDisplayIdToShowAppListOn(),
-         base::nullopt /* no AppListShowSource */, base::TimeTicks());
-  }
+  if (!presenter_.GetTargetVisibility())
+    Shell::Get()->home_screen_controller()->Show();
 
   // Hide app list UI initially to prevent app list from flashing in background
   // while the initial app window is being shown.
-  if (HasVisibleWindows() ||
-      Shell::Get()->overview_controller()->InOverviewSession()) {
+  if (!last_target_visible_) {
     presenter_.GetView()->SetVisible(false);
     presenter_.GetView()->search_box_view()->SetVisible(false);
   } else {
@@ -550,19 +547,6 @@
 
   UpdateLauncherContainer();
 
-  // Band-aid for https://b/144056527 to update visibility after AppListState
-  // change. Otherwise, previously calculated visibility in OnVisibilityChanged
-  // and OnVisibilityWillChange is not correct and makes focus change handler
-  // code in AppListPresenterImpl::OnWindowFocused close the app list window
-  // when focus moves into Assistant web contents.
-  aura::Window* app_list_window = GetWindow();
-  if (app_list_window) {
-    const bool app_list_visible = app_list_window->TargetVisibility();
-    if (app_list_visible != IsVisible()) {
-      OnVisibilityChanged(app_list_visible, last_visible_display_id_);
-    }
-  }
-
   if (new_state == AppListState::kStateEmbeddedAssistant) {
     // ShowUi will be no-op if the AssistantUiModel is already visible.
     Shell::Get()->assistant_controller()->ui_controller()->ShowUi(
@@ -1439,7 +1423,7 @@
   // HomeLauncher is only visible when no other app windows are visible,
   // unless we are in the process of animating to (or dragging) the home
   // launcher.
-  if (IsTabletMode() && ShouldLauncherShowBehindApps())
+  if (IsTabletMode())
     real_visibility &= !HasVisibleWindows();
 
   aura::Window* app_list_window = GetWindow();
@@ -1481,9 +1465,8 @@
   // HomeLauncher is only visible when no other app windows are visible,
   // unless we are in the process of animating to (or dragging) the home
   // launcher.
-  if (IsTabletMode() && ShouldLauncherShowBehindApps() &&
-      home_launcher_transition_state_ ==
-          HomeLauncherTransitionState::kFinished) {
+  if (IsTabletMode() && home_launcher_transition_state_ ==
+                            HomeLauncherTransitionState::kFinished) {
     real_target_visibility &= !HasVisibleWindows();
   }
 
diff --git a/ash/shelf/scrollable_shelf_view.cc b/ash/shelf/scrollable_shelf_view.cc
index 10cc28d92..259ece9 100644
--- a/ash/shelf/scrollable_shelf_view.cc
+++ b/ash/shelf/scrollable_shelf_view.cc
@@ -9,6 +9,7 @@
 #include "ash/public/cpp/presentation_time_recorder.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/screen_util.h"
+#include "ash/shelf/shelf_app_button.h"
 #include "ash/shelf/shelf_focus_cycler.h"
 #include "ash/shelf/shelf_navigation_widget.h"
 #include "ash/shelf/shelf_tooltip_manager.h"
@@ -131,6 +132,58 @@
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
+// DragIconDropAnimationDelegate
+
+class ScrollableShelfView::DragIconDropAnimationDelegate
+    : public ui::ImplicitAnimationObserver {
+ public:
+  DragIconDropAnimationDelegate(views::View* original_view,
+                                const gfx::Rect& target_bounds,
+                                std::unique_ptr<DragImageView> proxy_view)
+      : original_view_(original_view),
+        target_bounds_(target_bounds),
+        proxy_view_(std::move(proxy_view)) {}
+  ~DragIconDropAnimationDelegate() override = default;
+
+  DragIconDropAnimationDelegate(const DragIconDropAnimationDelegate&) = delete;
+  DragIconDropAnimationDelegate& operator=(
+      const DragIconDropAnimationDelegate&) = delete;
+
+  void StartAnimation() {
+    ui::ScopedLayerAnimationSettings animation_settings(
+        proxy_view_->layer()->GetAnimator());
+    animation_settings.SetTweenType(gfx::Tween::FAST_OUT_LINEAR_IN);
+    animation_settings.SetPreemptionStrategy(
+        ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET);
+    animation_settings.AddObserver(this);
+
+    proxy_view_->layer()->SetBounds(target_bounds_);
+  }
+
+  // ui::ImplicitAnimationObserver:
+  void OnImplicitAnimationsCompleted() override {
+    StopObserving();
+
+    // Destructs the proxy image view and shows the original drag view at the
+    // end of animation.
+    original_view_->layer()->SetOpacity(1.0f);
+    proxy_view_.reset();
+  }
+
+ private:
+  // Original app icon being dragged in ShelfView.
+  views::View* original_view_ = nullptr;
+
+  // The target bounds after icon is dropped in |proxy_view_| parent's
+  // coordinates.
+  gfx::Rect target_bounds_;
+
+  // Placeholder icon representing |original_icon_| that moves with the pointer
+  // while being dragged.
+  std::unique_ptr<DragImageView> proxy_view_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
 // GradientLayerDelegate
 
 class ScrollableShelfView::GradientLayerDelegate : public ui::LayerDelegate {
@@ -1045,6 +1098,8 @@
   drag_icon_->GetWidget()->SetVisibilityAnimationTransition(
       views::Widget::ANIMATE_NONE);
   drag_icon_->SetWidgetVisible(true);
+  drag_icon_->SetPaintToLayer();
+  drag_icon_->layer()->SetFillsBoundsOpaquely(false);
 }
 
 void ScrollableShelfView::UpdateDragIconProxy(
@@ -1063,10 +1118,47 @@
 }
 
 void ScrollableShelfView::DestroyDragIconProxy() {
-  drag_icon_.reset();
-
   if (page_flip_timer_.IsRunning())
     page_flip_timer_.AbandonAndStop();
+
+  views::View* drag_view = shelf_view_->drag_view();
+
+  const bool should_start_animation =
+      drag_view && !shelf_view_->dragged_off_shelf() && drag_icon_.get();
+  if (!should_start_animation) {
+    drag_icon_.reset();
+    return;
+  }
+
+  // The ideal bounds stored in view model are in |shelf_view_|'s coordinates.
+  views::ViewModel* shelf_view_model = shelf_view_->view_model();
+  const gfx::Rect target_bounds = shelf_view_model->ideal_bounds(
+      shelf_view_model->GetIndexOfView(drag_view));
+  const gfx::Rect mirrored_target_bounds =
+      shelf_view_->GetMirroredRect(target_bounds);
+
+  // No animation is created if the target slot for the drag icon is not on the
+  // current page. This edge case may be triggered by trying to move the icon of
+  // a running app to the area exclusively for pinned apps.
+  gfx::RectF target_bounds_in_local(mirrored_target_bounds);
+  ConvertRectToTarget(shelf_view_, this, &target_bounds_in_local);
+  if (!visible_space_.Contains(gfx::ToEnclosedRect(target_bounds_in_local))) {
+    drag_icon_.reset();
+    drag_view->layer()->SetOpacity(1.0f);
+    return;
+  }
+
+  // Converts the ideal bounds to |drag_icon_|'s coordinates. Notes that
+  // |drag_icon_| and |shelf_view_| are in different widgets.
+  gfx::Point origin_point = mirrored_target_bounds.origin();
+  ConvertPointToScreen(shelf_view_, &origin_point);
+  ConvertPointFromScreen(drag_icon_->parent(), &origin_point);
+
+  drag_icon_drop_animation_delegate_ =
+      std::make_unique<DragIconDropAnimationDelegate>(
+          drag_view, gfx::Rect(origin_point, target_bounds.size()),
+          std::move(drag_icon_));
+  drag_icon_drop_animation_delegate_->StartAnimation();
 }
 
 bool ScrollableShelfView::StartDrag(
@@ -1849,7 +1941,15 @@
   gfx::Rect visible_space_in_screen = visible_space_;
   views::View::ConvertRectToScreen(this, &visible_space_in_screen);
 
-  return visible_space_in_screen.Contains(drag_icon_->GetBoundsInScreen());
+  const gfx::Rect drag_icon_screen_bounds = drag_icon_->GetBoundsInScreen();
+
+  if (GetShelf()->IsHorizontalAlignment()) {
+    return drag_icon_screen_bounds.x() >= visible_space_in_screen.x() &&
+           drag_icon_screen_bounds.right() <= visible_space_in_screen.right();
+  }
+
+  return drag_icon_screen_bounds.y() >= visible_space_in_screen.y() &&
+         drag_icon_screen_bounds.bottom() <= visible_space_in_screen.bottom();
 }
 
 bool ScrollableShelfView::ShouldDelegateScrollToShelf(
diff --git a/ash/shelf/scrollable_shelf_view.h b/ash/shelf/scrollable_shelf_view.h
index 78f73b4..4eadbbc 100644
--- a/ash/shelf/scrollable_shelf_view.h
+++ b/ash/shelf/scrollable_shelf_view.h
@@ -95,6 +95,8 @@
   LayoutStrategy layout_strategy_for_test() const { return layout_strategy_; }
   gfx::Vector2dF scroll_offset_for_test() const { return scroll_offset_; }
 
+  const DragImageView* drag_icon_for_test() const { return drag_icon_.get(); }
+
   int first_tappable_app_index() { return first_tappable_app_index_; }
   int last_tappable_app_index() { return last_tappable_app_index_; }
 
@@ -117,6 +119,7 @@
  private:
   class GradientLayerDelegate;
   class ScrollableShelfArrowView;
+  class DragIconDropAnimationDelegate;
 
   struct FadeZone {
     // Bounds of the fade in/out zone.
@@ -437,6 +440,11 @@
   // app icon can be dragged out of the shelf view.
   std::unique_ptr<DragImageView> drag_icon_;
 
+  // The delegate to create the animation of moving the dropped icon to the
+  // ideal place after drag release.
+  std::unique_ptr<DragIconDropAnimationDelegate>
+      drag_icon_drop_animation_delegate_;
+
   base::OneShotTimer page_flip_timer_;
 
   // Metric reporter for scrolling animations.
diff --git a/ash/shelf/scrollable_shelf_view_unittest.cc b/ash/shelf/scrollable_shelf_view_unittest.cc
index a67f285..a3eef910 100644
--- a/ash/shelf/scrollable_shelf_view_unittest.cc
+++ b/ash/shelf/scrollable_shelf_view_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "ash/shelf/scrollable_shelf_view.h"
 
+#include "ash/drag_drop/drag_image_view.h"
 #include "ash/public/cpp/shelf_config.h"
 #include "ash/root_window_controller.h"
 #include "ash/shelf/shelf_test_util.h"
@@ -385,7 +386,9 @@
   EXPECT_TRUE(tooltip_manager->IsVisible());
 }
 
-// Verifies that dragging an app icon to a new shelf page works well.
+// Verifies that dragging an app icon to a new shelf page works well. In
+// addition, the dragged icon moves with mouse before mouse release (see
+// https://crbug.com/1031367).
 TEST_F(ScrollableShelfViewTest, DragIconToNewPage) {
   scrollable_shelf_view_->set_page_flip_time_threshold(
       base::TimeDelta::FromMilliseconds(10));
@@ -401,8 +404,13 @@
       view_model->view_at(scrollable_shelf_view_->last_tappable_app_index());
   const gfx::Point drag_start_point =
       dragged_view->GetBoundsInScreen().CenterPoint();
+
+  // Ensures that the app icon is not dragged to the ideal bounds directly.
+  // It helps to construct a more complex scenario that the animation
+  // is created to move the dropped icon to the target place after drag release.
   const gfx::Point drag_end_point =
-      scrollable_shelf_view_->left_arrow()->GetBoundsInScreen().CenterPoint();
+      scrollable_shelf_view_->left_arrow()->GetBoundsInScreen().origin() -
+      gfx::Vector2d(10, 0);
 
   ASSERT_NE(0, view_model->GetIndexOfView(dragged_view));
 
@@ -415,7 +423,18 @@
     PageFlipWaiter waiter(scrollable_shelf_view_);
     waiter.Wait();
   }
+
+  // Expects that the drag icon moves with drag pointer before mouse release.
+  const gfx::Rect intermediate_bounds =
+      scrollable_shelf_view_->drag_icon_for_test()->GetBoundsInScreen();
+  EXPECT_EQ(drag_end_point, intermediate_bounds.CenterPoint());
+
   GetEventGenerator()->ReleaseLeftButton();
+  ASSERT_NE(intermediate_bounds.CenterPoint(),
+            dragged_view->GetBoundsInScreen().CenterPoint());
+
+  // Expects that the proxy icon is deleted after mouse release.
+  EXPECT_EQ(nullptr, scrollable_shelf_view_->drag_icon_for_test());
 
   // Verifies that:
   // (1) Scrollable shelf view has the expected layout strategy.
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index 37cce9a..47b39f4 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -1490,14 +1490,9 @@
   if (drag_pointer_ != NONE)
     return;
 
-  if (chromeos::switches::ShouldShowScrollableShelf()) {
+  if (chromeos::switches::ShouldShowScrollableShelf())
     drag_and_drop_host_->DestroyDragIconProxy();
 
-    // |drag_view_| is reset already when being removed from the shelf view.
-    if (drag_view_)
-      drag_view_->layer()->SetOpacity(1.0f);
-  }
-
   // If the drag pointer is NONE, no drag operation is going on and the
   // drag_view can be released.
   drag_view_ = nullptr;
@@ -1658,13 +1653,17 @@
     }
   }
 
+  // Calculates the drag point in screen before MoveDragViewTo is called.
+  gfx::Point drag_point_in_screen(event.location());
+  ConvertPointToScreen(drag_view_, &drag_point_in_screen);
+
   gfx::Point drag_point(event.location());
   ConvertPointToTarget(drag_view_, this, &drag_point);
   MoveDragViewTo(shelf_->PrimaryAxisValue(drag_point.x() - drag_origin_.x(),
                                           drag_point.y() - drag_origin_.y()));
   if (chromeos::switches::ShouldShowScrollableShelf()) {
-    drag_and_drop_host_->UpdateDragIconProxy(
-        drag_view_->GetBoundsInScreen().origin());
+    drag_and_drop_host_->UpdateDragIconProxy(drag_point_in_screen -
+                                             drag_origin_.OffsetFromOrigin());
   }
 }
 
@@ -1873,12 +1872,13 @@
                         drag_view_, gfx::Vector2d(0, 0),
                         kDragAndDropProxyScale);
 
+    dragged_off_shelf_ = true;
+
     if (chromeos::switches::ShouldShowScrollableShelf())
       drag_and_drop_host_->DestroyDragIconProxy();
     else
       drag_view_->layer()->SetOpacity(0.0f);
 
-    dragged_off_shelf_ = true;
     if (RemovableByRipOff(current_index) == REMOVABLE) {
       // Move the item to the back and hide it. ShelfItemMoved() callback will
       // handle the |view_model_| update and call AnimateToIdealBounds().
diff --git a/ash/shelf/shelf_view.h b/ash/shelf/shelf_view.h
index 4fa7c2e..6b8d082 100644
--- a/ash/shelf/shelf_view.h
+++ b/ash/shelf/shelf_view.h
@@ -332,7 +332,7 @@
     app_icons_layout_offset_ = app_icons_layout_offset;
   }
 
-  const ShelfAppButton* drag_view() const { return drag_view_; }
+  ShelfAppButton* drag_view() { return drag_view_; }
 
   // Returns true when this ShelfView is used for Overflow Bubble.
   // In this mode, it does not show app list and overflow button.
@@ -356,6 +356,7 @@
   ShelfWidget* shelf_widget() const { return shelf_->shelf_widget(); }
   OverflowBubble* overflow_bubble() { return overflow_bubble_.get(); }
   views::ViewModel* view_model() { return view_model_.get(); }
+  bool dragged_off_shelf() const { return dragged_off_shelf_; }
 
  private:
   friend class ShelfViewTestAPI;
diff --git a/ash/wm/overview/overview_grid_unittest.cc b/ash/wm/overview/overview_grid_unittest.cc
index 028770c..9053c15 100644
--- a/ash/wm/overview/overview_grid_unittest.cc
+++ b/ash/wm/overview/overview_grid_unittest.cc
@@ -7,7 +7,9 @@
 #include "ash/screen_util.h"
 #include "ash/shell.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/overview/overview_controller.h"
 #include "ash/wm/overview/overview_item.h"
+#include "ash/wm/overview/overview_test_util.h"
 #include "ash/wm/splitview/split_view_controller.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
@@ -258,14 +260,12 @@
 
 // Tests that only one window animates when entering overview from splitview
 // double snapped.
-// TODO(sammiequon): The way this test is setup causes an unnatural state by
-// calling |GetGridBoundsInScreen| with split view state both snapped. Find a
-// way to re-enable this.
-TEST_F(OverviewGridTest, DISABLED_SnappedWindow) {
+TEST_F(OverviewGridTest, SnappedWindow) {
   auto window1 = CreateTestWindow(gfx::Rect(100, 100));
   auto window2 = CreateTestWindow(gfx::Rect(100, 100));
   auto window3 = CreateTestWindow(gfx::Rect(100, 100));
   wm::ActivateWindow(window1.get());
+  wm::ActivateWindow(window2.get());
 
   Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
   split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT);
@@ -275,13 +275,19 @@
                                       SplitViewController::RIGHT);
   EXPECT_TRUE(WindowState::Get(window3.get())->IsMaximized());
 
+  // We cannot create a grid object like in the other tests because creating a
+  // grid calls |GetGridBoundsInScreen| with split view state both snapped which
+  // is an unnatural state.
+  Shell::Get()->overview_controller()->StartOverview(
+      OverviewSession::EnterExitOverviewType::kNormal);
+
   // Tests that |window3| is not animated even though its bounds are larger than
   // |window2| because it is fully occluded by |window1| + |window2| and the
   // split view divider.
-  std::vector<gfx::RectF> target_bounds = {gfx::RectF(100.f, 100.f),
-                                           gfx::RectF(100.f, 100.f)};
-  CheckAnimationStates({window2.get(), window3.get()}, target_bounds,
-                       {true, false}, {true, false});
+  OverviewItem* item2 = GetOverviewItemForWindow(window2.get());
+  OverviewItem* item3 = GetOverviewItemForWindow(window3.get());
+  EXPECT_TRUE(item2->should_animate_when_entering());
+  EXPECT_FALSE(item3->should_animate_when_entering());
 }
 
 }  // namespace ash
diff --git a/ash/wm/overview/overview_session_unittest.cc b/ash/wm/overview/overview_session_unittest.cc
index 9a048ce..4acc895 100644
--- a/ash/wm/overview/overview_session_unittest.cc
+++ b/ash/wm/overview/overview_session_unittest.cc
@@ -3700,12 +3700,13 @@
 
 // Verify the correct behavior when dragging windows in overview mode.
 TEST_P(SplitViewOverviewSessionTest, OverviewDragControllerBehavior) {
-  // TODO(sammiequon): Make this work once this feature is enabled by default
-  // for good.
-  if (base::FeatureList::IsEnabled(features::kNewOverviewLayout))
+  if (!base::FeatureList::IsEnabled(features::kNewOverviewLayout))
     return;
 
-  aura::Env::GetInstance()->set_throttle_input_on_resize_for_testing(false);
+  ui::GestureConfiguration* gesture_config =
+      ui::GestureConfiguration::GetInstance();
+  gesture_config->set_long_press_time_in_ms(1);
+  gesture_config->set_show_press_delay_in_ms(1);
 
   std::unique_ptr<aura::Window> window1 = CreateTestWindow();
   std::unique_ptr<aura::Window> window2 = CreateTestWindow();
@@ -3723,9 +3724,18 @@
   generator->set_current_screen_location(
       gfx::ToRoundedPoint(window_item1->target_bounds().CenterPoint()));
   generator->PressTouch();
+
+  // Simulate a long press, which is required to snap windows.
+  base::RunLoop run_loop;
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromMilliseconds(2));
+  run_loop.Run();
+
   OverviewWindowDragController* drag_controller =
       overview_session()->window_drag_controller();
-  EXPECT_EQ(DragBehavior::kUndefined, drag_controller->current_drag_behavior());
+  ASSERT_TRUE(drag_controller);
+  EXPECT_EQ(DragBehavior::kNormalDrag,
+            drag_controller->current_drag_behavior());
   generator->MoveTouchBy(20, 0);
   EXPECT_EQ(DragBehavior::kNormalDrag,
             drag_controller->current_drag_behavior());
@@ -3737,12 +3747,13 @@
   generator->set_current_screen_location(
       gfx::ToRoundedPoint(window_item2->target_bounds().CenterPoint()));
   generator->PressTouch();
-  drag_controller = overview_session()->window_drag_controller();
-  EXPECT_EQ(DragBehavior::kUndefined, drag_controller->current_drag_behavior());
 
   // Use small increments otherwise a fling event will be fired.
   for (int j = 0; j < 20; ++j)
     generator->MoveTouchBy(0, 1);
+
+  // A new instance of drag controller gets created each time a drag starts.
+  drag_controller = overview_session()->window_drag_controller();
   EXPECT_EQ(DragBehavior::kDragToClose,
             drag_controller->current_drag_behavior());
 }
diff --git a/base/BUILD.gn b/base/BUILD.gn
index e48850c..90b62f44 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -2156,7 +2156,10 @@
   header = "clang_coverage_buildflags.h"
   header_dir = "base"
 
-  flags = [ "CLANG_COVERAGE=$use_clang_coverage" ]
+  flags = [
+    "CLANG_COVERAGE=$use_clang_coverage",
+    "CLANG_COVERAGE_INSIDE_SANDBOX=$use_clang_coverage_inside_sandbox"
+  ]
 }
 
 buildflag_header("sanitizer_buildflags") {
@@ -3305,6 +3308,7 @@
       "android/java/src/org/chromium/base/UserDataHost.java",
       "android/java/src/org/chromium/base/annotations/AccessedByNative.java",
       "android/java/src/org/chromium/base/annotations/CalledByNative.java",
+      "android/java/src/org/chromium/base/annotations/CalledByNativeJavaTest.java",
       "android/java/src/org/chromium/base/annotations/CalledByNativeUnchecked.java",
       "android/java/src/org/chromium/base/annotations/CheckDiscard.java",
       "android/java/src/org/chromium/base/annotations/DoNotInline.java",
diff --git a/base/android/java/src/org/chromium/base/annotations/CalledByNativeJavaTest.java b/base/android/java/src/org/chromium/base/annotations/CalledByNativeJavaTest.java
new file mode 100644
index 0000000..d17c1f2
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/annotations/CalledByNativeJavaTest.java
@@ -0,0 +1,24 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.base.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @CalledByNativeJavaTest is used by the JNI generator to create the necessary JNI
+ * bindings, expose this method to native code, and generate a native test proxy
+ * method for this Java Test method.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.CLASS)
+public @interface CalledByNativeJavaTest {
+    /*
+     *  If present, tells which inner class the method belongs to.
+     */
+    public String value() default "";
+}
diff --git a/base/android/jni_generator/README.md b/base/android/jni_generator/README.md
index 2d15c7af..7388847 100644
--- a/base/android/jni_generator/README.md
+++ b/base/android/jni_generator/README.md
@@ -214,6 +214,40 @@
  * `JavaParamRef<>` - Use to accept any of the above as a parameter to a
    function without creating a redundant registration.
 
+### Native Java Unittests and @CalledByNativeJavaTest
+
+Native Java Unittests are Java unit tests that run on Android (not on the host
+machine). Unlike junit and robolectric, these tests can use the native library,
+and real Android APIs. Unlike unit tests in ChromePublicTestApk and similar, the
+Activity is not restarted between tests, so the tests run much faster (and you
+must be careful not to leave state behind). Example tests may be found in
+chrome/android/native_java_unittests/.
+
+The @CalledByNativeJavaTest annotation causes the JNI generator to generate
+C++ test methods that simply call out to the Java test methods.
+
+For Example:
+```java
+class FooTest {
+  @CalledByNative public FooTest() {}
+  @CalledByNativeJavaTest public void testFoo() { ... }
+  @CalledByNativeJavaTest public void testOtherFoo() { ... }
+}
+```
+```c++
+class FooTest : public ::testing::Test {
+  FooTest() : j_test_(Java_FooTest_Constructor(AttachCurrentThread())) {}
+  const ScopedJavaGlobalRef<jobject>& j_test() { return j_test_; }
+ private:
+  ScopedJavaGlobalRef<jobject> j_test_;
+}
+JAVA_TESTS(AndroidPaymentAppFinderUnitTest, j_test())
+```
+
+In some cases you may want to run custom C++ code before running the Java test,
+in which case, use CalledByNative instead of CalledByNativeJavaTest and call the
+test method yourself. eg. Java_FooTest_testFancyFoo(env, j_test());
+
 ### Additional Guidelines / Advice
 
 Minimize the surface API between the two sides. Rather than calling multiple
diff --git a/base/android/jni_generator/golden/HashedSampleForAnnotationProcessor_jni.golden b/base/android/jni_generator/golden/HashedSampleForAnnotationProcessor_jni.golden
index ce5b642..8592458 100644
--- a/base/android/jni_generator/golden/HashedSampleForAnnotationProcessor_jni.golden
+++ b/base/android/jni_generator/golden/HashedSampleForAnnotationProcessor_jni.golden
@@ -262,4 +262,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_example_jni_generator_SampleForAnnotationProcessor_JNI
diff --git a/base/android/jni_generator/golden/SampleForAnnotationProcessor_jni.golden b/base/android/jni_generator/golden/SampleForAnnotationProcessor_jni.golden
index 313766fe..38324a2 100644
--- a/base/android/jni_generator/golden/SampleForAnnotationProcessor_jni.golden
+++ b/base/android/jni_generator/golden/SampleForAnnotationProcessor_jni.golden
@@ -280,4 +280,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_example_jni_generator_SampleForAnnotationProcessor_JNI
diff --git a/base/android/jni_generator/golden/SampleForTests_jni.golden b/base/android/jni_generator/golden/SampleForTests_jni.golden
index 2efae836..d023191 100644
--- a/base/android/jni_generator/golden/SampleForTests_jni.golden
+++ b/base/android/jni_generator/golden/SampleForTests_jni.golden
@@ -524,4 +524,7 @@
 }  // namespace android
 }  // namespace base
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_generator/golden/testCalledByNativeJavaTest.golden b/base/android/jni_generator/golden/testCalledByNativeJavaTest.golden
new file mode 100644
index 0000000..938bc18
--- /dev/null
+++ b/base/android/jni_generator/golden/testCalledByNativeJavaTest.golden
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+// This file is autogenerated by
+//     base/android/jni_generator/jni_generator.py
+// For
+//     org/chromium/TestJni
+
+#ifndef org_chromium_TestJni_JNI
+#define org_chromium_TestJni_JNI
+
+#include <jni.h>
+
+#include "base/android/jni_generator/jni_generator_helper.h"
+
+
+// Step 1: Forward declarations.
+
+JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni[];
+const char kClassPath_org_chromium_TestJni[] = "org/chromium/TestJni";
+
+JNI_REGISTRATION_EXPORT extern const char kClassPath_org_chromium_TestJni_00024MyInnerClass[];
+const char kClassPath_org_chromium_TestJni_00024MyInnerClass[] =
+    "org/chromium/TestJni$MyInnerClass";
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT std::atomic<jclass> g_org_chromium_TestJni_clazz(nullptr);
+#ifndef org_chromium_TestJni_clazz_defined
+#define org_chromium_TestJni_clazz_defined
+inline jclass org_chromium_TestJni_clazz(JNIEnv* env) {
+  return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni,
+      &g_org_chromium_TestJni_clazz);
+}
+#endif
+// Leaking this jclass as we cannot use LazyInstance from some threads.
+JNI_REGISTRATION_EXPORT std::atomic<jclass> g_org_chromium_TestJni_00024MyInnerClass_clazz(nullptr);
+#ifndef org_chromium_TestJni_00024MyInnerClass_clazz_defined
+#define org_chromium_TestJni_00024MyInnerClass_clazz_defined
+inline jclass org_chromium_TestJni_00024MyInnerClass_clazz(JNIEnv* env) {
+  return base::android::LazyGetClass(env, kClassPath_org_chromium_TestJni_00024MyInnerClass,
+      &g_org_chromium_TestJni_00024MyInnerClass_clazz);
+}
+#endif
+
+
+// Step 2: Constants (optional).
+
+
+// Step 3: Method stubs.
+
+static std::atomic<jmethodID> g_org_chromium_TestJni_Constructor(nullptr);
+static base::android::ScopedJavaLocalRef<jobject> Java_TestJni_Constructor(JNIEnv* env) {
+  jclass clazz = org_chromium_TestJni_clazz(env);
+  CHECK_CLAZZ(env, clazz,
+      org_chromium_TestJni_clazz(env), NULL);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "<init>",
+          "()V",
+          &g_org_chromium_TestJni_Constructor);
+
+  jobject ret =
+      env->NewObject(clazz,
+          call_context.base.method_id);
+  return base::android::ScopedJavaLocalRef<jobject>(env, ret);
+}
+
+static std::atomic<jmethodID> g_org_chromium_TestJni_testFoo(nullptr);
+static jint Java_TestJni_testFoo(JNIEnv* env, const base::android::JavaRef<jobject>& obj) {
+  jclass clazz = org_chromium_TestJni_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_TestJni_clazz(env), 0);
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "testFoo",
+          "()I",
+          &g_org_chromium_TestJni_testFoo);
+
+  jint ret =
+      env->CallIntMethod(obj.obj(),
+          call_context.base.method_id);
+  return ret;
+}
+
+static std::atomic<jmethodID> g_org_chromium_TestJni_testOtherFoo(nullptr);
+static void Java_TestJni_testOtherFoo(JNIEnv* env, const base::android::JavaRef<jobject>& obj) {
+  jclass clazz = org_chromium_TestJni_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_TestJni_clazz(env));
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "testOtherFoo",
+          "()V",
+          &g_org_chromium_TestJni_testOtherFoo);
+
+     env->CallVoidMethod(obj.obj(),
+          call_context.base.method_id);
+}
+
+static std::atomic<jmethodID> g_org_chromium_TestJni_00024MyInnerClass_testInnerFoo(nullptr);
+static void Java_MyInnerClass_testInnerFoo(JNIEnv* env, const base::android::JavaRef<jobject>& obj)
+    {
+  jclass clazz = org_chromium_TestJni_00024MyInnerClass_clazz(env);
+  CHECK_CLAZZ(env, obj.obj(),
+      org_chromium_TestJni_00024MyInnerClass_clazz(env));
+
+  jni_generator::JniJavaCallContextChecked call_context;
+  call_context.Init<
+      base::android::MethodID::TYPE_INSTANCE>(
+          env,
+          clazz,
+          "testInnerFoo",
+          "()V",
+          &g_org_chromium_TestJni_00024MyInnerClass_testInnerFoo);
+
+     env->CallVoidMethod(obj.obj(),
+          call_context.base.method_id);
+}
+
+// Step 4: Generated test functions (optional).
+#define JAVA_TESTS(test_fixture, java_test_object)\
+  TEST_F(test_fixture, TestFoo) { \
+    JNIEnv* env = base::android::AttachCurrentThread(); \
+    Java_TestJni_testFoo(\
+        env, java_test_object); \
+  }\
+  TEST_F(test_fixture, TestOtherFoo) { \
+    JNIEnv* env = base::android::AttachCurrentThread(); \
+    Java_TestJni_testOtherFoo(\
+        env, java_test_object); \
+  }\
+  TEST_F(test_fixture, TestInnerFoo) { \
+    JNIEnv* env = base::android::AttachCurrentThread(); \
+    Java_MyInnerClass_testInnerFoo(\
+        env, java_test_object); \
+  }
+
+#endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testCalledByNatives.golden b/base/android/jni_generator/golden/testCalledByNatives.golden
index 6249bd6..8e82504a 100644
--- a/base/android/jni_generator/golden/testCalledByNatives.golden
+++ b/base/android/jni_generator/golden/testCalledByNatives.golden
@@ -475,4 +475,7 @@
   return base::android::ScopedJavaLocalRef<jobject>(env, ret);
 }
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testConstantsFromJavaP.golden b/base/android/jni_generator/golden/testConstantsFromJavaP.golden
index b0364515..292b2d1 100644
--- a/base/android/jni_generator/golden/testConstantsFromJavaP.golden
+++ b/base/android/jni_generator/golden/testConstantsFromJavaP.golden
@@ -2317,4 +2317,7 @@
 
 }  // namespace JNI_MotionEvent
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // android_view_MotionEvent_JNI
diff --git a/base/android/jni_generator/golden/testFromJavaP.golden b/base/android/jni_generator/golden/testFromJavaP.golden
index e5f9467..c824864 100644
--- a/base/android/jni_generator/golden/testFromJavaP.golden
+++ b/base/android/jni_generator/golden/testFromJavaP.golden
@@ -273,4 +273,7 @@
 
 }  // namespace JNI_InputStream
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // java_io_InputStream_JNI
diff --git a/base/android/jni_generator/golden/testFromJavaPGenerics.golden b/base/android/jni_generator/golden/testFromJavaPGenerics.golden
index 9c34dfc..c9b845c3 100644
--- a/base/android/jni_generator/golden/testFromJavaPGenerics.golden
+++ b/base/android/jni_generator/golden/testFromJavaPGenerics.golden
@@ -84,4 +84,7 @@
 
 }  // namespace JNI_HashSet
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // java_util_HashSet_JNI
diff --git a/base/android/jni_generator/golden/testInnerClassNatives.golden b/base/android/jni_generator/golden/testInnerClassNatives.golden
index 6cad2b0..2046e09c 100644
--- a/base/android/jni_generator/golden/testInnerClassNatives.golden
+++ b/base/android/jni_generator/golden/testInnerClassNatives.golden
@@ -57,4 +57,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuter.golden b/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuter.golden
index 5b695409..03d3463 100644
--- a/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuter.golden
+++ b/base/android/jni_generator/golden/testInnerClassNativesBothInnerAndOuter.golden
@@ -67,4 +67,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testInnerClassNativesMultiple.golden b/base/android/jni_generator/golden/testInnerClassNativesMultiple.golden
index 983fb1f..c6c0ba1 100644
--- a/base/android/jni_generator/golden/testInnerClassNativesMultiple.golden
+++ b/base/android/jni_generator/golden/testInnerClassNativesMultiple.golden
@@ -80,4 +80,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testMultipleJNIAdditionalImport.golden b/base/android/jni_generator/golden/testMultipleJNIAdditionalImport.golden
index 38cca86..2c0180a0 100644
--- a/base/android/jni_generator/golden/testMultipleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/golden/testMultipleJNIAdditionalImport.golden
@@ -68,4 +68,7 @@
           call_context.base.method_id, callback1.obj(), callback2.obj());
 }
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/golden/testNativeExportsOnlyOption.golden b/base/android/jni_generator/golden/testNativeExportsOnlyOption.golden
index 95ee8824..adbe95e5695 100644
--- a/base/android/jni_generator/golden/testNativeExportsOnlyOption.golden
+++ b/base/android/jni_generator/golden/testNativeExportsOnlyOption.golden
@@ -226,4 +226,7 @@
   return base::android::ScopedJavaLocalRef<jstring>(env, ret);
 }
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_example_jni_generator_SampleForTests_JNI
diff --git a/base/android/jni_generator/golden/testNatives.golden b/base/android/jni_generator/golden/testNatives.golden
index 98b2568..a2ceed2c 100644
--- a/base/android/jni_generator/golden/testNatives.golden
+++ b/base/android/jni_generator/golden/testNatives.golden
@@ -213,4 +213,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testNativesLong.golden b/base/android/jni_generator/golden/testNativesLong.golden
index 52169dcf..a0811ea 100644
--- a/base/android/jni_generator/golden/testNativesLong.golden
+++ b/base/android/jni_generator/golden/testNativesLong.golden
@@ -46,4 +46,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_TestJni_JNI
diff --git a/base/android/jni_generator/golden/testProxyNatives.golden b/base/android/jni_generator/golden/testProxyNatives.golden
index a6bd014..f768289 100644
--- a/base/android/jni_generator/golden/testProxyNatives.golden
+++ b/base/android/jni_generator/golden/testProxyNatives.golden
@@ -60,4 +60,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_example_SampleProxyJni_JNI
diff --git a/base/android/jni_generator/golden/testProxyNativesWithNatives.golden b/base/android/jni_generator/golden/testProxyNativesWithNatives.golden
index 728d604..b1a6737 100644
--- a/base/android/jni_generator/golden/testProxyNativesWithNatives.golden
+++ b/base/android/jni_generator/golden/testProxyNativesWithNatives.golden
@@ -114,4 +114,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/golden/testREForNatives.golden b/base/android/jni_generator/golden/testREForNatives.golden
index c4b3c14..e4a1e3b7 100644
--- a/base/android/jni_generator/golden/testREForNatives.golden
+++ b/base/android/jni_generator/golden/testREForNatives.golden
@@ -46,4 +46,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // foo_bar_JNI
diff --git a/base/android/jni_generator/golden/testSingleJNIAdditionalImport.golden b/base/android/jni_generator/golden/testSingleJNIAdditionalImport.golden
index 13fb55f3..e32bba7 100644
--- a/base/android/jni_generator/golden/testSingleJNIAdditionalImport.golden
+++ b/base/android/jni_generator/golden/testSingleJNIAdditionalImport.golden
@@ -64,4 +64,7 @@
           call_context.base.method_id, callback.obj());
 }
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/golden/testStaticBindingCaller.golden b/base/android/jni_generator/golden/testStaticBindingCaller.golden
index df55ec4..fa94bc1 100644
--- a/base/android/jni_generator/golden/testStaticBindingCaller.golden
+++ b/base/android/jni_generator/golden/testStaticBindingCaller.golden
@@ -97,4 +97,7 @@
 }
 
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/golden/testTracing.golden b/base/android/jni_generator/golden/testTracing.golden
index 5ebe728..6d414451 100644
--- a/base/android/jni_generator/golden/testTracing.golden
+++ b/base/android/jni_generator/golden/testTracing.golden
@@ -103,4 +103,7 @@
 }  // namespace chromium_foo
 }  // namespace org
 
+// Step 4: Generated test functions (optional).
+
+
 #endif  // org_chromium_foo_Foo_JNI
diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py
index 04801d01..ce3f5c2 100755
--- a/base/android/jni_generator/jni_generator.py
+++ b/base/android/jni_generator/jni_generator.py
@@ -173,6 +173,7 @@
     self.env_call = GetEnvCall(self.is_constructor, self.static,
                                self.return_type)
     self.static_cast = GetStaticCastForReturnType(self.return_type)
+    self.gen_test_method = kwargs.get('gen_test_method', False)
 
 
 class ConstantField(object):
@@ -670,7 +671,8 @@
 
 # Regex to match a string like "@CalledByNative public void foo(int bar)".
 RE_CALLED_BY_NATIVE = re.compile(
-    r'@CalledByNative(?P<Unchecked>(?:Unchecked)?)(?:\("(?P<annotation>.*)"\))?'
+    r'@CalledByNative(?P<Unchecked>(?:Unchecked)?)(?P<JavaTest>(?:JavaTest)?)'
+    r'(?:\("(?P<annotation>.*)"\))?'
     r'(?:\s+@\w+(?:\(.*\))?)*'  # Ignore any other annotations.
     r'\s+(?P<prefix>('
     r'(private|protected|public|static|abstract|final|default|synchronized)'
@@ -721,7 +723,8 @@
             return_type=return_type,
             name=name,
             is_constructor=is_constructor,
-            params=JniParams.Parse(match.group('params')))
+            params=JniParams.Parse(match.group('params')),
+            gen_test_method='JavaTest' in match.group('JavaTest'))
     ]
   # Check for any @CalledByNative occurrences that weren't matched.
   unmatched_lines = re.sub(RE_CALLED_BY_NATIVE, '', contents).split('\n')
@@ -1122,6 +1125,9 @@
 // Step 3: Method stubs.
 $METHOD_STUBS
 
+// Step 4: Generated test functions (optional).
+$TEST_METHODS
+
 #endif  // ${HEADER_GUARD}
 """)
     values = {
@@ -1132,6 +1138,7 @@
         'METHOD_STUBS': self.GetMethodStubsString(),
         'HEADER_GUARD': self.header_guard,
         'INCLUDES': self.GetIncludesString(),
+        'TEST_METHODS': self.GetTestMethodsString(),
     }
     open_namespace = self.GetOpenNamespaceString()
     if open_namespace:
@@ -1174,6 +1181,17 @@
         for called_by_native in self.called_by_natives
     ]
 
+  def GetTestMethodsString(self):
+    strings = []
+    for called_by_native in self.called_by_natives:
+      if not called_by_native.gen_test_method:
+        continue
+      strings += [self.GetTestMethodString(called_by_native)]
+    if strings:
+      strings.insert(0, "#define JAVA_TESTS(test_fixture, java_test_object)\\")
+    ret = '\n'.join(strings)
+    return ret[:-1]  # Drop trailing '\'.
+
   def GetIncludesString(self):
     if not self.options.includes:
       return ''
@@ -1454,6 +1472,20 @@
       values['TRACE_EVENT'] = ''
     return RemoveIndentedEmptyLines(template.substitute(values))
 
+  def GetTestMethodString(self, called_by_native):
+    method_template = Template("""\
+  TEST_F(test_fixture, ${METHOD_ID_VAR_NAME_UPPERCASE}) { \\
+    JNIEnv* env = base::android::AttachCurrentThread(); \\
+    Java_${JAVA_CLASS_ONLY}_${METHOD_ID_VAR_NAME}(\\
+        env, java_test_object); \\
+  }\\""")
+    values = self.GetCalledByNativeValues(called_by_native)
+    method_name = values['METHOD_ID_VAR_NAME']
+    values['METHOD_ID_VAR_NAME_UPPERCASE'] = method_name[0].upper(
+    ) + method_name[1:]
+    return RemoveIndentedEmptyLines(method_template.substitute(values))
+
+
   def GetTraceEventForNameTemplate(self, name_template, values):
     name = Template(name_template).substitute(values)
     return '  TRACE_EVENT0("jni", "%s");\n' % name
diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py
index 03a9959d..341db1ea 100755
--- a/base/android/jni_generator/jni_generator_tests.py
+++ b/base/android/jni_generator/jni_generator_tests.py
@@ -814,6 +814,85 @@
     except jni_generator.ParseError as e:
       self.assertEqual(('@CalledByNative', 'scooby doo'), e.context_lines)
 
+  def testCalledByNativeJavaTest(self):
+    test_data = """
+    class MyOuterClass {
+      @CalledByNative
+      public MyOuterClass() {}
+
+      @CalledByNativeJavaTest
+      public int testFoo() {}
+
+      @CalledByNativeJavaTest
+      public void testOtherFoo() {}
+
+      class MyInnerClass {
+        @CalledByNativeJavaTest("MyInnerClass")
+        public void testInnerFoo() {}
+      }
+    }
+    """
+    jni_params = jni_generator.JniParams('org/chromium/Foo')
+    jni_params.ExtractImportsAndInnerClasses(test_data)
+    called_by_natives = jni_generator.ExtractCalledByNatives(
+        jni_params, test_data, always_mangle=False)
+    golden_called_by_natives = [
+        CalledByNative(
+            return_type='MyOuterClass',
+            system_class=False,
+            static=False,
+            name='Constructor',
+            method_id_var_name='Constructor',
+            java_class_name='',
+            params=[],
+            env_call=('Void', ''),
+            unchecked=False,
+            gen_test_method=False,
+            is_constructor=True,
+        ),
+        CalledByNative(
+            return_type='int',
+            system_class=False,
+            static=False,
+            name='testFoo',
+            method_id_var_name='testFoo',
+            java_class_name='',
+            params=[],
+            env_call=('Void', ''),
+            unchecked=False,
+            gen_test_method=True,
+        ),
+        CalledByNative(
+            return_type='void',
+            system_class=False,
+            static=False,
+            name='testOtherFoo',
+            method_id_var_name='testOtherFoo',
+            java_class_name='',
+            params=[],
+            env_call=('Void', ''),
+            unchecked=False,
+            gen_test_method=True,
+        ),
+        CalledByNative(
+            return_type='void',
+            system_class=False,
+            static=False,
+            name='testInnerFoo',
+            method_id_var_name='testInnerFoo',
+            java_class_name='MyInnerClass',
+            params=[],
+            env_call=('Void', ''),
+            unchecked=False,
+            gen_test_method=True,
+        )
+    ]
+    self.AssertListEquals(golden_called_by_natives, called_by_natives)
+    h = jni_generator.InlHeaderFileGenerator('', 'org/chromium/TestJni', [],
+                                             called_by_natives, [], jni_params,
+                                             TestOptions())
+    self.AssertGoldenTextEquals(h.GetContent())
+
   def testFullyQualifiedClassName(self):
     contents = """
 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
diff --git a/base/task/lazy_task_runner.h b/base/task/lazy_task_runner.h
index b507118..20c44b3 100644
--- a/base/task/lazy_task_runner.h
+++ b/base/task/lazy_task_runner.h
@@ -22,12 +22,17 @@
 //
 // Lazy(Sequenced|SingleThread|COMSTA)TaskRunner is meant to be instantiated in
 // an anonymous namespace (no static initializer is generated) and used to post
-// tasks to the same sequence/thread from pieces of code that don't have a
-// better way of sharing a TaskRunner. It is important to use this class
-// instead of a self-managed global variable or LazyInstance so that the
-// TaskRunners do not outlive the scope of the TaskEnvironment in unit
-// tests (otherwise the next test in the same process will die in use-after-
-// frees).
+// tasks to the same thread-pool-bound sequence/thread from pieces of code that
+// don't have a better way of sharing a TaskRunner. It is important to use this
+// class instead of a self-managed global variable or LazyInstance so that the
+// TaskRunners do not outlive the scope of the TaskEnvironment in unit tests
+// (otherwise the next test in the same process will die in use-after-frees).
+//
+// Note: This is only meant for base::ThreadPool bound task runners. Task
+// runners bound to other destination which share an existing sequence (like
+// BrowserThreads) should just use the appropriate getter each time (e.g.
+// base::Create*TaskRunner({BrowserThread::UI})).
+// TODO(1026641): Rename this API to LazyThreadPoolTaskRunner.
 //
 // IMPORTANT: Only use this API as a last resort. Prefer storing a
 // (Sequenced|SingleThread)TaskRunner returned by
@@ -96,6 +101,7 @@
 // |traits| are TaskTraits used when creating the SequencedTaskRunner.
 #define LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(traits)                 \
   base::LazySequencedTaskRunner::CreateInternal(traits);               \
+  static_assert(traits.use_thread_pool(), "");                         \
   ALLOW_UNUSED_TYPE constexpr base::TaskTraits                         \
       LAZY_TASK_RUNNER_CONCATENATE_INTERNAL(kVerifyTraitsAreConstexpr, \
                                             __LINE__) = traits
@@ -105,6 +111,7 @@
 // thread with other SingleThreadTaskRunners.
 #define LAZY_SINGLE_THREAD_TASK_RUNNER_INITIALIZER(traits, thread_mode)   \
   base::LazySingleThreadTaskRunner::CreateInternal(traits, thread_mode);  \
+  static_assert(traits.use_thread_pool(), "");                            \
   ALLOW_UNUSED_TYPE constexpr base::TaskTraits                            \
       LAZY_TASK_RUNNER_CONCATENATE_INTERNAL(kVerifyTraitsAreConstexpr,    \
                                             __LINE__) = traits;           \
@@ -118,6 +125,7 @@
 // SingleThreadTaskRunners.
 #define LAZY_COM_STA_TASK_RUNNER_INITIALIZER(traits, thread_mode)         \
   base::LazyCOMSTATaskRunner::CreateInternal(traits, thread_mode);        \
+  static_assert(traits.use_thread_pool(), "");                            \
   ALLOW_UNUSED_TYPE constexpr base::TaskTraits                            \
       LAZY_TASK_RUNNER_CONCATENATE_INTERNAL(kVerifyTraitsAreConstexpr,    \
                                             __LINE__) = traits;           \
diff --git a/base/test/task_environment.cc b/base/test/task_environment.cc
index 9085791f..2f730e6 100644
--- a/base/test/task_environment.cc
+++ b/base/test/task_environment.cc
@@ -354,13 +354,11 @@
     MainThreadType main_thread_type,
     ThreadPoolExecutionMode thread_pool_execution_mode,
     ThreadingMode threading_mode,
-    ThreadPoolCOMEnvironment thread_pool_com_environment,
     bool subclass_creates_default_taskrunner,
     trait_helpers::NotATraitTag)
     : main_thread_type_(main_thread_type),
       thread_pool_execution_mode_(thread_pool_execution_mode),
       threading_mode_(threading_mode),
-      thread_pool_com_environment_(thread_pool_com_environment),
       subclass_creates_default_taskrunner_(subclass_creates_default_taskrunner),
       sequence_manager_(
           CreateSequenceManagerForMainThreadType(main_thread_type)),
@@ -436,10 +434,18 @@
   ThreadPoolInstance::InitParams init_params(kMaxThreads);
   init_params.suggested_reclaim_time = TimeDelta::Max();
 #if defined(OS_WIN)
-  if (thread_pool_com_environment_ == ThreadPoolCOMEnvironment::COM_MTA) {
-    init_params.common_thread_pool_environment =
-        ThreadPoolInstance::InitParams::CommonThreadPoolEnvironment::COM_MTA;
-  }
+  // Enable the MTA in unit tests to match the browser process's
+  // ThreadPoolInstance configuration.
+  //
+  // This has the adverse side-effect of enabling the MTA in non-browser unit
+  // tests as well but the downside there is not as bad as not having it in
+  // browser unit tests. It just means some COM asserts may pass in unit tests
+  // where they wouldn't in integration tests or prod. That's okay because unit
+  // tests are already generally very loose on allowing I/O, waits, etc. Such
+  // misuse will still be caught in later phases (and COM usage should already
+  // be pretty much inexistent in sandboxed processes).
+  init_params.common_thread_pool_environment =
+      ThreadPoolInstance::InitParams::CommonThreadPoolEnvironment::COM_MTA;
 #endif
 
   auto task_tracker = std::make_unique<TestTaskTracker>();
diff --git a/base/test/task_environment.h b/base/test/task_environment.h
index c8d1d71..01d19d9 100644
--- a/base/test/task_environment.h
+++ b/base/test/task_environment.h
@@ -156,28 +156,6 @@
     DEFAULT = MULTIPLE_THREADS
   };
 
-  // On Windows, sets the COM environment for the ThreadPoolInstance. Ignored
-  // on other platforms.
-  enum class ThreadPoolCOMEnvironment {
-    // Do not initialize COM for the pool's workers.
-    NONE,
-
-    // Place the pool's workers in a COM MTA.
-    COM_MTA,
-
-    // Enable the MTA by default in unit tests to match the browser process's
-    // ThreadPoolInstance configuration.
-    //
-    // This has the adverse side-effect of enabling the MTA in non-browser unit
-    // tests as well but the downside there is not as bad as not having it in
-    // browser unit tests. It just means some COM asserts may pass in unit
-    // tests where they wouldn't in integration tests or prod. That's okay
-    // because unit tests are already generally very loose on allowing I/O,
-    // waits, etc. Such misuse will still be caught in later phases (and COM
-    // usage should already be pretty much inexistent in sandboxed processes).
-    DEFAULT = COM_MTA,
-  };
-
   // List of traits that are valid inputs for the constructor below.
   struct ValidTraits {
     ValidTraits(TimeSource);
@@ -185,7 +163,6 @@
     ValidTraits(ThreadPoolExecutionMode);
     ValidTraits(SubclassCreatesDefaultTaskRunner);
     ValidTraits(ThreadingMode);
-    ValidTraits(ThreadPoolCOMEnvironment);
   };
 
   // Constructor accepts zero or more traits which customize the testing
@@ -203,9 +180,6 @@
                                    ThreadPoolExecutionMode::DEFAULT>(traits...),
             trait_helpers::GetEnum<ThreadingMode, ThreadingMode::DEFAULT>(
                 traits...),
-            trait_helpers::GetEnum<ThreadPoolCOMEnvironment,
-                                   ThreadPoolCOMEnvironment::DEFAULT>(
-                traits...),
             trait_helpers::HasTrait<SubclassCreatesDefaultTaskRunner,
                                     TaskEnvironmentTraits...>(),
             trait_helpers::NotATraitTag()) {}
@@ -355,14 +329,12 @@
                   MainThreadType main_thread_type,
                   ThreadPoolExecutionMode thread_pool_execution_mode,
                   ThreadingMode threading_mode,
-                  ThreadPoolCOMEnvironment thread_pool_com_environment,
                   bool subclass_creates_default_taskrunner,
                   trait_helpers::NotATraitTag tag);
 
   const MainThreadType main_thread_type_;
   const ThreadPoolExecutionMode thread_pool_execution_mode_;
   const ThreadingMode threading_mode_;
-  const ThreadPoolCOMEnvironment thread_pool_com_environment_;
   const bool subclass_creates_default_taskrunner_;
 
   std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_;
diff --git a/base/test/task_environment_unittest.cc b/base/test/task_environment_unittest.cc
index fea1b9c..e6a929e 100644
--- a/base/test/task_environment_unittest.cc
+++ b/base/test/task_environment_unittest.cc
@@ -44,10 +44,6 @@
 #include "base/files/file_descriptor_watcher_posix.h"
 #endif  // defined(OS_POSIX)
 
-#if defined(OS_WIN)
-#include "base/win/scoped_com_initializer.h"
-#endif
-
 namespace base {
 namespace test {
 
@@ -1203,58 +1199,5 @@
   run_loop.Run();
 }
 
-#if defined(OS_WIN)
-namespace {
-
-enum class ApartmentType {
-  kSTA,
-  kMTA,
-};
-
-void InitializeSTAApartment() {
-  base::win::ScopedCOMInitializer initializer;
-  EXPECT_TRUE(initializer.Succeeded());
-}
-
-void InitializeMTAApartment() {
-  base::win::ScopedCOMInitializer initializer(
-      base::win::ScopedCOMInitializer::kMTA);
-  EXPECT_TRUE(initializer.Succeeded());
-}
-
-void InitializeCOMOnWorker(
-    TaskEnvironment::ThreadPoolCOMEnvironment com_environment,
-    ApartmentType apartment_type) {
-  TaskEnvironment task_environment(com_environment);
-  PostTask(FROM_HERE, BindOnce(apartment_type == ApartmentType::kSTA
-                                   ? &InitializeSTAApartment
-                                   : &InitializeMTAApartment));
-  task_environment.RunUntilIdle();
-}
-
-}  // namespace
-
-TEST_F(TaskEnvironmentTest, DefaultCOMEnvironment) {
-  // Attempt to initialize an MTA COM apartment. Expect this to succeed since
-  // the thread is already in an MTA apartment.
-  InitializeCOMOnWorker(TaskEnvironment::ThreadPoolCOMEnvironment::DEFAULT,
-                        ApartmentType::kMTA);
-
-  // Attempt to initialize an STA COM apartment. Expect this to fail since the
-  // thread is already in an MTA apartment.
-  EXPECT_CHECK_DEATH(InitializeCOMOnWorker(
-      TaskEnvironment::ThreadPoolCOMEnvironment::DEFAULT, ApartmentType::kSTA));
-}
-
-TEST_F(TaskEnvironmentTest, NoCOMEnvironment) {
-  // Attempt to initialize both MTA and STA COM apartments. Both should succeed
-  // when the thread is not already in an apartment.
-  InitializeCOMOnWorker(TaskEnvironment::ThreadPoolCOMEnvironment::NONE,
-                        ApartmentType::kMTA);
-  InitializeCOMOnWorker(TaskEnvironment::ThreadPoolCOMEnvironment::NONE,
-                        ApartmentType::kSTA);
-}
-#endif
-
 }  // namespace test
 }  // namespace base
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 2d2ffd4..366bb01 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -197,13 +197,11 @@
         @trace_event.traced
         def install_helper_internal(d, apk_path=None):
           # pylint: disable=unused-argument
-          logging.info('Start Installing %s', apk.path)
           d.Install(
               apk,
               modules=modules,
               fake_modules=fake_modules,
               permissions=permissions)
-          logging.info('Finished Installing %s', apk.path)
 
         return install_helper_internal
 
@@ -212,10 +210,7 @@
         @trace_event.traced
         def incremental_install_helper_internal(d, apk_path=None):
           # pylint: disable=unused-argument
-          logging.info('Start Incremental Installing %s', apk.path)
           installer.Install(d, json_path, apk=apk, permissions=permissions)
-          logging.info('Finished Incremental Installing %s', apk.path)
-
         return incremental_install_helper_internal
 
       permissions = self._test_instance.test_apk.GetPermissions()
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 304629f..d9253fb 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -661,6 +661,17 @@
       }
     }
 
+    # This is done in Android case to keep binary size under linker limit for
+    # 32bit, especially since when ThinLTO is enabled Chrome Android build
+    # cannot yet support DWARF Fission. This workaround was copied from
+    # crosbug/1032159
+    if (is_android) {
+      ldflags += [
+        "-Wl,-mllvm",
+        "-Wl,-generate-type-units",
+      ]
+    }
+
     # Work-around for http://openradar.appspot.com/20356002
     if (is_mac) {
       ldflags += [ "-Wl,-all_load" ]
@@ -2321,6 +2332,18 @@
       # version 7 also produces debug data that is incompatible with Breakpad
       # dump_syms, so this is still required (https://crbug.com/622406).
       cflags += [ "-fno-standalone-debug" ]
+    }
+    if (!use_debug_fission && current_cpu == "arm") {
+      # dump_syms has issues with dwarf4 on arm, https://crbug.com/744956
+      # TODO(thakis): Remove this again once dump_syms is fixed.
+      #
+      # debug fission needs DWARF DIEs to be emitted at version 4.
+      # Chrome OS emits Debug Frame in DWARF2's .debug_frame v1 to make breakpad
+      # happy [1].
+      # Unless Android needs debug fission, DWARF3 is the simplest solution.
+      #
+      # [1] crrev.com/a81d5ade0b043208e06ad71a38bcf9c348a1a52f
+      cflags += [ "-gdwarf-3" ]
     } else if (is_mac) {
       # clang defaults to DWARF2 on macOS unless mac_deployment_target is
       # at least 10.11.
@@ -2338,22 +2361,17 @@
       cflags += [ "-g2" ]
     }
 
-    ldflags = []
-    if (use_debug_fission && !is_nacl) {
+    if (use_debug_fission && !is_nacl && !is_android) {
       # NOTE: Some Chrome OS builds globally set |use_debug_fission| to true,
-      # but they also build some targets whose toolchains aren't
+      # but they also build some targets against Android toolchains which aren't
       # compatible with it.
       #
       # TODO(https://crbug.com/837032): See if we can clean this up by e.g. not
       # setting use_debug_fission globally.
       cflags += [ "-gsplit-dwarf" ]
-
-      # NOTE: This flag is for use by the link wrapper scripts. They are also
-      # expected to remove it before sending the rest of ldflags to the actual
-      # linker.
-      ldflags += [ "--generate-dwp" ]
     }
     asmflags = cflags
+    ldflags = []
 
     # TODO(thakis): Figure out if there's a way to make this go for 32-bit,
     # currently we get "warning:
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 6ac4598..2a11c00 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -225,8 +225,7 @@
 # If it wasn't manually set, set to an appropriate default.
 assert(symbol_level >= -1 && symbol_level <= 2, "Invalid symbol_level")
 if (symbol_level == -1) {
-  if (is_android && !is_component_build &&
-      !(use_debug_fission != "default" && use_debug_fission)) {
+  if (is_android && !is_component_build && !use_thin_lto) {
     # Reduce symbol level when it will cause invalid elf files to be created
     # (due to file size). https://crbug.com/648948.
     symbol_level = 1
@@ -279,25 +278,16 @@
 
 # Assert that the configuration isn't going to hit https://crbug.com/648948.
 # An exception is made when target_os == "chromeos" as we only use the Android
-# toolchain there to build relatively small binaries.
+# toolchain there to build relatively small binaries. There is a workaround in
+# place for builds using ThinLTO (through '-generate-type-units' flag), so
+# these are exempt from limitation.
 assert(ignore_elf32_limitations || !is_android || target_os == "chromeos" ||
-           is_component_build || symbol_level < 2 ||
-           (use_debug_fission != "default" && use_debug_fission),
+           is_component_build || use_thin_lto || symbol_level < 2,
        "Android 32-bit non-component builds cannot have symbol_level=2 " +
            "due to 4GiB file size limit, see https://crbug.com/648948. " +
            "If you really want to try this out, " +
            "set ignore_elf32_limitations=true.")
 
-# Assert that we aren't simultaneously expecting ThinLTO and DWARF Fission to
-# work. The way that ThinLTO is implemented in Android build will silently
-# disable DWARF Fission, causing build steps that expect Fission outputs to
-# break. This work should clear a path to having both enabled for Android:
-# https://crbug.com/877722
-assert(!is_android || !use_thin_lto || use_debug_fission == "default" ||
-           !use_debug_fission,
-       "Android builds do not yet support ThinLTO and DWARF Fission " +
-           "simultaneously.")
-
 # This variable is true if the current toolchain is either the default
 # toolchain, or, on Android, the secondary ABI toolchain. In other words, it's
 # a toolchain that generates targets for the product, as opposed to the host
diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni
index 5287ebf6..bb58aa9 100644
--- a/build/config/sanitizers/sanitizers.gni
+++ b/build/config/sanitizers/sanitizers.gni
@@ -155,6 +155,13 @@
       !use_clang_coverage &&
       (use_fuzzing_engine || sanitizer_coverage_flags != "")
 
+  # https://crbug.com/1002058: Code coverage works inside the sandbox via the
+  # help of several helper IPCs. Unfortunately, the sandbox-only path does not
+  # work well for fuzzing builds. Since fuzzing builds already disable the
+  # sandbox when dumping coverage, limit the sandbox-only path to non-fuzzing
+  # builds.
+  use_clang_coverage_inside_sandbox = use_clang_coverage && !use_fuzzing_engine
+
   # Detect overflow/underflow for global objects.
   #
   # Mac: http://crbug.com/352073
diff --git a/build/extract_partition.py b/build/extract_partition.py
index 7ee93cb..52607c8 100755
--- a/build/extract_partition.py
+++ b/build/extract_partition.py
@@ -30,7 +30,6 @@
       required=True,
       help='Stripped output file',
       metavar='FILE')
-  parser.add_argument('--dwp', help='Path to dwp binary', metavar='FILE')
   parser.add_argument('input', help='Input file')
   args = parser.parse_args()
 
@@ -47,13 +46,6 @@
   ]
   subprocess.check_call(objcopy_args)
 
-  if args.dwp:
-    dwp_args = [
-        args.dwp, '-e', args.unstripped_output, '-o',
-        args.stripped_output + '.dwp'
-    ]
-    subprocess.check_call(dwp_args)
-
 
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/build/partitioned_shared_library.gni b/build/partitioned_shared_library.gni
index 88479842..ca8a3c61 100644
--- a/build/partitioned_shared_library.gni
+++ b/build/partitioned_shared_library.gni
@@ -3,9 +3,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//build/config/android/config.gni")
 import("//build/config/clang/clang.gni")
-import("//build/config/compiler/compiler.gni")
 
 # This template creates a set of shared libraries, by linking a single
 # "partitioned" shared library, then splitting it into multiple pieces.
@@ -104,13 +102,6 @@
           "${invoker.partition}",
         ]
       }
-
-      if (use_debug_fission != "default" && use_debug_fission &&
-          symbol_level == 2) {
-        dwp = rebase_path("${android_tool_prefix}dwp", root_build_dir)
-        args += [ "--dwp=${dwp}" ]
-        outputs += [ invoker.stripped_output + ".dwp" ]
-      }
       args += [ rebase_path(sources[0], root_build_dir) ]
     }
   }
diff --git a/build/toolchain/android/BUILD.gn b/build/toolchain/android/BUILD.gn
index df1285e..be185f9f 100644
--- a/build/toolchain/android/BUILD.gn
+++ b/build/toolchain/android/BUILD.gn
@@ -29,8 +29,7 @@
     # Output linker map files for binary size analysis.
     enable_linker_map = true
 
-    _android_tool_prefix =
-        "$android_toolchain_root/bin/${invoker.binary_prefix}-"
+    _android_tool_prefix = "$android_toolchain_root/bin/${invoker.binary_prefix}-"
 
     # The tools should be run relative to the build dir.
     _tool_prefix = rebase_path("$_android_tool_prefix", root_build_dir)
@@ -42,7 +41,6 @@
     ld = cxx
     readelf = _tool_prefix + "readelf"
     nm = _tool_prefix + "nm"
-    dwp = _tool_prefix + "dwp"
     strip = rebase_path("//buildtools/third_party/eu-strip/bin/eu-strip",
                         root_build_dir)
     use_unstripped_as_runtime_outputs = android_unstripped_runtime_outputs
diff --git a/build/toolchain/gcc_link_wrapper.py b/build/toolchain/gcc_link_wrapper.py
index f73221e..8892f14b 100755
--- a/build/toolchain/gcc_link_wrapper.py
+++ b/build/toolchain/gcc_link_wrapper.py
@@ -45,7 +45,6 @@
                       help=('Use --Wl,-Map to generate a map file. Will be '
                             'gzipped if extension ends with .gz'),
                       metavar='FILE')
-  parser.add_argument('--dwp', help=('The dwp binary to run'), metavar='FILE')
   parser.add_argument('--output',
                       required=True,
                       help='Final output executable file',
@@ -54,10 +53,6 @@
                       help='Linking command')
   args = parser.parse_args()
 
-  generate_dwp = '--generate-dwp' in args.command
-  if generate_dwp:
-    args.command.remove('--generate-dwp')
-
   # Work-around for gold being slow-by-default. http://crbug.com/632230
   fast_env = dict(os.environ)
   fast_env['LC_ALL'] = 'C'
@@ -66,29 +61,12 @@
   if result != 0:
     return result
 
-  # If dwp is set, then package debug info for this exe.
-  dwp_proc = None
-  if generate_dwp:
-    if not args.dwp:
-      parser.error('--generate-dwp requireds --dwp')
-    exe_file = args.output
-    if args.unstripped_file:
-      exe_file = args.unstripped_file
-    dwp_proc = subprocess.Popen(
-        wrapper_utils.CommandToRun(
-            [args.dwp, '-e', exe_file, '-o', args.output + '.dwp']))
-
   # Finally, strip the linked executable (if desired).
   if args.strip:
     result = subprocess.call(CommandToRun([
         args.strip, '-o', args.output, args.unstripped_file
         ]))
 
-  if dwp_proc:
-    dwp_result = dwp_proc.wait()
-    if dwp_result != 0:
-      return dwp_result
-
   return result
 
 
diff --git a/build/toolchain/gcc_solink_wrapper.py b/build/toolchain/gcc_solink_wrapper.py
index e5f6d87..540b1eda 100755
--- a/build/toolchain/gcc_solink_wrapper.py
+++ b/build/toolchain/gcc_solink_wrapper.py
@@ -70,7 +70,6 @@
   parser.add_argument('--strip',
                       help='The strip binary to run',
                       metavar='PATH')
-  parser.add_argument('--dwp', help='The dwp binary to run', metavar='PATH')
   parser.add_argument('--sofile',
                       required=True,
                       help='Shared object file produced by linking command',
@@ -103,10 +102,6 @@
   if link_only:
     args.command.remove('--link-only')
 
-  generate_dwp = '--generate-dwp' in args.command
-  if generate_dwp:
-    args.command.remove('--generate-dwp')
-
   # First, run the actual link.
   command = wrapper_utils.CommandToRun(args.command)
   result = wrapper_utils.RunLinkWithOptionalMapFile(command, env=fast_env,
@@ -135,15 +130,6 @@
       pass
     return 0
 
-  # If dwp is set, then package debug info for this SO.
-  dwp_proc = None
-  if generate_dwp:
-    if not args.dwp:
-      parser.error('--generate-dwp requireds --dwp')
-    dwp_proc = subprocess.Popen(
-        wrapper_utils.CommandToRun(
-            [args.dwp, '-e', args.sofile, '-o', args.output + '.dwp']))
-
   # Next, generate the contents of the TOC file.
   result, toc = CollectTOC(args)
   if result != 0:
@@ -158,11 +144,6 @@
     result = subprocess.call(wrapper_utils.CommandToRun(
         [args.strip, '-o', args.output, args.sofile]))
 
-  if dwp_proc:
-    dwp_result = dwp_proc.wait()
-    if dwp_result != 0:
-      return dwp_result
-
   return result
 
 
diff --git a/build/toolchain/gcc_toolchain.gni b/build/toolchain/gcc_toolchain.gni
index b43c5d3..5673947 100644
--- a/build/toolchain/gcc_toolchain.gni
+++ b/build/toolchain/gcc_toolchain.gni
@@ -225,11 +225,6 @@
     } else {
       nm = "nm"
     }
-    if (defined(invoker.dwp)) {
-      dwp = invoker.dwp
-    } else {
-      dwp = "dwp"
-    }
 
     if (defined(invoker.shlib_extension)) {
       default_shlib_extension = invoker.shlib_extension
@@ -399,7 +394,7 @@
       # The host might not have a POSIX shell and utilities (e.g. Windows).
       solink_wrapper =
           rebase_path("//build/toolchain/gcc_solink_wrapper.py", root_build_dir)
-      command = "$python_path \"$solink_wrapper\" --readelf=\"$readelf\" --nm=\"$nm\" $strip_switch--dwp=\"${dwp}\" --sofile=\"$unstripped_sofile\" --tocfile=\"$tocfile\"$map_switch --output=\"$sofile\" -- $link_command"
+      command = "$python_path \"$solink_wrapper\" --readelf=\"$readelf\" --nm=\"$nm\" $strip_switch--sofile=\"$unstripped_sofile\" --tocfile=\"$tocfile\"$map_switch --output=\"$sofile\" -- $link_command"
 
       if (target_cpu == "mipsel" && is_component_build && is_android) {
         rspfile_content = "-Wl,--start-group -Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}} -Wl,--end-group"
@@ -531,9 +526,13 @@
         strip_switch = " --strip=\"${invoker.strip}\" --unstripped-file=\"$unstripped_outfile\""
       }
 
-      link_wrapper =
-          rebase_path("//build/toolchain/gcc_link_wrapper.py", root_build_dir)
-      command = "$python_path \"$link_wrapper\" --output=\"$outfile\"$strip_switch$map_switch --dwp=\"${dwp}\" -- $link_command"
+      if (strip_switch != "" || map_switch != "") {
+        link_wrapper =
+            rebase_path("//build/toolchain/gcc_link_wrapper.py", root_build_dir)
+        command = "$python_path \"$link_wrapper\" --output=\"$outfile\"$strip_switch$map_switch -- $link_command"
+      } else {
+        command = link_command
+      }
 
       description = "LINK $outfile"
       rspfile_content = "{{inputs}}"
@@ -597,7 +596,6 @@
     readelf = "${toolprefix}readelf"
     ar = "${prefix}/llvm-ar"
     nm = "${toolprefix}nm"
-    dwp = "${toolprefix}dwp"
 
     forward_variables_from(invoker,
                            [
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 8e9a06b18..cc801af 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -62,16 +62,16 @@
 
 struct CC_EXPORT InputHandlerScrollResult {
   InputHandlerScrollResult();
-  // Did any layer scroll as a result this ScrollBy call?
+  // Did any layer scroll as a result this ScrollUpdate call?
   bool did_scroll;
-  // Was any of the scroll delta argument to this ScrollBy call not used?
+  // Was any of the scroll delta argument to this ScrollUpdate call not used?
   bool did_overscroll_root;
-  // The total overscroll that has been accumulated by all ScrollBy calls that
-  // have had overscroll since the last ScrollBegin call. This resets upon a
-  // ScrollBy with no overscroll.
+  // The total overscroll that has been accumulated by all ScrollUpdate calls
+  // that have had overscroll since the last ScrollBegin call. This resets upon
+  // a ScrollUpdate with no overscroll.
   gfx::Vector2dF accumulated_root_overscroll;
-  // The amount of the scroll delta argument to this ScrollBy call that was not
-  // used for scrolling.
+  // The amount of the scroll delta argument to this ScrollUpdate call that was
+  // not used for scrolling.
   gfx::Vector2dF unused_scroll_delta;
   // How the browser should handle the overscroll navigation based on the css
   // property scroll-boundary-behavior.
@@ -159,11 +159,13 @@
   // handler calls WillShutdown() on the client.
   virtual void BindToClient(InputHandlerClient* client) = 0;
 
-  // Selects a layer to be scrolled using the |scroll_state| start position.
-  // Returns SCROLL_STARTED if the layer at the coordinates can be scrolled,
-  // SCROLL_ON_MAIN_THREAD if the scroll event should instead be delegated to
-  // the main thread, or SCROLL_IGNORED if there is nothing to be scrolled at
-  // the given coordinates.
+  // Selects a ScrollNode to be "latched" for scrolling using the
+  // |scroll_state| start position. The selected node remains latched until the
+  // gesture is ended by a call to ScrollEnd.  Returns SCROLL_STARTED if a node
+  // at the coordinates can be scrolled and was latched, SCROLL_ON_MAIN_THREAD
+  // if the scroll event should instead be delegated to the main thread, or
+  // SCROLL_IGNORED if there is nothing to be scrolled at the given
+  // coordinates.
   virtual ScrollStatus ScrollBegin(ScrollState* scroll_state,
                                    ScrollInputType type) = 0;
 
@@ -172,25 +174,23 @@
   virtual ScrollStatus RootScrollBegin(ScrollState* scroll_state,
                                        ScrollInputType type) = 0;
 
-  // |delayed_by| is the delay that is taken into account when determining
-  // the duration of the animation. TODO(bokan): Should eventually be merged
-  // into ScrollBy. https://crbug.com/1016229.
-  virtual void ScrollAnimated(const gfx::Point& viewport_point,
-                              const gfx::Vector2dF& scroll_delta,
-                              base::TimeDelta delayed_by) = 0;
-
   // Scroll the layer selected by |ScrollBegin| by given |scroll_state| delta.
   // Internally, the delta is transformed to local layer's coordinate space for
-  // scrolls gestures that are direct manipulation (e.g. touch). If there is no
-  // room to move the layer in the requested direction, its first ancestor layer
-  // that can be scrolled will be moved instead. The return value's |did_scroll|
-  // field is set to false if no layer can be moved in the requested direction
-  // at all, and set to true if any layer is moved. If the scroll delta hits the
-  // root layer, and the layer can no longer move, the root overscroll
+  // scrolls gestures that are direct manipulation (e.g. touch). If the
+  // viewport is latched, and it can no longer scroll, the root overscroll
   // accumulated within this ScrollBegin() scope is reported in the return
   // value's |accumulated_overscroll| field. Should only be called if
   // ScrollBegin() returned SCROLL_STARTED.
-  virtual InputHandlerScrollResult ScrollBy(ScrollState* scroll_state) = 0;
+  // |delayed_by| is the delay from the event that caused the scroll. This is
+  // taken into account when determining the duration of the animation if one
+  // is created.
+  virtual InputHandlerScrollResult ScrollUpdate(ScrollState* scroll_state,
+                                                ScrollInputType type,
+                                                base::TimeDelta delayed_by) = 0;
+
+  // Stop scrolling the selected layer. Should only be called if ScrollBegin()
+  // returned SCROLL_STARTED. Snap to a snap position if |should_snap| is true.
+  virtual void ScrollEnd(bool should_snap) = 0;
 
   virtual InputHandlerPointerResult MouseMoveAt(
       const gfx::Point& mouse_position) = 0;
@@ -202,10 +202,6 @@
       const gfx::PointF& mouse_position) = 0;
   virtual void MouseLeave() = 0;
 
-  // Stop scrolling the selected layer. Should only be called if ScrollBegin()
-  // returned SCROLL_STARTED. Snap to a snap position if |should_snap| is true.
-  virtual void ScrollEnd(bool should_snap) = 0;
-
   // Requests a callback to UpdateRootLayerStateForSynchronousInputHandler()
   // giving the current root scroll and page scale information.
   virtual void RequestUpdateForSynchronousInputHandler() = 0;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 7eb7eba3..8653610 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -4586,6 +4586,49 @@
   render_frame_metadata_observer_->BindToCurrentThread();
 }
 
+bool LayerTreeHostImpl::ShouldAnimateScroll(
+    const ScrollState& scroll_state,
+    InputHandler::ScrollInputType type) const {
+  if (!settings_.enable_smooth_scroll)
+    return false;
+
+  bool has_precise_scroll_deltas =
+      scroll_state.delta_granularity() ==
+      static_cast<double>(
+          ui::input_types::ScrollGranularity::kScrollByPrecisePixel);
+
+#if defined(OS_MACOSX)
+  // Mac does not smooth scroll wheel events (crbug.com/574283).
+  return type == InputHandler::SCROLLBAR ? !has_precise_scroll_deltas : false;
+#else
+  return !has_precise_scroll_deltas;
+#endif
+}
+
+InputHandlerScrollResult LayerTreeHostImpl::ScrollUpdate(
+    ScrollState* scroll_state,
+    InputHandler::ScrollInputType type,
+    base::TimeDelta delayed_by) {
+  DCHECK(scroll_state);
+
+  if (ShouldAnimateScroll(*scroll_state, type)) {
+    DCHECK(!scroll_state->is_in_inertial_phase());
+    gfx::Vector2dF scroll_delta(scroll_state->delta_x(),
+                                scroll_state->delta_y());
+    ScrollAnimated(gfx::Point(), scroll_delta, delayed_by);
+
+    // TODO(bokan): Always return |did_scroll| to preserve existing behavior
+    // where ScrollAnimated used to not return anything at all and then ACK as
+    // DID_HANDLE. Long term we should fill in the result with meaningful
+    // values.
+    InputHandlerScrollResult result;
+    result.did_scroll = true;
+    return result;
+  }
+
+  return ScrollBy(scroll_state);
+}
+
 InputHandlerScrollResult LayerTreeHostImpl::ScrollBy(
     ScrollState* scroll_state) {
   DCHECK(scroll_state);
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index a91f207b..437b384c 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -258,6 +258,13 @@
 
   LayerTreeHostImpl& operator=(const LayerTreeHostImpl&) = delete;
 
+  // TODO(bokan): Replace calls in unit tests to ScrollUpdate and make these
+  // private.
+  void ScrollAnimated(const gfx::Point& viewport_point,
+                      const gfx::Vector2dF& scroll_delta,
+                      base::TimeDelta delayed_by = base::TimeDelta());
+  InputHandlerScrollResult ScrollBy(ScrollState* scroll_state);
+
   // InputHandler implementation
   void BindToClient(InputHandlerClient* client) override;
   InputHandler::ScrollStatus ScrollBegin(
@@ -266,10 +273,10 @@
   InputHandler::ScrollStatus RootScrollBegin(
       ScrollState* scroll_state,
       InputHandler::ScrollInputType type) override;
-  void ScrollAnimated(const gfx::Point& viewport_point,
-                      const gfx::Vector2dF& scroll_delta,
-                      base::TimeDelta delayed_by = base::TimeDelta()) override;
-  InputHandlerScrollResult ScrollBy(ScrollState* scroll_state) override;
+  InputHandlerScrollResult ScrollUpdate(
+      ScrollState* scroll_state,
+      InputHandler::ScrollInputType type,
+      base::TimeDelta delayed_by = base::TimeDelta()) override;
   void RequestUpdateForSynchronousInputHandler() override;
   void SetSynchronousInputHandlerRootScrollOffset(
       const gfx::ScrollOffset& root_content_offset) override;
@@ -925,6 +932,9 @@
 
   void ScrollLatchedScroller(ScrollState* scroll_state);
 
+  bool ShouldAnimateScroll(const ScrollState& scroll_state,
+                           InputHandler::ScrollInputType type) const;
+
   bool AnimatePageScale(base::TimeTicks monotonic_time);
   bool AnimateScrollbars(base::TimeTicks monotonic_time);
   bool AnimateBrowserControls(base::TimeTicks monotonic_time);
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 66ebe3417..3a0655b 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -3872,6 +3872,124 @@
   EXPECT_EQ(gfx::ScrollOffset(10, 10), inner_scroll->MaxScrollOffset());
 }
 
+// Ensures scroll gestures coming from scrollbars cause animations in the
+// appropriate scenarios.
+TEST_F(LayerTreeHostImplTest, AnimatedGranularityCausesSmoothScroll) {
+  // Enable animated scrolling
+  LayerTreeSettings settings = DefaultSettings();
+  settings.enable_smooth_scroll = true;
+  CreateHostImpl(settings, CreateLayerTreeFrameSink());
+
+  gfx::Size viewport_size(300, 200);
+  gfx::Size content_size(1000, 1000);
+  SetupViewportLayersOuterScrolls(viewport_size, content_size);
+
+  gfx::Point position(295, 195);
+  gfx::Vector2dF offset(0, 50);
+
+// TODO(bokan): Unfortunately, Mac currently doesn't support smooth scrolling
+// wheel events. https://crbug.com/574283.
+#if defined(OS_MACOSX)
+  std::vector<InputHandler::ScrollInputType> types = {InputHandler::SCROLLBAR};
+#else
+  std::vector<InputHandler::ScrollInputType> types = {InputHandler::SCROLLBAR,
+                                                      InputHandler::WHEEL};
+#endif
+  for (auto type : types) {
+    auto begin_state = BeginState(position, offset, type);
+    begin_state->data()->set_current_native_scrolling_element(
+        host_impl_->OuterViewportScrollNode()->element_id);
+    begin_state->data()->delta_granularity =
+        static_cast<double>(ui::input_types::ScrollGranularity::kScrollByPixel);
+
+    auto update_state = UpdateState(position, offset, type);
+    update_state->data()->delta_granularity =
+        static_cast<double>(ui::input_types::ScrollGranularity::kScrollByPixel);
+
+    ASSERT_FALSE(GetImplAnimationHost()->IsImplOnlyScrollAnimating());
+
+    // Perform a scrollbar-like scroll (one injected by the
+    // ScrollbarController). It should cause an animation to be created.
+    {
+      host_impl_->ScrollBegin(begin_state.get(), type);
+      ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
+                host_impl_->OuterViewportScrollNode());
+
+      host_impl_->ScrollUpdate(update_state.get(), type);
+      EXPECT_TRUE(GetImplAnimationHost()->IsImplOnlyScrollAnimating());
+
+      host_impl_->ScrollEnd();
+    }
+
+    GetImplAnimationHost()->ScrollAnimationAbort();
+    ASSERT_FALSE(GetImplAnimationHost()->IsImplOnlyScrollAnimating());
+
+    // Perform a scrollbar-like scroll (one injected by the
+    // ScrollbarController). This time we change the granularity to precise (as
+    // if thumb-dragging). This should not cause an animation.
+    {
+      begin_state->data()->delta_granularity = static_cast<double>(
+          ui::input_types::ScrollGranularity::kScrollByPrecisePixel);
+      update_state->data()->delta_granularity = static_cast<double>(
+          ui::input_types::ScrollGranularity::kScrollByPrecisePixel);
+
+      host_impl_->ScrollBegin(begin_state.get(), type);
+      ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
+                host_impl_->OuterViewportScrollNode());
+
+      host_impl_->ScrollUpdate(update_state.get(), type);
+      EXPECT_FALSE(GetImplAnimationHost()->IsImplOnlyScrollAnimating());
+
+      host_impl_->ScrollEnd();
+    }
+  }
+}
+
+// Ensures scroll gestures coming from scrollbars don't cause animations if
+// smooth scrolling is disabled.
+TEST_F(LayerTreeHostImplTest, NonAnimatedGranularityCausesInstantScroll) {
+  // Disable animated scrolling
+  LayerTreeSettings settings = DefaultSettings();
+  settings.enable_smooth_scroll = false;
+  CreateHostImpl(settings, CreateLayerTreeFrameSink());
+
+  gfx::Size viewport_size(300, 200);
+  gfx::Size content_size(1000, 1000);
+  SetupViewportLayersOuterScrolls(viewport_size, content_size);
+
+  gfx::Point position(295, 195);
+  gfx::Vector2dF offset(0, 50);
+
+  std::vector<InputHandler::ScrollInputType> types = {InputHandler::SCROLLBAR,
+                                                      InputHandler::WHEEL};
+  for (auto type : types) {
+    auto begin_state = BeginState(position, offset, type);
+    begin_state->data()->set_current_native_scrolling_element(
+        host_impl_->OuterViewportScrollNode()->element_id);
+    begin_state->data()->delta_granularity =
+        static_cast<double>(ui::input_types::ScrollGranularity::kScrollByPixel);
+
+    auto update_state = UpdateState(position, offset, type);
+    update_state->data()->delta_granularity =
+        static_cast<double>(ui::input_types::ScrollGranularity::kScrollByPixel);
+
+    ASSERT_FALSE(GetImplAnimationHost()->IsImplOnlyScrollAnimating());
+
+    // Perform a scrollbar-like scroll (one injected by the
+    // ScrollbarController). It should cause an animation to be created.
+    {
+      host_impl_->ScrollBegin(begin_state.get(), type);
+      ASSERT_EQ(host_impl_->CurrentlyScrollingNode(),
+                host_impl_->OuterViewportScrollNode());
+
+      host_impl_->ScrollUpdate(update_state.get(), type);
+      EXPECT_FALSE(GetImplAnimationHost()->IsImplOnlyScrollAnimating());
+
+      host_impl_->ScrollEnd();
+    }
+  }
+}
+
 class LayerTreeHostImplOverridePhysicalTime : public LayerTreeHostImpl {
  public:
   LayerTreeHostImplOverridePhysicalTime(
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index 81e5875..161ce54 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -136,6 +136,13 @@
   // on the compositor thread.
   bool compositor_threaded_scrollbar_scrolling = false;
 
+  // Determines whether animated scrolling is supported. If true, and the
+  // incoming gesture scroll is of a type that would normally be animated (e.g.
+  // coarse granularity scrolls like those coming from an external mouse wheel),
+  // the scroll will be performed smoothly using the animation system rather
+  // than instantly.
+  bool enable_smooth_scroll = false;
+
   // Whether layer tree commits should be made directly to the active
   // tree on the impl thread. If |false| LayerTreeHostImpl creates a
   // pending layer tree and produces that as the 'sync tree' with
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 142d3fb..1ad3b178 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -184,14 +184,9 @@
       "$root_out_dir/resources.pak",
     ]
     if (is_linux || is_win) {
-      data += [
-        "$root_out_dir/chrome_100_percent.pak",
-        "$root_out_dir/locales/en-US.pak",
-        "$root_out_dir/locales/fr.pak",
+      data_deps = [
+        "//chrome:packed_resources",
       ]
-      if (enable_hidpi) {
-        data += [ "$root_out_dir/chrome_200_percent.pak" ]
-      }
     }
 
     data_deps = []
@@ -345,6 +340,9 @@
     # These files are used by the installer so we need a public dep.
     public_deps += [ ":packed_resources" ]
 
+    # The step's output are needed at runtime, so we also need a data_dep.
+    data_deps += [ ":packed_resources" ]
+
     # Only ChromeOS has precompiled Flash that needs to get copied to the output
     # directory. On other platforms, Flash is either component-updated only or
     # not supported at all.
@@ -1212,7 +1210,6 @@
       ":chrome_framework_resources",
       ":chrome_framework_services",
       ":keystone_registration_framework",
-      ":packed_resources",
       ":swiftshader_library",
       ":widevine_cdm_library",
       "//chrome/browser/resources/media/mei_preload:component_bundle",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 1f842ae..0a56dfd 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1374,7 +1374,6 @@
   "java/src/org/chromium/chrome/browser/settings/SeekBarPreference.java",
   "java/src/org/chromium/chrome/browser/settings/SettingsActivity.java",
   "java/src/org/chromium/chrome/browser/settings/SettingsLauncher.java",
-  "java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java",
   "java/src/org/chromium/chrome/browser/settings/TextAndButtonPreference.java",
   "java/src/org/chromium/chrome/browser/settings/about/AboutChromePreferenceOSVersion.java",
   "java/src/org/chromium/chrome/browser/settings/about/AboutChromeSettings.java",
diff --git a/chrome/android/chrome_public_apk_tmpl.gni b/chrome/android/chrome_public_apk_tmpl.gni
index b51e73e..1c2a03a8 100644
--- a/chrome/android/chrome_public_apk_tmpl.gni
+++ b/chrome/android/chrome_public_apk_tmpl.gni
@@ -86,7 +86,8 @@
     _add_unwind_tables = invoker.add_unwind_tables_in_apk
   } else {
     _needs_32bit_lib =
-        target_cpu == "arm" || (_is_monochrome && target_cpu == "arm64")
+        target_cpu == "arm" ||
+        (_is_monochrome && target_cpu == "arm64" && !_is_64_bit_browser)
     _add_unwind_tables =
         _needs_32bit_lib && _add_unwind_tables_in_chrome_32bit_apk &&
         ((android_64bit_target_cpu &&
diff --git a/chrome/android/features/vr/OWNERS b/chrome/android/features/vr/OWNERS
index 8ba1799..f35d259 100644
--- a/chrome/android/features/vr/OWNERS
+++ b/chrome/android/features/vr/OWNERS
@@ -1,6 +1,5 @@
 alcooper@chromium.org
 bialpio@chromium.org
-cjgrant@chromium.org
 mthiesse@chromium.org
 
 # TEAM: xr-dev@chromium.org
diff --git a/chrome/android/java/res/layout/start_top_toolbar.xml b/chrome/android/java/res/layout/start_top_toolbar.xml
index f2d2d80..d501dae 100644
--- a/chrome/android/java/res/layout/start_top_toolbar.xml
+++ b/chrome/android/java/res/layout/start_top_toolbar.xml
@@ -29,7 +29,10 @@
         android:layout_height="32dp"
         android:layout_centerHorizontal="true"
         android:layout_centerVertical="true"
+        android:paddingStart="8dp"
+        android:paddingEnd="8dp"
         android:scaleType="centerInside"
+        android:adjustViewBounds="true"
         app:srcCompat="@drawable/google_logo"
         android:visibility="gone"
         android:contentDescription="@null"/>
diff --git a/chrome/android/java/res/values/attrs.xml b/chrome/android/java/res/values/attrs.xml
index ba92f92e..a55d249 100644
--- a/chrome/android/java/res/values/attrs.xml
+++ b/chrome/android/java/res/values/attrs.xml
@@ -18,13 +18,6 @@
         <attr name="emptyErrorMessage" format="string" />
     </declare-styleable>
 
-    <declare-styleable name="SpinnerPreference">
-        <!-- Used to change the SpinnerPreference to display the TextView and the Spinner on the
-             same line. Devices with a smallest width of less than 360dp will still use two separate
-             lines. -->
-        <attr name="singleLine" format="boolean" />
-    </declare-styleable>
-
     <declare-styleable name="TileGridLayout">
         <attr name="minHorizontalSpacing" format="dimension"/>
     </declare-styleable>
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml
index 5661c76e..34c425e 100644
--- a/chrome/android/java/res/values/styles.xml
+++ b/chrome/android/java/res/values/styles.xml
@@ -246,12 +246,6 @@
     <style name="PreferenceSpinnerItem">
         <item name="android:textAppearance">@style/TextAppearance.PreferenceMediumText</item>
     </style>
-    <style name="PreferenceSpinnerUnderlineView">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">1dp</item>
-        <item name="android:layout_marginTop">2dp</item>
-        <item name="android:background">@color/modern_grey_600</item>
-    </style>
     <style name="TextAppearance.AccessibilityTextPreference">
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 4583c6e..9e091293 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -849,7 +849,7 @@
     }
 
     @Override
-    public int getBaseStatusBarColor(boolean activityHasTab) {
+    public int getBaseStatusBarColor(Tab tab) {
         return StatusBarColorController.UNDEFINED_STATUS_BAR_COLOR;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TrustedWebActivityCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TrustedWebActivityCoordinator.java
index 9159f1e..43dbc9b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TrustedWebActivityCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/trustedwebactivityui/TrustedWebActivityCoordinator.java
@@ -14,11 +14,11 @@
 import org.chromium.chrome.browser.browserservices.TrustedWebActivityUmaRecorder;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.ClientPackageNameProvider;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.CurrentPageVerifier;
+import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.CurrentPageVerifier.VerificationStatus;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityBrowserControlsVisibilityManager;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityDisclosureController;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityOpenTimeRecorder;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TwaRegistrar;
-import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.CurrentPageVerifier.VerificationStatus;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.Verifier;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.splashscreen.TwaSplashController;
 import org.chromium.chrome.browser.browserservices.trustedwebactivityui.view.TrustedWebActivityDisclosureView;
@@ -27,6 +27,7 @@
 import org.chromium.chrome.browser.customtabs.ExternalIntentsPolicyProvider;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController;
 import org.chromium.chrome.browser.customtabs.features.ImmersiveModeController;
+import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarColorController;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
 import org.chromium.chrome.browser.lifecycle.InflationObserver;
@@ -45,6 +46,7 @@
 
     private final CurrentPageVerifier mCurrentPageVerifier;
     private TrustedWebActivityBrowserControlsVisibilityManager mBrowserControlsVisibilityManager;
+    private final CustomTabToolbarColorController mToolbarColorController;
     private final CustomTabStatusBarColorProvider mStatusBarColorProvider;
     private final Lazy<ImmersiveModeController> mImmersiveModeController;
     private final TwaRegistrar mTwaRegistrar;
@@ -67,6 +69,7 @@
             Lazy<TwaSplashController> splashController,
             BrowserServicesIntentDataProvider intentDataProvider,
             TrustedWebActivityUmaRecorder umaRecorder,
+            CustomTabToolbarColorController toolbarColorController,
             CustomTabStatusBarColorProvider statusBarColorProvider,
             ActivityLifecycleDispatcher lifecycleDispatcher,
             TrustedWebActivityBrowserControlsVisibilityManager browserControlsVisibilityManager,
@@ -78,6 +81,7 @@
         // so they start working.
         mCurrentPageVerifier = currentPageVerifier;
         mBrowserControlsVisibilityManager = browserControlsVisibilityManager;
+        mToolbarColorController = toolbarColorController;
         mStatusBarColorProvider = statusBarColorProvider;
         mImmersiveModeController = immersiveModeController;
         mTwaRegistrar = twaRegistrar;
@@ -145,6 +149,7 @@
 
     private void updateUi(boolean inTwaMode) {
         updateImmersiveMode(inTwaMode);
+        mToolbarColorController.setUseTabThemeColor(inTwaMode);
         mStatusBarColorProvider.setUseTabThemeColor(inTwaMode);
         mBrowserControlsVisibilityManager.updateIsInTwaMode(inTwaMode);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
index b4b923a7..0a2756f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.compositor;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -30,6 +31,7 @@
 
 import androidx.annotation.Nullable;
 
+import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.SysUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.compat.ApiHelperForN;
@@ -961,9 +963,10 @@
     }
 
     @Override
-    public int getBrowserControlsBackgroundColor() {
-        return mTabVisible == null ? Color.WHITE
-                                   : ToolbarColors.getToolbarSceneLayerBackground(mTabVisible);
+    public int getBrowserControlsBackgroundColor(Resources res) {
+        return mTabVisible == null
+                ? ApiCompatibilityUtils.getColor(res, R.color.toolbar_background_primary)
+                : ToolbarColors.getToolbarSceneLayerBackground(mTabVisible);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java
index 00c3e75..45b6498 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutRenderHost.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.compositor.layouts;
 
+import android.content.res.Resources;
 import android.graphics.Rect;
 
 import org.chromium.ui.resources.ResourceManager;
@@ -54,7 +55,7 @@
     /**
      * @return The background color of the toolbar.
      */
-    int getBrowserControlsBackgroundColor();
+    int getBrowserControlsBackgroundColor(Resources resources);
 
     /**
      * @return The {@link ResourceManager}.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
index a091b6c2..d5fa6a3e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/ToolbarSceneLayer.java
@@ -164,7 +164,7 @@
         // compositor.
         float alpha = 1;
 
-        update(mRenderHost.getBrowserControlsBackgroundColor(), alpha,
+        update(mRenderHost.getBrowserControlsBackgroundColor(mContext.getResources()), alpha,
                 mLayoutProvider.getFullscreenManager(), resourceManager,
                 forceHideBrowserControlsAndroidView, viewportMode,
                 DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext), viewport.height());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index abc4e2a9..8d5edc7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -19,6 +19,7 @@
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.customtabs.content.TabCreationMode;
 import org.chromium.chrome.browser.customtabs.dependency_injection.BaseCustomTabActivityComponent;
+import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarColorController;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarCoordinator;
 import org.chromium.chrome.browser.dependency_injection.ChromeActivityComponent;
 import org.chromium.chrome.browser.tab.Tab;
@@ -39,6 +40,7 @@
     protected CustomTabToolbarCoordinator mToolbarCoordinator;
     protected CustomTabActivityNavigationController mNavigationController;
     protected CustomTabActivityTabProvider mTabProvider;
+    protected CustomTabToolbarColorController mToolbarColorController;
     protected CustomTabStatusBarColorProvider mStatusBarColorProvider;
     protected CustomTabActivityTabFactory mTabFactory;
 
@@ -67,6 +69,7 @@
         mToolbarCoordinator = component.resolveToolbarCoordinator();
         mNavigationController = component.resolveNavigationController();
         mTabProvider = component.resolveTabProvider();
+        mToolbarColorController = component.resolveToolbarColorController();
         mStatusBarColorProvider = component.resolveCustomTabStatusBarColorProvider();
         mTabFactory = component.resolveTabFactory();
 
@@ -151,9 +154,8 @@
     }
 
     @Override
-    public int getBaseStatusBarColor(boolean activityHasTab) {
-        return mStatusBarColorProvider.getBaseStatusBarColor(
-                activityHasTab, super.getBaseStatusBarColor(activityHasTab));
+    public int getBaseStatusBarColor(Tab tab) {
+        return mStatusBarColorProvider.getBaseStatusBarColor(tab, super.getBaseStatusBarColor(tab));
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 03d8074..bc534aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -449,7 +449,6 @@
         });
         mCustomTabIntentHandler = component.resolveIntentHandler();
         component.resolveSessionHandler();
-        component.resolveToolbarColorController();
         component.resolveCustomTabIncognitoManager();
 
         if (mIntentDataProvider.isTrustedWebActivity()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProvider.java
index 94e5fbb..a868bdfe 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProvider.java
@@ -8,10 +8,11 @@
 import static org.chromium.chrome.browser.ui.system.StatusBarColorController.UNDEFINED_STATUS_BAR_COLOR;
 
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
+import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarColorController;
+import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarColorController.ToolbarColorType;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.system.StatusBarColorController;
-import org.chromium.chrome.browser.webapps.WebDisplayMode;
-import org.chromium.chrome.browser.webapps.WebappExtras;
 
 import javax.inject.Inject;
 
@@ -43,25 +44,20 @@
         mStatusBarColorController.updateStatusBarColor();
     }
 
-    int getBaseStatusBarColor(boolean activityHasTab, int fallbackStatusBarColor) {
+    int getBaseStatusBarColor(Tab tab, int fallbackStatusBarColor) {
         if (mIntentDataProvider.isOpenedByChrome()) return fallbackStatusBarColor;
 
-        if (shouldUseDefaultThemeColorForFullscreen()) {
-            return DEFAULT_STATUS_BAR_COLOR;
+        @ToolbarColorType
+        int toolbarColorType = CustomTabToolbarColorController.computeToolbarColorType(
+                mIntentDataProvider, mUseTabThemeColor, tab);
+        switch (toolbarColorType) {
+            case ToolbarColorType.THEME_COLOR:
+                return UNDEFINED_STATUS_BAR_COLOR;
+            case ToolbarColorType.DEFAULT_COLOR:
+                return DEFAULT_STATUS_BAR_COLOR;
+            case ToolbarColorType.INTENT_TOOLBAR_COLOR:
+                return mIntentDataProvider.getToolbarColor();
         }
-
-        if (activityHasTab && mUseTabThemeColor) return UNDEFINED_STATUS_BAR_COLOR;
-
-        return mIntentDataProvider.hasCustomToolbarColor() ? mIntentDataProvider.getToolbarColor()
-                                                           : DEFAULT_STATUS_BAR_COLOR;
-    }
-
-    private boolean shouldUseDefaultThemeColorForFullscreen() {
-        // Don't use the theme color provided by the page if we're in display: fullscreen. This
-        // works around an issue where the status bars go transparent and can't be seen on top of
-        // the page content when users swipe them in or they appear because the on-screen keyboard
-        // was triggered.
-        WebappExtras webappExtras = mIntentDataProvider.getWebappExtras();
-        return (webappExtras != null && webappExtras.displayMode == WebDisplayMode.FULLSCREEN);
+        return DEFAULT_STATUS_BAR_COLOR;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityComponent.java
index 4e4a30ff..9863357 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityComponent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/BaseCustomTabActivityComponent.java
@@ -12,6 +12,7 @@
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabFactory;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
 import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar;
+import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarColorController;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarCoordinator;
 import org.chromium.chrome.browser.dependency_injection.ChromeActivityComponent;
 import org.chromium.chrome.browser.webapps.SplashController;
@@ -27,6 +28,7 @@
     CustomTabCompositorContentInitializer resolveCompositorContentInitializer();
     CustomTabDelegateFactory resolveTabDelegateFactory();
     CustomTabStatusBarColorProvider resolveCustomTabStatusBarColorProvider();
+    CustomTabToolbarColorController resolveToolbarColorController();
     CustomTabTaskDescriptionHelper resolveTaskDescriptionHelper();
     CustomTabToolbarCoordinator resolveToolbarCoordinator();
     SplashController resolveSplashController();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java
index 4205217..c4e9657 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/dependency_injection/CustomTabActivityComponent.java
@@ -19,7 +19,6 @@
 import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleCoordinator;
 import org.chromium.chrome.browser.customtabs.dynamicmodule.DynamicModuleToolbarController;
 import org.chromium.chrome.browser.customtabs.features.ImmersiveModeController;
-import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarColorController;
 import org.chromium.chrome.browser.dependency_injection.ActivityScope;
 import org.chromium.chrome.browser.dependency_injection.ChromeActivityCommonsModule;
 
@@ -41,7 +40,6 @@
     CustomTabActivityLifecycleUmaTracker resolveUmaTracker();
     CustomTabIntentHandler resolveIntentHandler();
     CustomTabIncognitoManager resolveCustomTabIncognitoManager();
-    CustomTabToolbarColorController resolveToolbarColorController();
     CustomTabUmaRecorder resolveCustomTabUmaRecorder();
     CustomTabSessionHandler resolveSessionHandler();
     CustomTabActivityClientConnectionKeeper resolveConnectionKeeper();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java
index 661257c..bda29f94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarColorController.java
@@ -4,7 +4,9 @@
 
 package org.chromium.chrome.browser.customtabs.features.toolbar;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
@@ -14,9 +16,16 @@
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabImpl;
+import org.chromium.chrome.browser.tab.TabSelectionType;
+import org.chromium.chrome.browser.tab.TabThemeColorHelper;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
+import org.chromium.chrome.browser.webapps.WebDisplayMode;
+import org.chromium.chrome.browser.webapps.WebappExtras;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 import javax.inject.Inject;
 
 /**
@@ -24,26 +33,31 @@
  */
 @ActivityScope
 public class CustomTabToolbarColorController {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ToolbarColorType.THEME_COLOR, ToolbarColorType.DEFAULT_COLOR,
+            ToolbarColorType.INTENT_TOOLBAR_COLOR})
+    public @interface ToolbarColorType {
+        int THEME_COLOR = 0;
+        int DEFAULT_COLOR = 1;
+        // BrowserServicesIntentDataProvider#getToolbarColor() should be used.
+        int INTENT_TOOLBAR_COLOR = 2;
+    }
+
     private final BrowserServicesIntentDataProvider mIntentDataProvider;
     private final ChromeActivity mActivity;
     private final TabObserverRegistrar mTabObserverRegistrar;
     private final CustomTabActivityTabProvider mTabProvider;
 
-    /** Keeps track of the original color before the preview was shown. */
-    private int mOriginalColor;
-
-    /** True if a change to the toolbar color was made because of a preview. */
-    private boolean mTriggeredPreviewChange;
-
     private final CustomTabActivityTabProvider.Observer mActivityTabObserver =
             new CustomTabActivityTabProvider.Observer() {
                 @Override
                 public void onTabSwapped(@NonNull Tab tab) {
-                    updateColor(tab);
+                    updateColor();
                 }
             };
 
     private ToolbarManager mToolbarManager;
+    private boolean mUseTabThemeColor;
 
     @Inject
     public CustomTabToolbarColorController(BrowserServicesIntentDataProvider intentDataProvider,
@@ -56,6 +70,30 @@
     }
 
     /**
+     * Computes the toolbar color type.
+     * Returns a 'type' instead of a color so that the function can be used by non-toolbar UI
+     * surfaces with different values for {@link ToolbarColorType.DEFAULT_COLOR}.
+     */
+    public static int computeToolbarColorType(BrowserServicesIntentDataProvider intentDataProvider,
+            boolean useTabThemeColor, @Nullable Tab tab) {
+        if (intentDataProvider.isOpenedByChrome()) {
+            return (tab == null) ? ToolbarColorType.DEFAULT_COLOR : ToolbarColorType.THEME_COLOR;
+        }
+
+        if (shouldUseDefaultThemeColorForFullscreen(intentDataProvider)
+                || (tab != null && ((TabImpl) tab).isPreview())) {
+            return ToolbarColorType.DEFAULT_COLOR;
+        }
+
+        if (tab != null && useTabThemeColor) {
+            return ToolbarColorType.THEME_COLOR;
+        }
+
+        return intentDataProvider.hasCustomToolbarColor() ? ToolbarColorType.INTENT_TOOLBAR_COLOR
+                                                          : ToolbarColorType.DEFAULT_COLOR;
+    }
+
+    /**
      * Notifies the ColorController that the ToolbarManager has been created and is ready for
      * use. ToolbarManager isn't passed directly to the constructor because it's not guaranteed to
      * be initialized yet.
@@ -64,16 +102,10 @@
         mToolbarManager = manager;
         assert manager != null : "Toolbar manager not initialized";
 
-        // Logic isn't shared with WebappActivity yet.
-        if (mIntentDataProvider.getWebappExtras() != null) return;
-
-        int toolbarColor = mIntentDataProvider.getToolbarColor();
-        manager.onThemeColorChanged(toolbarColor, false);
-        if (!mIntentDataProvider.isOpenedByChrome()) {
-            manager.setShouldUpdateToolbarPrimaryColor(false);
-        }
         observeTabToUpdateColor();
         mTabProvider.addObserver(mActivityTabObserver);
+
+        updateColor();
     }
 
     private void observeTabToUpdateColor() {
@@ -81,44 +113,77 @@
             @Override
             public void onPageLoadFinished(Tab tab, String url) {
                 // Update the color when the page load finishes.
-                updateColor(tab);
+                updateColor();
             }
 
             @Override
             public void onUrlUpdated(Tab tab) {
                 // Update the color on every new URL.
-                updateColor(tab);
+                updateColor();
+            }
+
+            @Override
+            public void onDidChangeThemeColor(Tab tab, int color) {
+                updateColor();
+            }
+
+            @Override
+            public void onShown(Tab tab, @TabSelectionType int type) {
+                updateColor();
             }
         });
     }
 
     /**
-     * Updates the color of the Activity's CCT Toolbar. When a preview is shown, it should
-     * be reset to the default color. If the user later navigates away from that preview to
-     * a non-preview page, reset the color back to the original. This does not interfere
-     * with site-specific theme colors which are disabled when a preview is being shown.
+     * Sets whether the tab's theme color should be used for the toolbar and triggers an update of
+     * the toolbar color if needed.
      */
-    private void updateColor(Tab tab) {
-        ToolbarManager manager = mToolbarManager;
+    public void setUseTabThemeColor(boolean useTabThemeColor) {
+        if (mUseTabThemeColor == useTabThemeColor) return;
 
-        // Record the original toolbar color in case we need to revert back to it later
-        // after a preview has been shown then the user navigates to another non-preview
-        // page.
-        if (mOriginalColor == 0) mOriginalColor = manager.getPrimaryColor();
+        mUseTabThemeColor = useTabThemeColor;
+        updateColor();
+    }
 
-        final boolean shouldUpdateOriginal = manager.getShouldUpdateToolbarPrimaryColor();
-        manager.setShouldUpdateToolbarPrimaryColor(true);
+    /**
+     * Updates the color of the Activity's CCT Toolbar.
+     */
+    private void updateColor() {
+        if (mToolbarManager == null) return;
 
-        if (((TabImpl) tab).isPreview()) {
-            final int defaultColor =
-                    ChromeColors.getDefaultThemeColor(mActivity.getResources(), false);
-            manager.onThemeColorChanged(defaultColor, false);
-            mTriggeredPreviewChange = true;
-        } else if (mOriginalColor != manager.getPrimaryColor() && mTriggeredPreviewChange) {
-            manager.onThemeColorChanged(mOriginalColor, false);
-            mTriggeredPreviewChange = false;
-            mOriginalColor = 0;
+        mToolbarManager.setShouldUpdateToolbarPrimaryColor(true);
+        mToolbarManager.onThemeColorChanged(computeColor(), false);
+        mToolbarManager.setShouldUpdateToolbarPrimaryColor(false);
+    }
+
+    private int computeColor() {
+        Tab tab = mTabProvider.getTab();
+        @ToolbarColorType
+        int toolbarColorType = computeToolbarColorType(mIntentDataProvider, mUseTabThemeColor, tab);
+        switch (toolbarColorType) {
+            case ToolbarColorType.THEME_COLOR:
+                assert tab != null;
+                return TabThemeColorHelper.getColor(tab);
+            case ToolbarColorType.DEFAULT_COLOR:
+                return getDefaultColor();
+            case ToolbarColorType.INTENT_TOOLBAR_COLOR:
+                return mIntentDataProvider.getToolbarColor();
         }
-        manager.setShouldUpdateToolbarPrimaryColor(shouldUpdateOriginal);
+        return getDefaultColor();
+    }
+
+    private int getDefaultColor() {
+        return ChromeColors.getDefaultThemeColor(
+                mActivity.getResources(), mIntentDataProvider.isIncognito());
+    }
+
+    private static boolean shouldUseDefaultThemeColorForFullscreen(
+            BrowserServicesIntentDataProvider intentDataProvider) {
+        // Don't use the theme color provided by the page if we're in display: fullscreen. This
+        // works around an issue where the status bars go transparent and can't be seen on top of
+        // the page content when users swipe them in or they appear because the on-screen keyboard
+        // was triggered.
+        WebappExtras webappExtras = intentDataProvider.getWebappExtras();
+        return (webappExtras != null && webappExtras.displayMode == WebDisplayMode.FULLSCREEN);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
index 344c8a3..5d063c7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/explore_sites/ExploreSitesPage.java
@@ -171,11 +171,11 @@
     /**
      * Create a new instance of the explore sites page.
      */
-    public ExploreSitesPage(ChromeActivity activity, NativePageHost host) {
+    public ExploreSitesPage(ChromeActivity activity, NativePageHost host, TabImpl tab) {
         super(host);
 
         mHost = host;
-        mTab = activity.getActivityTab();
+        mTab = tab;
 
         mTitle = activity.getString(R.string.explore_sites_title);
         mView = (HistoryNavigationLayout) activity.getLayoutInflater().inflate(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
index e86229a..d11a5e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/native_page/NativePageFactory.java
@@ -78,7 +78,7 @@
 
         protected NativePage buildExploreSitesPage(ChromeActivity activity, Tab tab) {
             return new ExploreSitesPage(
-                    activity, new TabShim(tab, activity.getFullscreenManager()));
+                    activity, new TabShim(tab, activity.getFullscreenManager()), (TabImpl) tab);
         }
 
         protected NativePage buildHistoryPage(ChromeActivity activity, Tab tab) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index dce31f8..fd7a7f5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -1646,14 +1646,6 @@
     }
 
     /**
-     * @return Whether we should be updating the toolbar primary color based on updates from the
-     * Tab.
-     */
-    public boolean getShouldUpdateToolbarPrimaryColor() {
-        return mShouldUpdateToolbarPrimaryColor;
-    }
-
-    /**
      * @return The primary toolbar color.
      */
     public int getPrimaryColor() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarView.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarView.java
index 601dc9e..3caf6563 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/StartSurfaceToolbarView.java
@@ -6,6 +6,7 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.graphics.Rect;
 import android.support.v7.content.res.AppCompatResources;
 import android.util.AttributeSet;
 import android.view.View;
@@ -30,6 +31,9 @@
     private ColorStateList mLightIconTint;
     private ColorStateList mDarkIconTint;
 
+    private Rect mLogoRect = new Rect();
+    private Rect mViewRect = new Rect();
+
     public StartSurfaceToolbarView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -45,6 +49,26 @@
         updatePrimaryColorAndTint(false);
     }
 
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        // TODO(https://crbug.com/1040526)
+
+        super.onLayout(changed, l, t, r, b);
+
+        if (mLogo.getVisibility() == View.GONE) return;
+
+        mLogoRect.set(mLogo.getLeft(), mLogo.getTop(), mLogo.getRight(), mLogo.getBottom());
+        for (int viewIndex = 0; viewIndex < getChildCount(); viewIndex++) {
+            View view = getChildAt(viewIndex);
+            if (view == mLogo) continue;
+            mViewRect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+            if (Rect.intersects(mLogoRect, mViewRect)) {
+                mLogo.setVisibility(View.GONE);
+                break;
+            }
+        }
+    }
+
     /**
      * @param appMenuButtonHelper The {@link AppMenuButtonHelper} for managing menu button
      *         interactions.
@@ -64,7 +88,8 @@
     }
 
     /**
-     * @param isVisible Whether the Logo is visible.
+     * Sets the Logo visibility. Logo will not show if screen is not wide enough.
+     * @param isVisible Whether the Logo should be visible.
      */
     void setLogoVisibility(boolean isVisible) {
         mLogo.setVisibility(isVisible ? View.VISIBLE : View.GONE);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
index fcc3fafb..7fdc458 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/system/StatusBarColorController.java
@@ -61,7 +61,7 @@
          *         version.
          */
         @ColorInt
-        int getBaseStatusBarColor(boolean activityHasTab);
+        int getBaseStatusBarColor(Tab tab);
     }
 
     private final Window mWindow;
@@ -236,8 +236,7 @@
 
     private @ColorInt int calculateBaseStatusBarColor() {
         // Return overridden status bar color from StatusBarColorProvider if specified.
-        final int baseStatusBarColor = mStatusBarColorProvider.getBaseStatusBarColor(
-                mCurrentTab != null /* activityHasTab */);
+        final int baseStatusBarColor = mStatusBarColorProvider.getBaseStatusBarColor(mCurrentTab);
         if (baseStatusBarColor != UNDEFINED_STATUS_BAR_COLOR) {
             return baseStatusBarColor;
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 110f63aa..b215e4f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -49,9 +49,7 @@
 import org.chromium.chrome.browser.tab.TabDelegateFactory;
 import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.tab.TabObserver;
-import org.chromium.chrome.browser.tab.TabThemeColorHelper;
 import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate;
-import org.chromium.chrome.browser.ui.system.StatusBarColorController;
 import org.chromium.chrome.browser.usage_stats.UsageStatsService;
 import org.chromium.chrome.browser.util.AndroidTaskUtils;
 import org.chromium.chrome.browser.webapps.dependency_injection.WebappActivityComponent;
@@ -352,6 +350,7 @@
         mTabObserverRegistrar = component.resolveTabObserverRegistrar();
         mDelegateFactory = component.resolveTabDelegateFactory();
 
+        mToolbarColorController.setUseTabThemeColor(true /* useTabThemeColor */);
         mStatusBarColorProvider.setUseTabThemeColor(true /* useTabThemeColor */);
 
         mNavigationController.setFinishHandler((reason) -> { handleFinishAndClose(); });
@@ -391,7 +390,6 @@
                 // Avoid situations where Android starts two Activities with the same data.
                 AndroidTaskUtils.finishOtherTasksWithData(getIntent().getData(), getTaskId());
             }
-            updateToolbarColor();
         }
         super.onResume();
     }
@@ -540,11 +538,6 @@
             }
 
             @Override
-            public void onDidChangeThemeColor(Tab tab, int color) {
-                updateToolbarColor();
-            }
-
-            @Override
             public void onDidAttachInterstitialPage(Tab tab) {
                 int state = ApplicationStatus.getStateForActivity(WebappActivity.this);
                 if (state == ActivityState.PAUSED || state == ActivityState.STOPPED
@@ -596,23 +589,6 @@
         return null;
     }
 
-    private void updateToolbarColor() {
-        if (getToolbarManager() == null) return;
-
-        Tab tab = getActivityTab();
-        int toolbarColor = getBaseStatusBarColor((tab != null) /* activityHasTab */);
-
-        if (toolbarColor == StatusBarColorController.DEFAULT_STATUS_BAR_COLOR) return;
-
-        // If the color is undefined, we use the color from the tab (if available).
-        if (toolbarColor == StatusBarColorController.UNDEFINED_STATUS_BAR_COLOR) {
-            toolbarColor = (tab != null) ? TabThemeColorHelper.getColor(tab)
-                                         : mIntentDataProvider.getToolbarColor();
-        }
-
-        getToolbarManager().onThemeColorChanged(toolbarColor, false);
-    }
-
     @Override
     public boolean onMenuOrKeyboardAction(int id, boolean fromMenu) {
         // Disable creating bookmark.
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/CompositorVisibilityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/CompositorVisibilityTest.java
index 1efcb884..25447e5 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/CompositorVisibilityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/CompositorVisibilityTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.compositor;
 
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Build;
 import android.support.test.filters.SmallTest;
@@ -62,7 +63,7 @@
         public void loadPersitentTextureDataIfNeeded() {}
 
         @Override
-        public int getBrowserControlsBackgroundColor() {
+        public int getBrowserControlsBackgroundColor(Resources res) {
             return 0;
         }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
index e7afc5b..a960661 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/layouts/MockLayoutHost.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.compositor.layouts;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.graphics.RectF;
 
@@ -148,7 +149,7 @@
     public void onContentChanged() {}
 
     @Override
-    public int getBrowserControlsBackgroundColor() {
+    public int getBrowserControlsBackgroundColor(Resources res) {
         return 0;
     }
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java
index 72a5b64..76eb697 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabStatusBarColorProviderTest.java
@@ -20,6 +20,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabImpl;
 import org.chromium.chrome.browser.ui.system.StatusBarColorController;
 
 /**
@@ -34,6 +36,8 @@
 
     @Mock public CustomTabIntentDataProvider mCustomTabIntentDataProvider;
     @Mock public StatusBarColorController mStatusBarColorController;
+    @Mock
+    public TabImpl mTab;
     private CustomTabStatusBarColorProvider mColorProvider;
 
     @Before
@@ -45,42 +49,52 @@
 
         when(mCustomTabIntentDataProvider.getToolbarColor()).thenReturn(USER_PROVIDED_COLOR);
         when(mCustomTabIntentDataProvider.hasCustomToolbarColor()).thenReturn(true);
+        when(mTab.isPreview()).thenReturn(false);
     }
 
     @Test
     public void fallsBackWhenOpenedByChrome() {
         when(mCustomTabIntentDataProvider.isOpenedByChrome()).thenReturn(true);
 
-        Assert.assertEquals(FALLBACK_COLOR, getStatusBarColor(true /* activityHasTab */));
+        Assert.assertEquals(FALLBACK_COLOR, getStatusBarColor(mTab));
     }
 
     @Test
     public void useTabThemeColor_enable() {
         mColorProvider.setUseTabThemeColor(true);
-        Assert.assertEquals(
-                UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor(true /* activityHasTab */));
+        Assert.assertEquals(UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor(mTab));
         verify(mStatusBarColorController).updateStatusBarColor();
     }
 
     @Test
     public void useTabThemeColor_enable_nullTab() {
         mColorProvider.setUseTabThemeColor(true);
-        Assert.assertEquals(USER_PROVIDED_COLOR, getStatusBarColor(false /* activityHasTab */));
+        Assert.assertEquals(USER_PROVIDED_COLOR, getStatusBarColor(null));
 
         when(mCustomTabIntentDataProvider.hasCustomToolbarColor()).thenReturn(false);
-        Assert.assertEquals(
-                DEFAULT_STATUS_BAR_COLOR, getStatusBarColor(false /* activityHasTab */));
+        Assert.assertEquals(DEFAULT_STATUS_BAR_COLOR, getStatusBarColor(null));
+    }
+
+    @Test
+    public void useTabThemeColor_preview() {
+        when(mTab.isPreview()).thenReturn(true);
+        mColorProvider.setUseTabThemeColor(true);
+
+        Assert.assertEquals(DEFAULT_STATUS_BAR_COLOR, getStatusBarColor(mTab));
+        verify(mStatusBarColorController).updateStatusBarColor();
+
+        mColorProvider.setUseTabThemeColor(false);
+        verify(mStatusBarColorController, times(2)).updateStatusBarColor();
     }
 
     @Test
     public void useTabThemeColor_disable() {
         mColorProvider.setUseTabThemeColor(true);
-        Assert.assertEquals(
-                UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor(true /* activityHasTab */));
+        Assert.assertEquals(UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor(mTab));
         verify(mStatusBarColorController).updateStatusBarColor();
 
         mColorProvider.setUseTabThemeColor(false);
-        Assert.assertEquals(USER_PROVIDED_COLOR, getStatusBarColor(true /* activityHasTab */));
+        Assert.assertEquals(USER_PROVIDED_COLOR, getStatusBarColor(mTab));
         verify(mStatusBarColorController, times(2)).updateStatusBarColor();
     }
 
@@ -88,7 +102,7 @@
     public void useTabThemeColor_disable_noCustomColor() {
         when(mCustomTabIntentDataProvider.hasCustomToolbarColor()).thenReturn(false);
         mColorProvider.setUseTabThemeColor(false);
-        Assert.assertEquals(DEFAULT_STATUS_BAR_COLOR, getStatusBarColor(true /* activityHasTab */));
+        Assert.assertEquals(DEFAULT_STATUS_BAR_COLOR, getStatusBarColor(mTab));
     }
 
     @Test
@@ -96,12 +110,11 @@
         mColorProvider.setUseTabThemeColor(true);
         mColorProvider.setUseTabThemeColor(true);
 
-        Assert.assertEquals(
-                UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor(true /* activityHasTab */));
+        Assert.assertEquals(UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor(mTab));
         verify(mStatusBarColorController).updateStatusBarColor();
     }
 
-    private int getStatusBarColor(boolean activityHasTab) {
-        return mColorProvider.getBaseStatusBarColor(activityHasTab, FALLBACK_COLOR);
+    private int getStatusBarColor(Tab tab) {
+        return mColorProvider.getBaseStatusBarColor(tab, FALLBACK_COLOR);
     }
 }
diff --git a/chrome/android/modules/OWNERS b/chrome/android/modules/OWNERS
index 299251f..b18865e 100644
--- a/chrome/android/modules/OWNERS
+++ b/chrome/android/modules/OWNERS
@@ -1,3 +1,2 @@
 agrieve@chromium.org
-cjgrant@chromium.org
 tiborg@chromium.org
diff --git a/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java b/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java
index c5cc8af..aff68d71 100644
--- a/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java
+++ b/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulatorTest.java
@@ -17,6 +17,7 @@
 
 import org.chromium.base.ContextUtils;
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.CalledByNativeJavaTest;
 import org.chromium.blink_public.common.ContextMenuDataMediaType;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeFeatureList;
@@ -50,10 +51,6 @@
     private ChromeContextMenuPopulator mPopulator;
 
     @CalledByNative
-    public static ChromeContextMenuPopulatorTest create() {
-        return new ChromeContextMenuPopulatorTest();
-    }
-
     private ChromeContextMenuPopulatorTest() {}
 
     @CalledByNative
@@ -109,7 +106,7 @@
         }
     }
 
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testHttpLink() {
         FirstRunStatus.setFirstRunFlowComplete(false);
         ContextMenuParams contextMenuParams = new ContextMenuParams(0, PAGE_URL, LINK_URL,
@@ -146,7 +143,7 @@
         checkMenuOptions(contextMenuParams, expected4);
     }
 
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testMailLink() {
         FirstRunStatus.setFirstRunFlowComplete(false);
         ContextMenuParams contextMenuParams =
@@ -182,7 +179,7 @@
         checkMenuOptions(contextMenuParams, expected4);
     }
 
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testTelLink() {
         FirstRunStatus.setFirstRunFlowComplete(false);
         ContextMenuParams contextMenuParams =
@@ -219,7 +216,7 @@
         checkMenuOptions(contextMenuParams, expected4);
     }
 
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testVideoLink() {
         FirstRunStatus.setFirstRunFlowComplete(false);
         String sourceUrl = "http://www.blah.com/";
@@ -260,7 +257,7 @@
         checkMenuOptions(contextMenuParams, expected4Tab1, expected4Tab2);
     }
 
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testImageHiFi() {
         FirstRunStatus.setFirstRunFlowComplete(false);
         ContextMenuParams contextMenuParams = new ContextMenuParams(ContextMenuDataMediaType.IMAGE,
@@ -294,7 +291,7 @@
         checkMenuOptions(contextMenuParams, expected4);
     }
 
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testHttpLinkWithImageHiFi() {
         FirstRunStatus.setFirstRunFlowComplete(false);
         ContextMenuParams contextMenuParams = new ContextMenuParams(ContextMenuDataMediaType.IMAGE,
diff --git a/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderTest.java b/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderTest.java
index 87c5231..660aa9541 100644
--- a/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderTest.java
+++ b/chrome/android/native_java_unittests/src/org/chromium/chrome/browser/installedapp/InstalledAppProviderTest.java
@@ -16,6 +16,7 @@
 import org.junit.Assert;
 
 import org.chromium.base.annotations.CalledByNative;
+import org.chromium.base.annotations.CalledByNativeJavaTest;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.TimeoutTimer;
 import org.chromium.chrome.browser.instantapps.InstantAppsHandler;
@@ -344,10 +345,6 @@
     }
 
     @CalledByNative
-    public static InstalledAppProviderTest create() {
-        return new InstalledAppProviderTest();
-    }
-
     private InstalledAppProviderTest() {}
 
     @CalledByNative
@@ -361,7 +358,7 @@
 
     /** Origin of the page using the API is missing certain parts of the URI. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testOriginMissingParts() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -377,7 +374,7 @@
 
     /** Incognito mode with one related Android app. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testIncognitoWithOneInstalledRelatedApp() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -394,7 +391,7 @@
      * <p>An Android app relates to the web app, but not mutual.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testNoRelatedApps() throws Exception {
         // The web manifest has no related apps.
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {};
@@ -413,7 +410,7 @@
      * <p>An Android app relates to the web app, but not mutual.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testOneRelatedAppNoId() throws Exception {
         RelatedApplication manifestRelatedApps[] =
                 new RelatedApplication[] {createRelatedApplication(PLATFORM_ANDROID, null, null)};
@@ -431,7 +428,7 @@
      * manifest doesn't mention the Android app.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testOneRelatedNonAndroidApp() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_OTHER, PACKAGE_NAME_1, null)};
@@ -448,7 +445,7 @@
      * <p>Another Android app relates to the web app, but not mutual.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testOneRelatedAppNotInstalled() throws Exception {
         // The web manifest has a related Android app named |PACKAGE_NAME_1|.
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
@@ -466,7 +463,7 @@
      * Android app manifest has an asset_statements key, but the resource it links to is missing.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testOneRelatedAppBrokenAssetStatementsResource() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -482,7 +479,7 @@
 
     /** One related Android app; Android app is not mutually related (has no asset_statements). */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testOneRelatedAppNoAssetStatements() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -494,7 +491,7 @@
 
     /** One related Android app; Android app is not mutually related (has no asset_statements). */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testOneRelatedAppNoAssetStatementsNullMetadata() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -513,7 +510,7 @@
      * to a web app with a different port.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testOneRelatedAppRelatedToDifferentOrigins() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -534,7 +531,7 @@
 
     /** One related Android app; Android app is installed and mutually related. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testOneInstalledRelatedApp() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -551,7 +548,7 @@
      * <p>This simulates navigating the frame while keeping the same Mojo service open.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testDynamicallyChangingUrl() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -579,7 +576,7 @@
 
     /** One related Android app (installed and mutually related), with a non-null URL field. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testInstalledRelatedAppWithUrl() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, URL_UNRELATED)};
@@ -592,7 +589,7 @@
 
     /** One related Android app; Android app is related to multiple origins. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testMultipleAssetStatements() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -612,7 +609,7 @@
 
     /** A JSON syntax error in the Android app's asset statement. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementSyntaxError() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -626,7 +623,7 @@
 
     /** The Android app's asset statement is not an array. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementNotArray() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -640,7 +637,7 @@
 
     /** The Android app's asset statement array contains non-objects. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementArrayNoObjects() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -661,7 +658,7 @@
      * the app is still returned as "installed".
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementNoRelation() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -683,7 +680,7 @@
      * any will do. Is this desirable, or do we want to require a specific relation string?
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementNonStandardRelation() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -698,7 +695,7 @@
 
     /** Android app has no "target" in the asset statement. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementNoTarget() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -712,7 +709,7 @@
 
     /** Android app has no "namespace" in the asset statement. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementNoNamespace() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -728,7 +725,7 @@
 
     /** Android app is related, but not to the web namespace. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testNonWebAssetStatement() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -741,7 +738,7 @@
 
     /** Android app has no "site" in the asset statement. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementNoSite() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -757,7 +754,7 @@
 
     /** Android app has a syntax error in the "site" field of the asset statement. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementSiteSyntaxError() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -771,7 +768,7 @@
 
     /** Android app has a "site" field missing certain parts of the URI (scheme, host, port). */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementSiteMissingParts() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -797,7 +794,7 @@
      * if it is, we are lenient and just ignore it (matching only the origin).
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testAssetStatementSiteHasPath() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -815,7 +812,7 @@
      * <p>Another Android app relates to the web app, but not mutual.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testExtraInstalledApp() throws Exception {
         RelatedApplication manifestRelatedApps[] = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null)};
@@ -834,7 +831,7 @@
      * app which is not installed.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testMultipleInstalledRelatedApps() throws Exception {
         RelatedApplication[] manifestRelatedApps = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null),
@@ -851,7 +848,7 @@
 
     /** Tests the pseudo-random artificial delay to counter a timing attack. */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testArtificialDelay() throws Exception {
         byte[] salt = {0x64, 0x09, -0x68, -0x25, 0x70, 0x11, 0x25, 0x24, 0x68, -0x1a, 0x08, 0x79,
                 -0x12, -0x50, 0x3b, -0x57, -0x17, -0x4d, 0x46, 0x02};
@@ -878,7 +875,7 @@
     }
 
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testMultipleAppsIncludingInstantApps() throws Exception {
         RelatedApplication[] manifestRelatedApps = new RelatedApplication[] {
                 createRelatedApplication(PLATFORM_ANDROID, PACKAGE_NAME_1, null),
@@ -901,7 +898,7 @@
      * app which is installed and mutually related.
      */
     @Feature({"InstalledApp"})
-    @CalledByNative
+    @CalledByNativeJavaTest
     public void testRelatedAppsOverAllowedThreshold() throws Exception {
         RelatedApplication manifestRelatedApps[] =
                 new RelatedApplication[MAX_ALLOWED_RELATED_APPS + 1];
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index 61ed541..588e9a9 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -901,17 +901,6 @@
         </message>
       </if>
 
-      <!-- Cannot update when other instances open dialog -->
-      <message name="IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_TITLE" desc="The title for the dialog shown when multiple copies of Chromium are running during a requested update.">
-        Quit all copies of Chromium.
-      </message>
-      <message name="IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_MESSAGE" desc="The message for the dialog shown when multiple copies of Chromium are running during a requested update.">
-        Other copies of Chromium are running. To update, close them.
-      </message>
-      <message name="IDS_UPDATE_OTHER_INSTANCES_OTHER_USER_AUTHENTICATION_PROMPT" desc="The window title for the other instances dialog.">
-        To update, Chromium needs to stop running on this computer. This may cause other users logged into this computer to lose unsaved changes.
-      </message>
-
       <!-- Update bubble -->
       <message name="IDS_REINSTALL_APP" desc="Text for the button the user clicks to reinstall the app.">
         Reinstall Chromium
diff --git a/chrome/app/chromium_strings_grd/IDS_UPDATE_OTHER_INSTANCES_OTHER_USER_AUTHENTICATION_PROMPT.png.sha1 b/chrome/app/chromium_strings_grd/IDS_UPDATE_OTHER_INSTANCES_OTHER_USER_AUTHENTICATION_PROMPT.png.sha1
deleted file mode 100644
index 58f7dd84..0000000
--- a/chrome/app/chromium_strings_grd/IDS_UPDATE_OTHER_INSTANCES_OTHER_USER_AUTHENTICATION_PROMPT.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-66ae5fe59639369ffcec839317b605afbf6a9999
\ No newline at end of file
diff --git a/chrome/app/chromium_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_MESSAGE.png.sha1 b/chrome/app/chromium_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_MESSAGE.png.sha1
deleted file mode 100644
index 97f012f..0000000
--- a/chrome/app/chromium_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-5a459cf289bf4cb9a74cf0fc8101bd2fe4376da5
\ No newline at end of file
diff --git a/chrome/app/chromium_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_TITLE.png.sha1 b/chrome/app/chromium_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_TITLE.png.sha1
deleted file mode 100644
index 97f012f..0000000
--- a/chrome/app/chromium_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-5a459cf289bf4cb9a74cf0fc8101bd2fe4376da5
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 885b623..8a5a0f2b 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -920,17 +920,6 @@
         </message>
       </if>
 
-      <!-- Cannot update when other instances open dialog -->
-      <message name="IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_TITLE" desc="The title for the dialog shown when multiple copies of Chrome are running during a requested update.">
-        Quit all copies of Chrome.
-      </message>
-      <message name="IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_MESSAGE" desc="The message for the dialog shown when multiple copies of Chrome are running during a requested update.">
-        Other copies of Chrome are running. To update, close them.
-      </message>
-      <message name="IDS_UPDATE_OTHER_INSTANCES_OTHER_USER_AUTHENTICATION_PROMPT" desc="The window title for the other instances dialog.">
-        To update, Chrome needs to stop running on this computer. This may cause other users logged into this computer to lose unsaved changes.
-      </message>
-
       <!-- Update bubble -->
       <message name="IDS_REINSTALL_APP" desc="Text for the button the user clicks to reinstall the app.">
         Reinstall Chrome
diff --git a/chrome/app/google_chrome_strings_grd/IDS_UPDATE_OTHER_INSTANCES_OTHER_USER_AUTHENTICATION_PROMPT.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_UPDATE_OTHER_INSTANCES_OTHER_USER_AUTHENTICATION_PROMPT.png.sha1
deleted file mode 100644
index bc17871a..0000000
--- a/chrome/app/google_chrome_strings_grd/IDS_UPDATE_OTHER_INSTANCES_OTHER_USER_AUTHENTICATION_PROMPT.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-fc2ecc9de85254fabf108fb569cf826b58bbae1c
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_MESSAGE.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_MESSAGE.png.sha1
deleted file mode 100644
index 90bb1b2..0000000
--- a/chrome/app/google_chrome_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_MESSAGE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-99578547c9d8ceeb6dfc6af831c4f45491875970
\ No newline at end of file
diff --git a/chrome/app/google_chrome_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_TITLE.png.sha1 b/chrome/app/google_chrome_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_TITLE.png.sha1
deleted file mode 100644
index 90bb1b2..0000000
--- a/chrome/app/google_chrome_strings_grd/IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-99578547c9d8ceeb6dfc6af831c4f45491875970
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b874ebe3d..8bb1c2a 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4172,8 +4172,7 @@
     sources += [
       "first_run/upgrade_util.cc",
       "first_run/upgrade_util.h",
-      "first_run/upgrade_util_mac.h",
-      "first_run/upgrade_util_mac.mm",
+      "first_run/upgrade_util_mac.cc",
       "first_run/upgrade_util_win.cc",
       "first_run/upgrade_util_win.h",
       "lifetime/switch_utils.cc",
diff --git a/chrome/browser/android/vr/OWNERS b/chrome/browser/android/vr/OWNERS
index 370201a..ae65ee2 100644
--- a/chrome/browser/android/vr/OWNERS
+++ b/chrome/browser/android/vr/OWNERS
@@ -1,6 +1,5 @@
 alcooper@chromium.org
 bialpio@chromium.org
-cjgrant@chromium.org
 mthiesse@chromium.org
 tiborg@chromium.org
 vollick@chromium.org
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
index c14358e..f3e896b9 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -63,6 +63,8 @@
 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/translate/chrome_translate_client.h"
+#include "chrome/browser/ui/find_bar/find_bar_state.h"
+#include "chrome/browser/ui/find_bar/find_bar_state_factory.h"
 #include "chrome/browser/web_data_service_factory.h"
 #include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h"
 #include "chrome/common/buildflags.h"
@@ -639,6 +641,9 @@
 #endif
 
     CreateCrashUploadList()->Clear(delete_begin_, delete_end_);
+
+    FindBarStateFactory::GetForProfile(profile_)->set_last_prepopulate_text(
+        base::string16());
   }
 
   //////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/chrome_browser_main_mac.mm b/chrome/browser/chrome_browser_main_mac.mm
index 53a4ad6..8a40acb 100644
--- a/chrome/browser/chrome_browser_main_mac.mm
+++ b/chrome/browser/chrome_browser_main_mac.mm
@@ -5,9 +5,6 @@
 #include "chrome/browser/chrome_browser_main_mac.h"
 
 #import <Cocoa/Cocoa.h>
-#include <stdlib.h>
-#include <sys/mount.h>
-#include <sys/param.h>
 
 #include "base/bind.h"
 #include "base/command_line.h"
@@ -18,8 +15,6 @@
 #include "base/mac/mac_util.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/mac/sdk_forward_declarations.h"
-#include "base/metrics/histogram_functions.h"
-#include "base/optional.h"
 #include "base/path_service.h"
 #include "base/task/post_task.h"
 #include "base/task/task_traits.h"
@@ -30,14 +25,12 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #import "chrome/browser/chrome_browser_application_mac.h"
 #include "chrome/browser/first_run/first_run.h"
-#include "chrome/browser/first_run/upgrade_util_mac.h"
 #include "chrome/browser/mac/install_from_dmg.h"
 #import "chrome/browser/mac/keystone_glue.h"
 #include "chrome/browser/mac/mac_startup_profiler.h"
 #include "chrome/browser/ui/cocoa/main_menu_builder.h"
 #include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/mac/staging_watcher.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/chromium_strings.h"
 #include "components/crash/content/app/crashpad.h"
@@ -74,320 +67,6 @@
       base::BindOnce(&EnsureMetadataNeverIndexFileOnFileThread, user_data_dir));
 }
 
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class FilesystemType {
-  kUnknown,
-  kOther,
-  k_acfs,
-  k_afpfs,
-  k_apfs,
-  k_cdd9660,
-  k_cddafs,
-  k_exfat,
-  k_ftp,
-  k_hfs,
-  k_hfs_rodmg,
-  k_msdos,
-  k_nfs,
-  k_ntfs,
-  k_smbfs,
-  k_udf,
-  k_webdav,
-  kGoogleDriveFS,
-  kMaxValue = kGoogleDriveFS,
-};
-
-FilesystemType FilesystemStringToType(DiskImageStatus is_ro_dmg,
-                                      NSString* filesystem_type) {
-  if ([filesystem_type isEqualToString:@"acfs"])
-    return FilesystemType::k_acfs;
-  if ([filesystem_type isEqualToString:@"afpfs"])
-    return FilesystemType::k_afpfs;
-  if ([filesystem_type isEqualToString:@"apfs"])
-    return FilesystemType::k_apfs;
-  if ([filesystem_type isEqualToString:@"cdd9660"])
-    return FilesystemType::k_cdd9660;
-  if ([filesystem_type isEqualToString:@"cddafs"])
-    return FilesystemType::k_cddafs;
-  if ([filesystem_type isEqualToString:@"exfat"])
-    return FilesystemType::k_exfat;
-  if ([filesystem_type isEqualToString:@"hfs"]) {
-    switch (is_ro_dmg) {
-      case DiskImageStatusFailure:
-      case DiskImageStatusFalse:
-        return FilesystemType::k_hfs;
-        break;
-
-      case DiskImageStatusTrue:
-        return FilesystemType::k_hfs_rodmg;
-        break;
-    }
-  }
-  if ([filesystem_type isEqualToString:@"msdos"])
-    return FilesystemType::k_msdos;
-  if ([filesystem_type isEqualToString:@"nfs"])
-    return FilesystemType::k_nfs;
-  if ([filesystem_type isEqualToString:@"ntfs"])
-    return FilesystemType::k_ntfs;
-  if ([filesystem_type isEqualToString:@"smbfs"])
-    return FilesystemType::k_smbfs;
-  if ([filesystem_type isEqualToString:@"udf"])
-    return FilesystemType::k_udf;
-  if ([filesystem_type isEqualToString:@"webdav"])
-    return FilesystemType::k_webdav;
-  if ([filesystem_type isEqualToString:@"dfsfuse_DFS"])
-    return FilesystemType::kGoogleDriveFS;
-  return FilesystemType::kOther;
-}
-
-void RecordFilesystemStats() {
-  DiskImageStatus is_ro_dmg = IsAppRunningFromReadOnlyDiskImage(nullptr);
-  // Note that -getFileSystemInfoForPath:... is implemented with Disk
-  // Arbitration and |filesystem_type_string| is the value from
-  // kDADiskDescriptionVolumeKindKey. Furthermore, for built-in filesystems, the
-  // string returned specifies which file in /System/Library/Filesystems is
-  // handling it.
-  NSString* filesystem_type_string;
-  BOOL success = [[NSWorkspace sharedWorkspace]
-      getFileSystemInfoForPath:[base::mac::OuterBundle() bundlePath]
-                   isRemovable:nil
-                    isWritable:nil
-                 isUnmountable:nil
-                   description:nil
-                          type:&filesystem_type_string];
-
-  FilesystemType filesystem_type = FilesystemType::kUnknown;
-  if (success)
-    filesystem_type = FilesystemStringToType(is_ro_dmg, filesystem_type_string);
-
-  base::UmaHistogramEnumeration("OSX.InstallationFilesystem", filesystem_type);
-}
-
-void RecordInstanceStats() {
-  upgrade_util::ThisAndOtherUserCounts counts =
-      upgrade_util::GetCountOfOtherInstancesOfThisBinary();
-
-  base::UmaHistogramCounts100("OSX.OtherInstances.ThisUser",
-                              counts.this_user_count);
-  base::UmaHistogramCounts100("OSX.OtherInstances.OtherUser",
-                              counts.other_user_count);
-}
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class FastUserSwitchEvent {
-  kUserDidBecomeActiveEvent,
-  kUserDidBecomeInactiveEvent,
-  kMaxValue = kUserDidBecomeInactiveEvent,
-};
-
-void LogFastUserSwitchStat(FastUserSwitchEvent event) {
-  base::UmaHistogramEnumeration("OSX.FastUserSwitch", event);
-}
-
-void InstallFastUserSwitchStatRecorder() {
-  NSNotificationCenter* notification_center =
-      [[NSWorkspace sharedWorkspace] notificationCenter];
-  [notification_center
-      addObserverForName:NSWorkspaceSessionDidBecomeActiveNotification
-                  object:nil
-                   queue:nil
-              usingBlock:^(NSNotification* note) {
-                LogFastUserSwitchStat(
-                    FastUserSwitchEvent::kUserDidBecomeActiveEvent);
-              }];
-  [notification_center
-      addObserverForName:NSWorkspaceSessionDidResignActiveNotification
-                  object:nil
-                   queue:nil
-              usingBlock:^(NSNotification* note) {
-                LogFastUserSwitchStat(
-                    FastUserSwitchEvent::kUserDidBecomeInactiveEvent);
-              }];
-}
-
-bool IsDirectoryWriteable(NSString* dir_path) {
-  NSString* file_path = [dir_path stringByAppendingPathComponent:@"tempfile"];
-  NSData* data = [NSData dataWithBytes:"\01\02\03\04\05" length:5];
-  BOOL success = [data writeToFile:file_path atomically:NO];
-  if (success)
-    [[NSFileManager defaultManager] removeItemAtPath:file_path error:nil];
-
-  return success;
-}
-
-bool IsOnSameFilesystemAsChromium(NSString* dir_path) {
-  static const base::Optional<fsid_t> cr_fsid = []() -> base::Optional<fsid_t> {
-    struct statfs buf;
-    int result = statfs(
-        [[base::mac::OuterBundle() bundlePath] fileSystemRepresentation], &buf);
-    if (result != 0)
-      return base::nullopt;
-    return buf.f_fsid;
-  }();
-
-  if (!cr_fsid)
-    return false;
-
-  struct statfs buf;
-  int result = statfs([dir_path fileSystemRepresentation], &buf);
-  if (result != 0)
-    return false;
-
-  return cr_fsid->val[0] == buf.f_fsid.val[0] &&
-         cr_fsid->val[1] == buf.f_fsid.val[1];
-}
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class StagingDirectoryStep {
-  kFailedToFindDirectory,
-  kItemReplacementDirectory,
-  kSiblingDirectory,
-  kNSTemporaryDirectory,
-  kTMPDIRDirectory,
-  kTmpDirectory,
-  kTmpDirectoryDifferentVolume,
-  kMaxValue = kTmpDirectoryDifferentVolume,
-};
-
-void LogStagingDirectoryLocation(StagingDirectoryStep step) {
-  base::UmaHistogramEnumeration("OSX.StagingDirectoryLocation2", step);
-}
-
-void RecordStagingDirectoryStats() {
-  NSURL* bundle_url = [base::mac::OuterBundle() bundleURL];
-  NSFileManager* file_manager = [NSFileManager defaultManager];
-
-  // 1. A directory alongside Chromium.
-
-  NSURL* bundle_parent_url =
-      [[bundle_url URLByStandardizingPath] URLByDeletingLastPathComponent];
-  NSURL* sibling_dir =
-      [bundle_parent_url URLByAppendingPathComponent:@".GoogleChromeStaging"
-                                         isDirectory:YES];
-  NSString* sibling_dir_path = [sibling_dir path];
-
-  BOOL is_directory;
-  BOOL path_existed = [file_manager fileExistsAtPath:sibling_dir_path
-                                         isDirectory:&is_directory];
-
-  BOOL success = true;
-  NSError* error = nil;
-  if (!path_existed) {
-    success = [file_manager createDirectoryAtURL:sibling_dir
-                     withIntermediateDirectories:YES
-                                      attributes:nil
-                                           error:&error];
-  } else if (!is_directory) {
-    // There is a non-directory there; don't attempt to use this location
-    // further.
-    success = false;
-  }
-
-  if (success) {
-    success &= !error && IsDirectoryWriteable(sibling_dir_path);
-
-    // Only delete this directory if this was the code that created it.
-    if (!path_existed)
-      [file_manager removeItemAtURL:sibling_dir error:nil];
-  }
-
-  if (success) {
-    LogStagingDirectoryLocation(StagingDirectoryStep::kSiblingDirectory);
-    return;
-  }
-
-  // 2. NSItemReplacementDirectory
-
-  error = nil;
-  NSURL* item_replacement_dir =
-      [file_manager URLForDirectory:NSItemReplacementDirectory
-                           inDomain:NSUserDomainMask
-                  appropriateForURL:bundle_url
-                             create:YES
-                              error:&error];
-  if (item_replacement_dir && !error &&
-      IsDirectoryWriteable([item_replacement_dir path])) {
-    LogStagingDirectoryLocation(
-        StagingDirectoryStep::kItemReplacementDirectory);
-    return;
-  }
-
-  // 3. NSTemporaryDirectory()
-
-  NSString* ns_temporary_dir = NSTemporaryDirectory();
-  if (ns_temporary_dir && IsOnSameFilesystemAsChromium(ns_temporary_dir) &&
-      IsDirectoryWriteable(ns_temporary_dir)) {
-    LogStagingDirectoryLocation(StagingDirectoryStep::kNSTemporaryDirectory);
-    return;
-  }
-
-  // 4. $TMPDIR
-
-  const char* tmpdir_cstr = getenv("TMPDIR");
-  NSString* tmpdir = tmpdir_cstr ? @(tmpdir_cstr) : nil;
-  if (tmpdir && IsOnSameFilesystemAsChromium(tmpdir) &&
-      IsDirectoryWriteable(tmpdir)) {
-    LogStagingDirectoryLocation(StagingDirectoryStep::kTMPDIRDirectory);
-    return;
-  }
-
-  // 5. /tmp
-
-  NSString* tmp = @"/tmp";
-  if (IsOnSameFilesystemAsChromium(tmp) && IsDirectoryWriteable(tmp)) {
-    LogStagingDirectoryLocation(StagingDirectoryStep::kTmpDirectory);
-    return;
-  }
-
-  // 6. /tmp, but different volume
-
-  if (IsDirectoryWriteable(tmp)) {
-    LogStagingDirectoryLocation(StagingDirectoryStep::kTmpDirectory);
-    return;
-  }
-
-  // 7. Give up.
-
-  LogStagingDirectoryLocation(StagingDirectoryStep::kFailedToFindDirectory);
-}
-
-// Records various bits of information about the local Chromium installation in
-// UMA.
-void RecordInstallationStats() {
-  RecordFilesystemStats();
-  RecordInstanceStats();
-  InstallFastUserSwitchStatRecorder();
-  RecordStagingDirectoryStats();
-}
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class StartupUpdateState {
-  kUpdateKeyNotSet,
-  kUpdateKeySetAndStagedCopyPresent,
-  kUpdateKeySetAndStagedCopyNotPresent,
-  kMaxValue = kUpdateKeySetAndStagedCopyNotPresent,
-};
-
-// Records about the state of Chrome updates. This is pre-emptory data
-// gathering to make sure that a situation that the team thinks will be OK is
-// actually OK in the field.
-void RecordUpdateState() {
-  StartupUpdateState state = StartupUpdateState::kUpdateKeyNotSet;
-  NSString* staging_location = [CrStagingKeyWatcher stagingLocation];
-  if (staging_location) {
-    if ([[NSFileManager defaultManager] fileExistsAtPath:staging_location])
-      state = StartupUpdateState::kUpdateKeySetAndStagedCopyPresent;
-    else
-      state = StartupUpdateState::kUpdateKeySetAndStagedCopyNotPresent;
-  }
-
-  base::UmaHistogramEnumeration("OSX.StartupUpdateState", state);
-}
-
 }  // namespace
 
 // ChromeBrowserMainPartsMac ---------------------------------------------------
@@ -477,10 +156,6 @@
   MacStartupProfiler::GetInstance()->Profile(
       MacStartupProfiler::POST_MAIN_MESSAGE_LOOP_START);
   ChromeBrowserMainPartsPosix::PostMainMessageLoopStart();
-
-  RecordInstallationStats();
-
-  RecordUpdateState();
 }
 
 void ChromeBrowserMainPartsMac::PreProfileInit() {
diff --git a/chrome/browser/contextmenu/chrome_context_menu_populator_unittest.cc b/chrome/browser/contextmenu/chrome_context_menu_populator_unittest.cc
index c80a59c2..0236b87 100644
--- a/chrome/browser/contextmenu/chrome_context_menu_populator_unittest.cc
+++ b/chrome/browser/contextmenu/chrome_context_menu_populator_unittest.cc
@@ -10,14 +10,12 @@
 
 class ChromeContextMenuPopulatorTest : public ::testing::Test {
  public:
-  ChromeContextMenuPopulatorTest() {
-    JNIEnv* env = AttachCurrentThread();
-    j_test_.Reset(Java_ChromeContextMenuPopulatorTest_create(env));
-  }
+  ChromeContextMenuPopulatorTest()
+      : j_test_(Java_ChromeContextMenuPopulatorTest_Constructor(
+            AttachCurrentThread())) {}
 
   void SetUp() override {
-    JNIEnv* env = AttachCurrentThread();
-    Java_ChromeContextMenuPopulatorTest_setUp(env, j_test_);
+    Java_ChromeContextMenuPopulatorTest_setUp(AttachCurrentThread(), j_test_);
   }
 
   const base::android::ScopedJavaGlobalRef<jobject>& j_test() {
@@ -28,32 +26,4 @@
   base::android::ScopedJavaGlobalRef<jobject> j_test_;
 };
 
-TEST_F(ChromeContextMenuPopulatorTest, TestHttpLink) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_ChromeContextMenuPopulatorTest_testHttpLink(env, j_test());
-}
-
-TEST_F(ChromeContextMenuPopulatorTest, TestMailLink) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_ChromeContextMenuPopulatorTest_testMailLink(env, j_test());
-}
-
-TEST_F(ChromeContextMenuPopulatorTest, TestTelLink) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_ChromeContextMenuPopulatorTest_testTelLink(env, j_test());
-}
-
-TEST_F(ChromeContextMenuPopulatorTest, TestVideoLink) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_ChromeContextMenuPopulatorTest_testVideoLink(env, j_test());
-}
-
-TEST_F(ChromeContextMenuPopulatorTest, TestImageHiFi) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_ChromeContextMenuPopulatorTest_testImageHiFi(env, j_test());
-}
-
-TEST_F(ChromeContextMenuPopulatorTest, TestHttpLinkWithImageHiFi) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_ChromeContextMenuPopulatorTest_testHttpLinkWithImageHiFi(env, j_test());
-}
+JAVA_TESTS(ChromeContextMenuPopulatorTest, j_test())
diff --git a/chrome/browser/first_run/upgrade_util_mac.cc b/chrome/browser/first_run/upgrade_util_mac.cc
new file mode 100644
index 0000000..72ef49cc
--- /dev/null
+++ b/chrome/browser/first_run/upgrade_util_mac.cc
@@ -0,0 +1,14 @@
+// Copyright 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "chrome/browser/mac/relauncher.h"
+
+namespace upgrade_util {
+
+bool RelaunchChromeBrowserImpl(const base::CommandLine& command_line) {
+  return mac_relauncher::RelaunchApp(command_line.argv());
+}
+
+}  // namespace upgrade_util
diff --git a/chrome/browser/first_run/upgrade_util_mac.h b/chrome/browser/first_run/upgrade_util_mac.h
deleted file mode 100644
index ad9a06da..0000000
--- a/chrome/browser/first_run/upgrade_util_mac.h
+++ /dev/null
@@ -1,26 +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 CHROME_BROWSER_FIRST_RUN_UPGRADE_UTIL_MAC_H_
-#define CHROME_BROWSER_FIRST_RUN_UPGRADE_UTIL_MAC_H_
-
-namespace upgrade_util {
-
-struct ThisAndOtherUserCounts {
-  int this_user_count;
-  int other_user_count;
-};
-
-// Returns the number of other running instances of this binary, both by this
-// user and by other users. Returns 0 for the counts if there is an error.
-ThisAndOtherUserCounts GetCountOfOtherInstancesOfThisBinary();
-
-// Returns whether it is safe to relaunch Chromium to take a staged upgrade.
-// Returns true if there are no other instances of Chromium running; returns
-// false if there are other instances and they cannot be stopped.
-bool ShouldContinueToRelaunchForUpgrade();
-
-}  // namespace upgrade_util
-
-#endif  // CHROME_BROWSER_FIRST_RUN_UPGRADE_UTIL_MAC_H_
diff --git a/chrome/browser/first_run/upgrade_util_mac.mm b/chrome/browser/first_run/upgrade_util_mac.mm
deleted file mode 100644
index d08b7f5..0000000
--- a/chrome/browser/first_run/upgrade_util_mac.mm
+++ /dev/null
@@ -1,299 +0,0 @@
-// Copyright 2011 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/first_run/upgrade_util_mac.h"
-
-#import <AppKit/AppKit.h>
-#include <libproc.h>
-#include <unistd.h>
-
-#include <set>
-#include <vector>
-
-#include "base/command_line.h"
-#include "base/mac/authorization_util.h"
-#include "base/mac/foundation_util.h"
-#include "base/mac/scoped_authorizationref.h"
-#import "base/mac/scoped_nsobject.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "chrome/browser/mac/relauncher.h"
-#include "chrome/common/mac/staging_watcher.h"
-#include "chrome/grit/chromium_strings.h"
-#include "components/strings/grit/components_strings.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/l10n/l10n_util_mac.h"
-
-namespace upgrade_util {
-
-namespace {
-
-// Get the uid and executable path for a pid. Returns true iff successful.
-// |path_buffer| must be of PROC_PIDPATHINFO_MAXSIZE length.
-bool GetUIDAndPathOfPID(pid_t pid, char* path_buffer, uid_t* out_uid) {
-  struct proc_bsdshortinfo info;
-  int error = proc_pidinfo(pid, PROC_PIDT_SHORTBSDINFO, 0, &info, sizeof(info));
-  if (error <= 0)
-    return false;
-
-  error = proc_pidpath(pid, path_buffer, PROC_PIDPATHINFO_MAXSIZE);
-  if (error <= 0)
-    return false;
-
-  *out_uid = info.pbsi_uid;
-  return true;
-}
-
-struct ThisAndOtherUserPids {
-  std::set<pid_t> this_user;
-  std::set<pid_t> other_user;
-};
-
-ThisAndOtherUserPids GetPidsOfOtherInstancesOfThisBinary() {
-  ThisAndOtherUserPids pids;
-
-  // Get list of all processes.
-
-  int pid_array_size_needed = proc_listallpids(nullptr, 0);
-  if (pid_array_size_needed <= 0)
-    return pids;
-  std::vector<pid_t> pid_array(pid_array_size_needed * 4);  // slack
-  int pid_count = proc_listallpids(pid_array.data(),
-                                   pid_array.size() * sizeof(pid_array[0]));
-  if (pid_count <= 0)
-    return pids;
-
-  pid_array.resize(pid_count);
-
-  // Get info about this process.
-
-  const pid_t this_pid = getpid();
-  uid_t this_uid;
-  char this_path[PROC_PIDPATHINFO_MAXSIZE];
-  if (!GetUIDAndPathOfPID(this_pid, this_path, &this_uid))
-    return pids;
-
-  // Compare all other processes to this one.
-
-  for (pid_t pid : pid_array) {
-    if (pid == this_pid)
-      continue;
-
-    uid_t uid;
-    char path[PROC_PIDPATHINFO_MAXSIZE];
-    if (!GetUIDAndPathOfPID(pid, path, &uid))
-      continue;
-
-    if (strcmp(path, this_path) != 0)
-      continue;
-
-    if (uid == this_uid)
-      pids.this_user.insert(pid);
-    else
-      pids.other_user.insert(pid);
-  }
-
-  return pids;
-}
-
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-//
-// ShouldContinueToRelaunchForUpgrade() can complete in quite a few different
-// ways. This allows analysis of what's happening in the field.
-enum class OtherInstancesCheckResult {
-  kOldStyleUpdate = 0,
-  kNoOtherInstancesAtStart = 1,
-  kUserDidNotAuthorize = 2,
-  kInitialExecuteAndWaitFailed = 3,
-  kSecondaryExecuteAndWaitFailed = 4,
-  kShowedNoDialogsNoInstancesExistAtExit = 5,
-  kShowedNoDialogsInstancesExistAtExit = 6,
-  kShowedOtherUserDialogNoInstancesExistAtExit = 7,
-  kShowedOtherUserDialogInstancesExistAtExit = 8,
-  kShowedThisUserDialogNoInstancesExistAtExit = 9,
-  kShowedThisUserDialogInstancesExistAtExit = 10,
-  kShowedBothDialogsNoInstancesExistAtExit = 11,
-  kShowedBothDialogsInstancesExistAtExit = 12,
-  kMaxValue = kShowedBothDialogsInstancesExistAtExit,
-};
-
-void LogInstanceCheckResult(OtherInstancesCheckResult result) {
-  UMA_HISTOGRAM_ENUMERATION("OSX.OtherInstancesCheckResult", result);
-}
-
-void LogInstanceCheckResult(bool showed_this_user_dialog,
-                            bool showed_other_user_dialog,
-                            bool other_instances_exist) {
-  int uma_value = showed_this_user_dialog << 2 | showed_other_user_dialog << 1 |
-                  other_instances_exist << 0;
-  uma_value += static_cast<int>(
-      OtherInstancesCheckResult::kShowedNoDialogsNoInstancesExistAtExit);
-  LogInstanceCheckResult(static_cast<OtherInstancesCheckResult>(uma_value));
-}
-
-// Sends a termination signal to a set of processes. Returns true if the kill
-// command was successfully executed. This says nothing about whether the kill
-// command succeeded in actually killing anything.
-enum class SignalType { kSoftKill, kHardKill };
-bool SendKillSignalToPids(AuthorizationRef authorization,
-                          SignalType signal_type,
-                          std::set<pid_t> pids) {
-  DCHECK(!pids.empty());
-
-  // ExecuteWithPrivilegesAndWait() requires a helper tool that returns
-  // its pid via stdout. Using the inherent abilities of /bin/sh
-  // simplifies things.
-  static constexpr char shell_path[] = "/bin/sh";
-  static constexpr char shell_execute_command[] = "-c";
-
-  std::string command_string = "echo $$; exec /bin/kill ";
-  if (signal_type == SignalType::kSoftKill)
-    command_string += "-TERM";
-  else
-    command_string += "-KILL";
-  for (pid_t pid : pids) {
-    command_string += " ";
-    command_string += base::NumberToString(pid);
-  }
-
-  const char* command_string_c = command_string.c_str();
-  const char* arguments[] = {shell_execute_command, command_string_c, nullptr};
-
-  OSStatus status = base::mac::ExecuteWithPrivilegesAndWait(
-      authorization, shell_path, kAuthorizationFlagDefaults, arguments,
-      nullptr,   // pipe
-      nullptr);  // exit_status; no point in checking, as the result is checked
-                 // later by enumerating processes.
-  return status == errAuthorizationSuccess;
-}
-
-}  // namespace
-
-ThisAndOtherUserCounts GetCountOfOtherInstancesOfThisBinary() {
-  ThisAndOtherUserPids pids = GetPidsOfOtherInstancesOfThisBinary();
-
-  return ThisAndOtherUserCounts{pids.this_user.size(), pids.other_user.size()};
-}
-
-bool ShouldContinueToRelaunchForUpgrade() {
-  // If this isn't a new-style update, and the staging key isn't set, don't
-  // block at all.
-
-  if (![CrStagingKeyWatcher isStagingKeySet]) {
-    LogInstanceCheckResult(OtherInstancesCheckResult::kOldStyleUpdate);
-    return true;
-  }
-
-  ThisAndOtherUserPids pids = GetPidsOfOtherInstancesOfThisBinary();
-
-  if (pids.this_user.empty() && pids.other_user.empty()) {
-    LogInstanceCheckResult(OtherInstancesCheckResult::kNoOtherInstancesAtStart);
-    return true;
-  }
-
-  // If there are both other-user and same-user instances, start with the auth
-  // dialog, as the user might not be able to auth. It would be weird to have
-  // the user kill all their own instances, and then hit a dialog they can't
-  // answer, and be left having quit a lot of stuff.
-  bool showed_this_user_dialog = false;
-  bool showed_other_user_dialog = false;
-
-  if (!pids.other_user.empty()) {
-    showed_other_user_dialog = true;
-
-    NSString* prompt = l10n_util::GetNSString(
-        IDS_UPDATE_OTHER_INSTANCES_OTHER_USER_AUTHENTICATION_PROMPT);
-    base::mac::ScopedAuthorizationRef authorization(
-        base::mac::AuthorizationCreateToRunAsRoot(
-            base::mac::NSToCFCast(prompt)));
-
-    if (authorization.get()) {
-      // A simple kill -TERM works if no page has a beforeunload handler; kill
-      // -KILL is required otherwise. Therefore, do a kill -TERM first, wait a
-      // few seconds, and then kill -KILL stragglers.
-
-      bool success = SendKillSignalToPids(authorization, SignalType::kSoftKill,
-                                          pids.other_user);
-      if (!success) {
-        // Killing other instances failed; installation cannot proceed.
-        LogInstanceCheckResult(
-            OtherInstancesCheckResult::kInitialExecuteAndWaitFailed);
-        return false;
-      }
-
-      // Three seconds is arbitrary; the intent is to give the other Chrome
-      // instances time to close themselves down safely, if they are able to.
-      // TODO(avi): Be smarter here; perhaps a kevent watcher (with timeout) on
-      // the pids, as to not wait as long if they all close quickly?
-      sleep(3);
-
-      pids = GetPidsOfOtherInstancesOfThisBinary();
-      if (!pids.other_user.empty()) {
-        success = SendKillSignalToPids(authorization, SignalType::kHardKill,
-                                       pids.other_user);
-        if (!success) {
-          // Killing other instances failed; installation cannot proceed.
-          LogInstanceCheckResult(
-              OtherInstancesCheckResult::kSecondaryExecuteAndWaitFailed);
-          return false;
-        }
-
-        // Also arbitrary, though less time is needed for a hard kill.
-        // TODO(avi): Use a watcher as above.
-        sleep(1);
-      }
-    } else {
-      // There was no auth to kill other-user instances. At this point,
-      // installation can't proceed, so don't ask the users to kill their own
-      // instances.
-      LogInstanceCheckResult(OtherInstancesCheckResult::kUserDidNotAuthorize);
-      return false;
-    }
-  }
-
-  if (!pids.this_user.empty()) {
-    showed_this_user_dialog = true;
-
-    @autoreleasepool {
-      NSString* title = l10n_util::GetNSString(
-          IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_TITLE);
-      NSString* error = l10n_util::GetNSString(
-          IDS_UPDATE_OTHER_INSTANCES_SAME_USER_DIALOG_MESSAGE);
-      NSString* ok = l10n_util::GetNSString(IDS_OK);
-
-      NSAlert* alert = [[[NSAlert alloc] init] autorelease];
-
-      [alert setAlertStyle:NSWarningAlertStyle];
-      [alert setMessageText:title];
-      [alert setInformativeText:error];
-      [alert addButtonWithTitle:ok];
-
-      [alert runModal];
-    }
-  }
-
-  // Count the processes again, as the non-user processes may have been killed
-  // (in the other-user case), or the user may have switched away and killed
-  // their own processes while the same-user dialog was up.
-  pids = GetPidsOfOtherInstancesOfThisBinary();
-  bool other_instances_exist =
-      !pids.this_user.empty() || !pids.other_user.empty();
-  LogInstanceCheckResult(showed_this_user_dialog, showed_other_user_dialog,
-                         other_instances_exist);
-
-  return !other_instances_exist;
-}
-
-bool RelaunchChromeBrowserImpl(const base::CommandLine& command_line) {
-  upgrade_util::ThisAndOtherUserCounts counts =
-      upgrade_util::GetCountOfOtherInstancesOfThisBinary();
-  const int other_instances = counts.this_user_count + counts.other_user_count;
-  const bool wait_for_staged_update = (other_instances == 0);
-
-  return mac_relauncher::RelaunchApp(command_line.argv(),
-                                     wait_for_staged_update);
-}
-
-}  // namespace upgrade_util
diff --git a/chrome/browser/hid/chrome_hid_delegate.cc b/chrome/browser/hid/chrome_hid_delegate.cc
index 4b9ec91..b1e9c47 100644
--- a/chrome/browser/hid/chrome_hid_delegate.cc
+++ b/chrome/browser/hid/chrome_hid_delegate.cc
@@ -28,7 +28,7 @@
   Browser* browser = chrome::FindBrowserWithWebContents(
       content::WebContents::FromRenderFrameHost(frame));
   if (!browser) {
-    std::move(callback).Run(nullptr);
+    std::move(callback).Run(std::vector<device::mojom::HidDeviceInfoPtr>());
     return nullptr;
   }
 
diff --git a/chrome/browser/installedapp/installed_app_provider_unittest.cc b/chrome/browser/installedapp/installed_app_provider_unittest.cc
index c8c5288..2bc035a3 100644
--- a/chrome/browser/installedapp/installed_app_provider_unittest.cc
+++ b/chrome/browser/installedapp/installed_app_provider_unittest.cc
@@ -10,14 +10,12 @@
 
 class InstalledAppProviderTest : public ::testing::Test {
  public:
-  InstalledAppProviderTest() {
-    JNIEnv* env = AttachCurrentThread();
-    j_test_.Reset(Java_InstalledAppProviderTest_create(env));
-  }
+  InstalledAppProviderTest()
+      : j_test_(
+            Java_InstalledAppProviderTest_Constructor(AttachCurrentThread())) {}
 
   void SetUp() override {
-    JNIEnv* env = AttachCurrentThread();
-    Java_InstalledAppProviderTest_setUp(env, j_test_);
+    Java_InstalledAppProviderTest_setUp(AttachCurrentThread(), j_test_);
   }
 
   const base::android::ScopedJavaGlobalRef<jobject>& j_test() {
@@ -28,169 +26,4 @@
   base::android::ScopedJavaGlobalRef<jobject> j_test_;
 };
 
-TEST_F(InstalledAppProviderTest, TestOriginMissingParts) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testOriginMissingParts(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestIncognitoWithOneInstalledRelatedApp) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testIncognitoWithOneInstalledRelatedApp(
-      env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestNoRelatedApps) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testNoRelatedApps(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestOneRelatedAppNoId) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testOneRelatedAppNoId(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestOneRelatedNonAndroidApp) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testOneRelatedNonAndroidApp(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestOneRelatedAppNotInstalled) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testOneRelatedAppNotInstalled(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest,
-       TestOneRelatedAppBrokenAssetStatementsResource) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testOneRelatedAppBrokenAssetStatementsResource(
-      env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestOneRelatedAppNoAssetStatements) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testOneRelatedAppNoAssetStatements(env,
-                                                                   j_test());
-}
-
-TEST_F(InstalledAppProviderTest,
-       TestOneRelatedAppNoAssetStatementsNullMetadata) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testOneRelatedAppNoAssetStatementsNullMetadata(
-      env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestOneRelatedAppRelatedToDifferentOrigins) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testOneRelatedAppRelatedToDifferentOrigins(
-      env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestOneInstalledRelatedApp) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testOneInstalledRelatedApp(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestDynamicallyChangingUrl) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testDynamicallyChangingUrl(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestInstalledRelatedAppWithUrl) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testInstalledRelatedAppWithUrl(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestMultipleAssetStatements) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testMultipleAssetStatements(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementSyntaxError) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementSyntaxError(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementNotArray) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementNotArray(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementArrayNoObjects) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementArrayNoObjects(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementNoRelation) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementNoRelation(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementNonStandardRelation) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementNonStandardRelation(env,
-                                                                      j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementNoTarget) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementNoTarget(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementNoNamespace) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementNoNamespace(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestNonWebAssetStatement) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testNonWebAssetStatement(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementNoSite) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementNoSite(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementSiteSyntaxError) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementSiteSyntaxError(env,
-                                                                  j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementSiteMissingParts) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementSiteMissingParts(env,
-                                                                   j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestAssetStatementSiteHasPath) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testAssetStatementSiteHasPath(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestExtraInstalledApp) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testExtraInstalledApp(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestMultipleInstalledRelatedApps) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testMultipleInstalledRelatedApps(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestArtificialDelay) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testArtificialDelay(env, j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestMultipleAppsIncludingInstantApps) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testMultipleAppsIncludingInstantApps(env,
-                                                                     j_test());
-}
-
-TEST_F(InstalledAppProviderTest, TestRelatedAppsOverAllowedThreshold) {
-  JNIEnv* env = AttachCurrentThread();
-  Java_InstalledAppProviderTest_testRelatedAppsOverAllowedThreshold(env,
-                                                                    j_test());
-}
+JAVA_TESTS(InstalledAppProviderTest, j_test())
diff --git a/chrome/browser/mac/keystone_glue.h b/chrome/browser/mac/keystone_glue.h
index dba4bd124..6fc2107 100644
--- a/chrome/browser/mac/keystone_glue.h
+++ b/chrome/browser/mac/keystone_glue.h
@@ -14,7 +14,6 @@
 
 #include "base/mac/scoped_authorizationref.h"
 #import "base/mac/scoped_nsobject.h"
-#include "chrome/common/mac/staging_watcher.h"
 
 // Possible outcomes of various operations.  A version may accompany some of
 // these, but beware: a version is never required.  For statuses that can be
@@ -97,9 +96,6 @@
 
   // YES if an update was ever successfully installed by -installUpdate.
   BOOL _updateSuccessfullyInstalled;
-
-  // The object to use to watch for the staging key.
-  base::scoped_nsobject<CrStagingKeyWatcher> _stagingKeyWatcher;
 }
 
 // Return the default Keystone Glue object.
diff --git a/chrome/browser/mac/keystone_glue.mm b/chrome/browser/mac/keystone_glue.mm
index fd46fa4..8f88d64 100644
--- a/chrome/browser/mac/keystone_glue.mm
+++ b/chrome/browser/mac/keystone_glue.mm
@@ -268,22 +268,6 @@
                selector:@selector(installUpdateComplete:)
                    name:ksr::KSRegistrationStartUpdateNotification
                  object:nil];
-
-    // Set up the watcher for the staging key, for new-style updating. Use a
-    // long polling time, as this isn't user-blocking, and it doesn't poll on
-    // >=10.12 anyway.
-    const NSTimeInterval kPollingTime = 60 * 60;  // 1 hour
-    _stagingKeyWatcher.reset(
-        [[CrStagingKeyWatcher alloc] initWithPollingTime:kPollingTime]);
-    [_stagingKeyWatcher setStagingKeyChangedObserver:^(BOOL stagingKeySet) {
-      if (stagingKeySet) {
-        // If the staging key is set, then there is a process waiting for Chrome
-        // to quit, to allow it to switch out the binary on disk. Because
-        // there's nothing for Chrome to do here except restart to allow the
-        // installation, use |kAutoupdateInstalled| to ask the user to restart.
-        [self updateStatus:kAutoupdateInstalled version:nil error:nil];
-      }
-    }];
   }
 
   return self;
@@ -700,9 +684,6 @@
     // If an update was successfully installed and this object saw it happen,
     // then don't even bother comparing versions.
     status = kAutoupdateInstalled;
-  } else if ([_stagingKeyWatcher isStagingKeySet]) {
-    // If there's a staging key, then the update will happen on restart.
-    status = kAutoupdateInstalled;
   } else {
     NSString* currentVersion = base::SysUTF8ToNSString(chrome::kChromeVersion);
     if (!version) {
diff --git a/chrome/browser/mac/relauncher.h b/chrome/browser/mac/relauncher.h
index 2a877f0..66391b4 100644
--- a/chrome/browser/mac/relauncher.h
+++ b/chrome/browser/mac/relauncher.h
@@ -38,14 +38,6 @@
 
 namespace mac_relauncher {
 
-// |wait_for_staged_update| is a boolean that allows the caller to specify
-// whether to wait for a staged update. If there are no other copies of Chrome
-// running, the caller should specify |true|, and always take the staged update.
-// However, in the case where there is a system ticket and Chrome _could_ be
-// updated, but there are other copies of Chrome running, if the user chooses to
-// not disrupt the other running copies with an update, the caller should pass
-// in |false| to not wait for the update.
-
 // Relaunches the application using the helper application associated with the
 // currently running instance of Chrome in the parent browser process as the
 // executable for the relauncher process. |args| is an argv-style vector of
@@ -57,8 +49,7 @@
 // successfully. Returns true on success, although some failures can occur
 // after this function returns true if, for example, they occur within the
 // relauncher process. Returns false when the relaunch definitely failed.
-bool RelaunchApp(const std::vector<std::string>& args,
-                 bool wait_for_staged_update = true);
+bool RelaunchApp(const std::vector<std::string>& args);
 
 // Identical to RelaunchApp, but uses |helper| as the path to the relauncher
 // process, and allows additional arguments to be supplied to the relauncher
@@ -71,8 +62,7 @@
 // location's helper.
 bool RelaunchAppWithHelper(const std::string& helper,
                            const std::vector<std::string>& relauncher_args,
-                           const std::vector<std::string>& args,
-                           bool wait_for_staged_update = true);
+                           const std::vector<std::string>& args);
 
 namespace internal {
 
diff --git a/chrome/browser/mac/relauncher.mm b/chrome/browser/mac/relauncher.mm
index f6b203b..28609f7 100644
--- a/chrome/browser/mac/relauncher.mm
+++ b/chrome/browser/mac/relauncher.mm
@@ -31,7 +31,6 @@
 #include "base/strings/sys_string_conversions.h"
 #include "chrome/browser/mac/install_from_dmg.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/mac/staging_watcher.h"
 #include "content/public/common/content_paths.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/main_function_params.h"
@@ -58,19 +57,6 @@
 // the relaunched process without bringing it to the foreground.
 const char kRelauncherBackgroundArg[] = "--background";
 
-// When this argument is supplied to the relauncher process, the launcher will
-// wait for a staged update to be applied.
-const char kRelauncherWaitForUpdateArg[] = "--wait-for-update";
-
-// This argument is supplied to the relauncher process whenever the
-// kRelauncherWaitForUpdateArg argument isn't supplied. This flag is not used
-// directly by the relauncher process, but it serves two purposes. First, this
-// flag and the previous flag are used as an indication for the updating process
-// as to whether or not the relauncher will wait for the update, and second, the
-// lack of either flag indicates to the updater that the relauncher process is
-// unaware of the updater process.
-const char kRelauncherDontWaitForUpdateArg[] = "--dont-wait-for-update";
-
 // The beginning of the "process serial number" argument that Launch Services
 // sometimes inserts into command lines. A process serial number is only valid
 // for a single process, so any PSN arguments will be stripped from command
@@ -87,8 +73,7 @@
 
 }  // namespace
 
-bool RelaunchApp(const std::vector<std::string>& args,
-                 bool wait_for_staged_update) {
+bool RelaunchApp(const std::vector<std::string>& args) {
   // Use the currently-running application's helper process. The automatic
   // update feature is careful to leave the currently-running version alone,
   // so this is safe even if the relaunch is the result of an update having
@@ -102,14 +87,12 @@
   }
 
   std::vector<std::string> relauncher_args;
-  return RelaunchAppWithHelper(child_path.value(), relauncher_args, args,
-                               wait_for_staged_update);
+  return RelaunchAppWithHelper(child_path.value(), relauncher_args, args);
 }
 
 bool RelaunchAppWithHelper(const std::string& helper,
                            const std::vector<std::string>& relauncher_args,
-                           const std::vector<std::string>& args,
-                           bool wait_for_staged_update) {
+                           const std::vector<std::string>& args) {
   std::vector<std::string> relaunch_args;
   relaunch_args.push_back(helper);
   relaunch_args.push_back(RelauncherTypeArg());
@@ -120,11 +103,6 @@
     relaunch_args.push_back(kRelauncherBackgroundArg);
   }
 
-  if (wait_for_staged_update)
-    relaunch_args.push_back(kRelauncherWaitForUpdateArg);
-  else
-    relaunch_args.push_back(kRelauncherDontWaitForUpdateArg);
-
   relaunch_args.insert(relaunch_args.end(),
                        relauncher_args.begin(), relauncher_args.end());
 
@@ -308,7 +286,6 @@
     // Figure out what to execute, what arguments to pass it, and whether to
     // start it in the background.
     bool background = false;
-    bool wait_for_staged_update = false;
     bool in_relaunch_args = false;
     std::string dmg_bsd_device_name;
     bool seen_relaunch_executable = false;
@@ -328,8 +305,6 @@
           in_relaunch_args = true;
         } else if (arg == kRelauncherBackgroundArg) {
           background = true;
-        } else if (arg == kRelauncherWaitForUpdateArg) {
-          wait_for_staged_update = true;
         } else if (arg.compare(0, relauncher_dmg_device_arg.size(),
                                relauncher_dmg_device_arg) == 0) {
           dmg_bsd_device_name.assign(
@@ -359,14 +334,6 @@
       return 1;
     }
 
-    // If an update is staged but not yet installed, wait for it to be
-    // installed.
-    if (wait_for_staged_update) {
-      base::scoped_nsobject<CrStagingKeyWatcher> watcher(
-          [[CrStagingKeyWatcher alloc] initWithPollingTime:0.5]);
-      [watcher waitForStagingKeyToClear];
-    }
-
     NSString* path = base::SysUTF8ToNSString(relaunch_executable);
     base::scoped_nsobject<NSURL> url([[NSURL alloc] initFileURLWithPath:path]);
     NSDictionary* configuration =
diff --git a/chrome/browser/media/webrtc/desktop_media_picker_controller.cc b/chrome/browser/media/webrtc/desktop_media_picker_controller.cc
index e026c204..672acc22 100644
--- a/chrome/browser/media/webrtc/desktop_media_picker_controller.cc
+++ b/chrome/browser/media/webrtc/desktop_media_picker_controller.cc
@@ -98,12 +98,17 @@
 
   picker_->Show(params_, std::move(source_lists_),
                 base::Bind(&DesktopMediaPickerController::OnPickerDialogResults,
-                           base::Unretained(this), std::string()));
+                           // A weak pointer is used here, because although
+                           // |picker_| can't outlive this object, it can
+                           // schedule this callback to be invoked
+                           // asynchronously after it has potentially been
+                           // destroyed.
+                           weak_factory_.GetWeakPtr(), std::string()));
 }
 
 void DesktopMediaPickerController::OnPickerDialogResults(
     const std::string& err,
     content::DesktopMediaID source) {
-  DCHECK(done_callback_);
-  std::move(done_callback_).Run(err, source);
+  if (done_callback_)
+    std::move(done_callback_).Run(err, source);
 }
diff --git a/chrome/browser/media/webrtc/desktop_media_picker_controller.h b/chrome/browser/media/webrtc/desktop_media_picker_controller.h
index 5bdd0ecd..5cf768b 100644
--- a/chrome/browser/media/webrtc/desktop_media_picker_controller.h
+++ b/chrome/browser/media/webrtc/desktop_media_picker_controller.h
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/media/webrtc/desktop_media_picker.h"
 #include "content/public/browser/desktop_media_id.h"
 #include "content/public/browser/web_contents_observer.h"
@@ -86,6 +87,7 @@
   std::vector<std::unique_ptr<DesktopMediaList>> source_lists_;
   std::unique_ptr<DesktopMediaPicker> picker_;
   DesktopMediaPickerFactory* picker_factory_;
+  base::WeakPtrFactory<DesktopMediaPickerController> weak_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_MEDIA_WEBRTC_DESKTOP_MEDIA_PICKER_CONTROLLER_H_
diff --git a/chrome/browser/metrics/first_web_contents_profiler.cc b/chrome/browser/metrics/first_web_contents_profiler.cc
index 92bc9105..a3cca88 100644
--- a/chrome/browser/metrics/first_web_contents_profiler.cc
+++ b/chrome/browser/metrics/first_web_contents_profiler.cc
@@ -62,8 +62,7 @@
 
 class FirstWebContentsProfiler : public content::WebContentsObserver {
  public:
-  FirstWebContentsProfiler(content::WebContents* web_contents,
-                           startup_metric_utils::WebContentsWorkload workload);
+  explicit FirstWebContentsProfiler(content::WebContents* web_contents);
 
  private:
   ~FirstWebContentsProfiler() override = default;
@@ -80,8 +79,6 @@
   // Logs |finish_reason| to UMA and deletes this FirstWebContentsProfiler.
   void FinishedCollectingMetrics(FinishReason finish_reason);
 
-  const startup_metric_utils::WebContentsWorkload workload_;
-
   // The first NavigationHandle id observed by this.
   int64_t first_navigation_id_ = kInvalidNavigationId;
 
@@ -92,9 +89,8 @@
 };
 
 FirstWebContentsProfiler::FirstWebContentsProfiler(
-    content::WebContents* web_contents,
-    startup_metric_utils::WebContentsWorkload workload)
-    : content::WebContentsObserver(web_contents), workload_(workload) {}
+    content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents) {}
 
 void FirstWebContentsProfiler::DidStartNavigation(
     content::NavigationHandle* navigation_handle) {
@@ -159,7 +155,7 @@
   did_finish_first_navigation_ = true;
 
   startup_metric_utils::RecordFirstWebContentsMainNavigationStart(
-      navigation_handle->NavigationStart(), workload_);
+      navigation_handle->NavigationStart());
   startup_metric_utils::RecordFirstWebContentsMainNavigationFinished(
       base::TimeTicks::Now());
 }
@@ -205,8 +201,6 @@
 namespace metrics {
 
 void BeginFirstWebContentsProfiling() {
-  using startup_metric_utils::WebContentsWorkload;
-
   const BrowserList* browser_list = BrowserList::GetInstance();
 
   content::WebContents* visible_contents = nullptr;
@@ -229,14 +223,9 @@
     return;
   }
 
-  const bool single_tab = browser_list->size() == 1 &&
-                          browser_list->get(0)->tab_strip_model()->count() == 1;
-
   // FirstWebContentsProfiler owns itself and is also bound to
   // |visible_contents|'s lifetime by observing WebContentsDestroyed().
-  new FirstWebContentsProfiler(visible_contents,
-                               single_tab ? WebContentsWorkload::SINGLE_TAB
-                                          : WebContentsWorkload::MULTI_TABS);
+  new FirstWebContentsProfiler(visible_contents);
 }
 
 }  // namespace metrics
diff --git a/chrome/browser/navigation_predictor/search_engine_preconnector.cc b/chrome/browser/navigation_predictor/search_engine_preconnector.cc
index 63edf82..2ad6284 100644
--- a/chrome/browser/navigation_predictor/search_engine_preconnector.cc
+++ b/chrome/browser/navigation_predictor/search_engine_preconnector.cc
@@ -123,6 +123,9 @@
   auto* loading_predictor = predictors::LoadingPredictorFactory::GetForProfile(
       Profile::FromBrowserContext(browser_context_));
 
+  if (!loading_predictor)
+    return;
+
   loading_predictor->preconnect_manager()->StartPreconnectUrl(
       preconnect_url, true /* allow_credentials */,
       net::NetworkIsolationKey(url::Origin::Create(preconnect_url),
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
index d6827ea7..f59af56 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -240,17 +240,11 @@
   // Get the current time, considered to be when this update occurred.
   base::TimeTicks current_time = clock_->NowTicks();
 
-  FrameData::InteractiveStatus interactive_status =
-      time_interactive_.is_null()
-          ? FrameData::InteractiveStatus::kPreInteractive
-          : FrameData::InteractiveStatus::kPostInteractive;
-  aggregate_frame_data_->UpdateCpuUsage(current_time, timing.task_time,
-                                        interactive_status);
+  aggregate_frame_data_->UpdateCpuUsage(current_time, timing.task_time);
 
   FrameData* ancestor_data = FindFrameData(subframe_rfh->GetFrameTreeNodeId());
   if (ancestor_data) {
-    ancestor_data->UpdateCpuUsage(current_time, timing.task_time,
-                                  interactive_status);
+    ancestor_data->UpdateCpuUsage(current_time, timing.task_time);
     MaybeTriggerHeavyAdIntervention(subframe_rfh, ancestor_data);
   }
 }
@@ -402,8 +396,6 @@
   if (timing.interactive_timing->interactive) {
     time_interactive_ = GetDelegate().GetNavigationStart() +
                         *timing.interactive_timing->interactive;
-    pre_interactive_duration_ =
-        GetDelegate().GetVisibilityTracker().GetForegroundDuration();
     page_ad_bytes_at_interactive_ = aggregate_frame_data_->ad_network_bytes();
   }
 }
@@ -634,6 +626,8 @@
       builder.SetAdBytesPerSecond(ad_kbps_from_commit);
     }
   }
+
+  // TODO(https://crbug.com/1040613): Consider removing TTI ad metrics.
   if (!time_interactive_.is_null()) {
     int time_since_interactive =
         (current_time - time_interactive_).InMicroseconds();
@@ -677,17 +671,13 @@
               .num_frames == 0)
     return;
 
-  // Get the relevant durations, set pre-interactive if the page never hit it.
   base::TimeDelta total_duration =
       GetDelegate().GetVisibilityTracker().GetForegroundDuration();
-  if (time_interactive_.is_null())
-    pre_interactive_duration_ = total_duration;
-
-  base::TimeDelta post_interactive_duration =
-      total_duration - pre_interactive_duration_;
   DCHECK(total_duration >= base::TimeDelta());
-  DCHECK(pre_interactive_duration_ >= base::TimeDelta());
-  DCHECK(post_interactive_duration >= base::TimeDelta());
+
+  // Do not record for pages with duration less than a millisecond.
+  if (total_duration.InMilliseconds() == 0)
+    return;
 
   // Only record cpu usage aggregate data for the AnyVisibility suffix as these
   // numbers do not change for different visibility types.
@@ -695,37 +685,18 @@
       FrameData::FrameVisibility::kAnyVisibility;
 
   // Record the aggregate data, which is never considered activated.
-  base::TimeDelta task_duration_pre =
-      aggregate_frame_data_->GetInteractiveCpuUsage(
-          FrameData::InteractiveStatus::kPreInteractive);
-  base::TimeDelta task_duration_post =
-      aggregate_frame_data_->GetInteractiveCpuUsage(
-          FrameData::InteractiveStatus::kPostInteractive);
-  base::TimeDelta task_duration_total = task_duration_pre + task_duration_post;
-  if (total_duration.InMilliseconds() > 0) {
-    ADS_HISTOGRAM("Cpu.AdFrames.Aggregate.TotalUsage", PAGE_LOAD_HISTOGRAM,
+  ADS_HISTOGRAM(
+      "Cpu.AdFrames.Aggregate.TotalUsage", PAGE_LOAD_HISTOGRAM, visibility,
+      aggregate_ad_info_by_visibility_[static_cast<int>(visibility)].cpu_time);
+  ADS_HISTOGRAM("Cpu.FullPage.TotalUsage", PAGE_LOAD_HISTOGRAM, visibility,
+                aggregate_frame_data_->GetTotalCpuUsage());
+  ADS_HISTOGRAM("Cpu.FullPage.PeakWindowedPercent", UMA_HISTOGRAM_PERCENTAGE,
+                visibility, aggregate_frame_data_->peak_windowed_cpu_percent());
+  if (aggregate_frame_data_->peak_window_start_time()) {
+    ADS_HISTOGRAM("Cpu.FullPage.PeakWindowStartTime", PAGE_LOAD_HISTOGRAM,
                   visibility,
-                  aggregate_ad_info_by_visibility_[static_cast<int>(visibility)]
-                      .cpu_time);
-    ADS_HISTOGRAM("Cpu.FullPage.TotalUsage", PAGE_LOAD_HISTOGRAM, visibility,
-                  task_duration_total);
-    ADS_HISTOGRAM("Cpu.FullPage.PeakWindowedPercent", UMA_HISTOGRAM_PERCENTAGE,
-                  visibility,
-                  aggregate_frame_data_->peak_windowed_cpu_percent());
-    if (aggregate_frame_data_->peak_window_start_time()) {
-      ADS_HISTOGRAM("Cpu.FullPage.PeakWindowStartTime", PAGE_LOAD_HISTOGRAM,
-                    visibility,
-                    aggregate_frame_data_->peak_window_start_time().value() -
-                        GetDelegate().GetNavigationStart());
-    }
-  }
-  if (pre_interactive_duration_.InMilliseconds() > 0) {
-    ADS_HISTOGRAM("Cpu.FullPage.TotalUsage.PreInteractive", PAGE_LOAD_HISTOGRAM,
-                  visibility, task_duration_pre);
-  }
-  if (post_interactive_duration.InMilliseconds() > 0) {
-    ADS_HISTOGRAM("Cpu.FullPage.TotalUsage.PostInteractive",
-                  PAGE_LOAD_HISTOGRAM, visibility, task_duration_post);
+                  aggregate_frame_data_->peak_window_start_time().value() -
+                      GetDelegate().GetNavigationStart());
   }
 }
 
@@ -818,16 +789,13 @@
 
 void AdsPageLoadMetricsObserver::RecordPerFrameHistogramsForCpuUsage(
     const FrameData& ad_frame_data) {
-  // Get the relevant durations, set pre-interactive if the page never hit it.
   base::TimeDelta total_duration =
       GetDelegate().GetVisibilityTracker().GetForegroundDuration();
-  if (time_interactive_.is_null())
-    pre_interactive_duration_ = total_duration;
-  base::TimeDelta post_interactive_duration =
-      total_duration - pre_interactive_duration_;
   DCHECK(total_duration >= base::TimeDelta());
-  DCHECK(pre_interactive_duration_ >= base::TimeDelta());
-  DCHECK(post_interactive_duration >= base::TimeDelta());
+
+  // Do not record for pages with small durations.
+  if (total_duration.InMilliseconds() == 0)
+    return;
 
   // This aggregate gets reported regardless of whether the frame used bytes.
   aggregate_ad_info_by_visibility_
@@ -844,8 +812,7 @@
     // (measured only for the unactivated period).  Only reported if there was a
     // relevant unactivated period.
     if ((ad_frame_data.user_activation_status() ==
-             FrameData::UserActivationStatus::kNoActivation &&
-         total_duration.InMilliseconds() > 0) ||
+         FrameData::UserActivationStatus::kNoActivation) ||
         (ad_frame_data.user_activation_status() ==
              FrameData::UserActivationStatus::kReceivedActivation &&
          ad_frame_data.pre_activation_foreground_duration().InMilliseconds() >
@@ -863,26 +830,9 @@
 
     if (ad_frame_data.user_activation_status() ==
         FrameData::UserActivationStatus::kNoActivation) {
-      base::TimeDelta task_duration_pre = ad_frame_data.GetInteractiveCpuUsage(
-          FrameData::InteractiveStatus::kPreInteractive);
-      base::TimeDelta task_duration_post = ad_frame_data.GetInteractiveCpuUsage(
-          FrameData::InteractiveStatus::kPostInteractive);
-      base::TimeDelta task_duration_total =
-          task_duration_pre + task_duration_post;
-      if (total_duration.InMilliseconds() > 0) {
-        ADS_HISTOGRAM("Cpu.AdFrames.PerFrame.TotalUsage.Unactivated",
-                      PAGE_LOAD_HISTOGRAM, visibility, task_duration_total);
-      }
-      if (pre_interactive_duration_.InMilliseconds() > 0) {
-        ADS_HISTOGRAM(
-            "Cpu.AdFrames.PerFrame.TotalUsage.Unactivated.PreInteractive",
-            PAGE_LOAD_HISTOGRAM, visibility, task_duration_pre);
-      }
-      if (post_interactive_duration.InMilliseconds() > 0) {
-        ADS_HISTOGRAM(
-            "Cpu.AdFrames.PerFrame.TotalUsage.Unactivated.PostInteractive",
-            PAGE_LOAD_HISTOGRAM, visibility, task_duration_post);
-      }
+      ADS_HISTOGRAM("Cpu.AdFrames.PerFrame.TotalUsage.Unactivated",
+                    PAGE_LOAD_HISTOGRAM, visibility,
+                    ad_frame_data.GetTotalCpuUsage());
     } else {
       base::TimeDelta task_duration_pre = ad_frame_data.GetActivationCpuUsage(
           FrameData::UserActivationStatus::kNoActivation);
@@ -894,10 +844,9 @@
           ad_frame_data.pre_activation_foreground_duration();
       base::TimeDelta post_activation_duration =
           total_duration - pre_activation_duration;
-      if (total_duration.InMilliseconds() > 0) {
-        ADS_HISTOGRAM("Cpu.AdFrames.PerFrame.TotalUsage.Activated",
-                      PAGE_LOAD_HISTOGRAM, visibility, task_duration_total);
-      }
+      ADS_HISTOGRAM("Cpu.AdFrames.PerFrame.TotalUsage.Activated",
+                    PAGE_LOAD_HISTOGRAM, visibility, task_duration_total);
+
       if (pre_activation_duration.InMilliseconds() > 0) {
         ADS_HISTOGRAM(
             "Cpu.AdFrames.PerFrame.TotalUsage.Activated.PreActivation",
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
index 22a5fd4..53cbe0a 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
@@ -231,9 +231,6 @@
   // Time the page was observed to be interactive.
   base::TimeTicks time_interactive_;
 
-  // Duration before |time_interactive_| during which the page was foregrounded.
-  base::TimeDelta pre_interactive_duration_;
-
   // Total ad bytes loaded by the page since it was observed to be interactive.
   size_t page_ad_bytes_at_interactive_ = 0u;
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index 3ea17fb..e0ac809f 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -424,16 +424,6 @@
         ->NotifyAdSubframeDetected(render_frame_host);
   }
 
-  // Set the interactive status of the main frame.
-  void OnMainFrameInteractive(base::TimeDelta frame_interactive_offset) {
-    auto timing = page_load_metrics::mojom::PageLoadTimingPtr(base::in_place);
-    page_load_metrics::InitPageLoadTimingForTest(timing.get());
-    timing->interactive_timing->interactive =
-        base::Optional<base::TimeDelta>(frame_interactive_offset);
-    // Call directly since main frame timing updates may be delayed.
-    ads_observer_->OnPageInteractive(*timing);
-  }
-
   void OnCpuTimingUpdate(RenderFrameHost* render_frame_host,
                          base::TimeDelta cpu_time_spent) {
     page_load_metrics::mojom::CpuTiming cpu_timing(cpu_time_spent);
@@ -568,17 +558,19 @@
                           int post_time) {
     int total_task_time = pre_task_time + post_task_time;
     int total_time = pre_time + post_time;
-    std::string suffix = type == "Activated" ? "Activation" : "Interactive";
     type = type.empty() ? "" : "." + type;
 
     CheckSpecificCpuHistogram(SuffixedHistogram(prefix + ".TotalUsage" + type),
                               total_task_time, total_time);
-    CheckSpecificCpuHistogram(
-        SuffixedHistogram(prefix + ".TotalUsage" + type + ".Pre" + suffix),
-        pre_task_time, pre_time);
-    CheckSpecificCpuHistogram(
-        SuffixedHistogram(prefix + ".TotalUsage" + type + ".Post" + suffix),
-        post_task_time, post_time);
+
+    if (type == "Activated") {
+      CheckSpecificCpuHistogram(
+          SuffixedHistogram(prefix + ".TotalUsage" + type + ".PreActivation"),
+          pre_task_time, pre_time);
+      CheckSpecificCpuHistogram(
+          SuffixedHistogram(prefix + ".TotalUsage" + type + ".PostActivation"),
+          post_task_time, post_time);
+    }
   }
 
  private:
@@ -1232,7 +1224,7 @@
       SuffixedHistogram("Cpu.FullPage.TotalUsage"), 1000, 1);
 }
 
-TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsWindowed) {
+TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsWindowUnactivated) {
   OverrideVisibilityTrackerWithMockClock();
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
   RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
@@ -1341,7 +1333,7 @@
       SuffixedHistogram("Cpu.FullPage.PeakWindowStartTime"), 12000, 1);
 }
 
-TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetrics) {
+TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsNoActivation) {
   OverrideVisibilityTrackerWithMockClock();
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
   RenderFrameHost* non_ad_frame =
@@ -1355,10 +1347,6 @@
   OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500));
   OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500));
 
-  // Set the main frame as interactive after 2 seconds.
-  AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000));
-  OnMainFrameInteractive(base::TimeDelta::FromMilliseconds(2000));
-
   // Do some more work on the ad frame.
   OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000));
 
@@ -1366,7 +1354,7 @@
   OnCpuTimingUpdate(main_frame, base::TimeDelta::FromMilliseconds(500));
 
   // Navigate away after 4 seconds.
-  AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000));
+  AdvancePageDuration(base::TimeDelta::FromMilliseconds(4000));
   NavigateFrame(kNonAdUrl, main_frame);
 
   // Check the cpu histograms.
@@ -1412,15 +1400,11 @@
   OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500));
   OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500));
 
-  // Set the main frame as interactive after 2 seconds.
-  AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000));
-  OnMainFrameInteractive(base::TimeDelta::FromMilliseconds(2000));
-
   // Do some more work on the ad frame.
   OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000));
 
   // Set the page as hidden after 3.5 seconds.
-  AdvancePageDuration(base::TimeDelta::FromMilliseconds(1500));
+  AdvancePageDuration(base::TimeDelta::FromMilliseconds(3500));
   OnHidden();
 
   // Do some more work on the main frame, shouldn't count to total.
@@ -1469,18 +1453,11 @@
   ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10);
 
   // Perform some updates on ad and non-ad frames.
-  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500));
+  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000));
   OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500));
 
-  // Set the main frame as interactive after 2 seconds.
-  AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000));
-  OnMainFrameInteractive(base::TimeDelta::FromMilliseconds(2000));
-
-  // Do some more work on the ad frame.
-  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500));
-
-  // Set the frame as interactive after 2.5 seconds
-  AdvancePageDuration(base::TimeDelta::FromMilliseconds(500));
+  // Set the frame as activated after 2.5 seconds
+  AdvancePageDuration(base::TimeDelta::FromMilliseconds(2500));
   TriggerFirstUserActivation(ad_frame);
 
   // Do some more work on the main frame.
@@ -1538,21 +1515,11 @@
   OnHidden();
 
   // Perform some updates on ad and non-ad frames.
-  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500));
-  OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500));
-
-  // Set the main frame as interactive after 2 seconds.
-  AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000));
-  OnMainFrameInteractive(base::TimeDelta::FromMilliseconds(2000));
-
-  // Do some more work on the ad frame.
-  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000));
-
-  // Do some more work on the main frame.
-  OnCpuTimingUpdate(main_frame, base::TimeDelta::FromMilliseconds(500));
+  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1500));
+  OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(1000));
 
   // Navigate away after 4 seconds.
-  AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000));
+  AdvancePageDuration(base::TimeDelta::FromMilliseconds(4000));
   NavigateFrame(kNonAdUrl, main_frame);
 
   // Ensure that all metrics are zero.
@@ -1585,45 +1552,6 @@
       ukm::builders::AdFrameLoad::kTiming_PreActivationForegroundDurationName));
 }
 
-TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsNoInteractive) {
-  OverrideVisibilityTrackerWithMockClock();
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* non_ad_frame =
-      CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
-  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
-
-  // Add some data to the ad frame so it get reported.
-  ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10);
-
-  // Perform some updates on ad and non-ad frames.
-  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500));
-  OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500));
-
-  // Navigate away after 2 seconds.
-  AdvancePageDuration(base::TimeDelta::FromMilliseconds(2000));
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  // Check the cpu histograms.
-  CheckCpuHistograms("Cpu.FullPage", "", /*pre_tasks=*/500 + 500,
-                     /*pre_time=*/2000, /*post_tasks=*/0, /*post_time=*/0);
-  CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Activated", 0, 0, 0, 0);
-  CheckCpuHistograms("Cpu.AdFrames.PerFrame", "Unactivated", /*pre_tasks=*/500,
-                     /*pre_time=*/2000, /*post_tasks=*/0, /*post_time=*/0);
-  histogram_tester().ExpectUniqueSample(
-      SuffixedHistogram("Cpu.AdFrames.Aggregate.TotalUsage"),
-      /*total_task_time=*/500, 1);
-
-  auto entries = test_ukm_recorder().GetEntriesByName(
-      ukm::builders::AdFrameLoad::kEntryName);
-  EXPECT_EQ(1u, entries.size());
-  test_ukm_recorder().ExpectEntryMetric(
-      entries.front(), ukm::builders::AdFrameLoad::kCpuTime_TotalName, 500);
-  test_ukm_recorder().ExpectEntryMetric(
-      entries.front(),
-      ukm::builders::AdFrameLoad::kCpuTime_PeakWindowedPercentName,
-      100 * 500 / 30000);
-}
-
 TEST_F(AdsPageLoadMetricsObserverTest, TestCpuTimingMetricsShortTimeframes) {
   OverrideVisibilityTrackerWithMockClock();
   RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
@@ -1635,19 +1563,10 @@
   ResourceDataUpdate(ad_frame, ResourceCached::kNotCached, 10);
 
   // Perform some updates on ad and non-ad frames.
-  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(500));
-
-  // Set interactive after 1 microsecond.
-  AdvancePageDuration(base::TimeDelta::FromMicroseconds(1));
-  OnMainFrameInteractive(base::TimeDelta::FromMicroseconds(1));
-
-  // Do some more work on the ad frame.
-  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1000));
-
-  // Do some more work on the main frame.
+  OnCpuTimingUpdate(ad_frame, base::TimeDelta::FromMilliseconds(1500));
   OnCpuTimingUpdate(non_ad_frame, base::TimeDelta::FromMilliseconds(500));
 
-  // Navigate away after 2 microseconds.
+  // Navigate away after 1 microsecond.
   AdvancePageDuration(base::TimeDelta::FromMicroseconds(1));
   NavigateFrame(kNonAdUrl, main_frame);
 
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
index 26e2c03f..6feadf8 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
@@ -150,10 +150,8 @@
 }
 
 void FrameData::UpdateCpuUsage(base::TimeTicks update_time,
-                               base::TimeDelta update,
-                               InteractiveStatus interactive) {
+                               base::TimeDelta update) {
   // Update the overall usage for all of the relevant buckets.
-  cpu_by_interactive_period_[static_cast<size_t>(interactive)] += update;
   cpu_by_activation_period_[static_cast<size_t>(user_activation_status_)] +=
       update;
 
@@ -204,10 +202,6 @@
   return true;
 }
 
-base::TimeDelta FrameData::GetInteractiveCpuUsage(
-    InteractiveStatus status) const {
-  return cpu_by_interactive_period_[static_cast<int>(status)];
-}
 
 base::TimeDelta FrameData::GetActivationCpuUsage(
     UserActivationStatus status) const {
@@ -216,7 +210,7 @@
 
 base::TimeDelta FrameData::GetTotalCpuUsage() const {
   base::TimeDelta total_cpu_time;
-  for (base::TimeDelta cpu_time : cpu_by_interactive_period_)
+  for (base::TimeDelta cpu_time : cpu_by_activation_period_)
     total_cpu_time += cpu_time;
   return total_cpu_time;
 }
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
index d34a4c0..f5d57e8 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
@@ -77,14 +77,6 @@
     kMaxValue = kReceivedActivation,
   };
 
-  // The interactive states for the main page, which describe when the page
-  // has reached interactive state, or finished loading.
-  enum class InteractiveStatus {
-    kPreInteractive = 0,
-    kPostInteractive = 1,
-    kMaxValue = kPostInteractive,
-  };
-
   // High level categories of mime types for resources loaded by the frame.
   enum class ResourceMimeType {
     kJavascript = 0,
@@ -145,10 +137,9 @@
   // Sets the display state of the frame and updates its visibility state.
   void SetDisplayState(bool is_display_none);
 
-  // Add the cpu |update| appropriately given the page |interactive| status.
-  void UpdateCpuUsage(base::TimeTicks update_time,
-                      base::TimeDelta update,
-                      InteractiveStatus interactive);
+  // Update CPU usage information with the timing |update| that was received at
+  // |update_time|.
+  void UpdateCpuUsage(base::TimeTicks update_time, base::TimeDelta update);
 
   // Returns whether the heavy ad intervention was triggered on this frame.
   // This intervention is triggered when the frame is considered heavy, has not
@@ -156,9 +147,6 @@
   // returns true the first time the criteria is met, and false afterwards.
   bool MaybeTriggerHeavyAdIntervention();
 
-  // Get the cpu usage for the appropriate interactive period.
-  base::TimeDelta GetInteractiveCpuUsage(InteractiveStatus status) const;
-
   // Get the cpu usage for the appropriate activation period.
   base::TimeDelta GetActivationCpuUsage(UserActivationStatus status) const;
 
@@ -276,12 +264,6 @@
   size_t ad_bytes_by_mime_[static_cast<size_t>(ResourceMimeType::kMaxValue) +
                            1] = {0};
 
-  // Time spent by the frame in the cpu before and after interactive.
-  base::TimeDelta cpu_by_interactive_period_[static_cast<size_t>(
-                                                 InteractiveStatus::kMaxValue) +
-                                             1] = {base::TimeDelta(),
-                                                   base::TimeDelta()};
-
   // Time spent by the frame in the cpu before and after activation.
   base::TimeDelta cpu_by_activation_period_
       [static_cast<size_t>(UserActivationStatus::kMaxValue) + 1] = {
diff --git a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.cc
index 7b75886..46b67b6 100644
--- a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.cc
+++ b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.cc
@@ -50,6 +50,10 @@
   committed_host_ = navigation_handle->GetWebContents()
                         ->GetLastCommittedURL()
                         .HostNoBrackets();
+  committed_origin_ = navigation_handle->GetWebContents()
+                          ->GetLastCommittedURL()
+                          .GetOrigin()
+                          .spec();
   return CONTINUE_OBSERVING;
 }
 
@@ -65,6 +69,7 @@
   if (data_reduction_proxy_settings &&
       data_reduction_proxy_settings->data_reduction_proxy_service()) {
     DCHECK(!committed_host_.empty());
+    DCHECK(!committed_origin_.empty());
     int64_t received_data_length = 0;
     int64_t data_reduction_proxy_bytes_saved = 0;
     for (auto const& resource : resources) {
@@ -83,6 +88,14 @@
             (resource->data_reduction_proxy_compression_ratio_estimate - 1.0);
       }
     }
+    double origin_save_data_savings =
+        data_reduction_proxy_settings->data_reduction_proxy_service()
+            ->GetSaveDataSavingsPercentEstimate(committed_origin_);
+    if (origin_save_data_savings) {
+      data_reduction_proxy_bytes_saved +=
+          received_data_length * origin_save_data_savings / 100;
+    }
+
     data_reduction_proxy_settings->data_reduction_proxy_service()
         ->UpdateDataUseForHost(
             received_data_length,
diff --git a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h
index b64c61c..d675d84 100644
--- a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h
+++ b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer.h
@@ -42,6 +42,7 @@
       const std::string& mime_type) const override;
 
   std::string committed_host_;
+  std::string committed_origin_;
 
   // The browser context this navigation is operating in.
   content::BrowserContext* browser_context_ = nullptr;
diff --git a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer_browsertest.cc
index 2bb66d6..5fbbc50 100644
--- a/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer_browsertest.cc
+++ b/chrome/browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer_browsertest.cc
@@ -63,18 +63,6 @@
 // Browser tests with Lite mode not enabled.
 class DataSaverSiteBreakdownMetricsObserverBrowserTestBase
     : public InProcessBrowserTest {
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{features::kLazyImageLoading,
-          {{"automatic-lazy-load-images-enabled", "true"},
-           {"enable-lazy-load-images-metadata-fetch", "true"},
-           {"lazy_image_first_k_fully_load", "4G:0"}}},
-         {features::kLazyFrameLoading,
-          {{"automatic-lazy-load-frames-enabled", "true"}}}},
-        {});
-    InProcessBrowserTest::SetUp();
-  }
-
  protected:
   // Gets the data usage recorded against the host the embedded server runs on.
   uint64_t GetDataUsage(const std::string& host) {
@@ -114,9 +102,6 @@
         ->PostTask(FROM_HERE, run_loop.QuitClosure());
     run_loop.Run();
   }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // Browser tests with Lite mode enabled.
@@ -137,14 +122,39 @@
         data_reduction_proxy::switches::kEnableDataReductionProxy);
     command_line->AppendSwitch(previews::switches::kIgnorePreviewsBlacklist);
   }
+};
 
-  void ScrollToAndWaitForScroll(unsigned int scroll_offset) {
-    ASSERT_TRUE(content::ExecuteScript(
-        browser()->tab_strip_model()->GetActiveWebContents(),
-        base::StringPrintf("window.scrollTo(0, %d);", scroll_offset)));
-    content::RenderFrameSubmissionObserver observer(
-        browser()->tab_strip_model()->GetActiveWebContents());
-    observer.WaitForScrollOffset(gfx::Vector2dF(0, scroll_offset));
+class LazyLoadWithoutLiteModeBrowserTest
+    : public DataSaverSiteBreakdownMetricsObserverBrowserTestBase {
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        {{features::kLazyImageLoading,
+          {{"automatic-lazy-load-images-enabled", "true"},
+           {"enable-lazy-load-images-metadata-fetch", "true"},
+           {"lazy_image_first_k_fully_load", "4G:0"}}},
+         {features::kLazyFrameLoading,
+          {{"automatic-lazy-load-frames-enabled", "true"}}}},
+        {});
+    DataSaverSiteBreakdownMetricsObserverBrowserTestBase::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+class LazyLoadBrowserTest
+    : public DataSaverSiteBreakdownMetricsObserverBrowserTest {
+ public:
+  void SetUp() override {
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        {{features::kLazyImageLoading,
+          {{"automatic-lazy-load-images-enabled", "true"},
+           {"enable-lazy-load-images-metadata-fetch", "true"},
+           {"lazy_image_first_k_fully_load", "4G:0"}}},
+         {features::kLazyFrameLoading,
+          {{"automatic-lazy-load-frames-enabled", "true"}}}},
+        {});
+    DataSaverSiteBreakdownMetricsObserverBrowserTest::SetUp();
   }
 
   // Navigates to |url| waiting until |expected_resources| are received and then
@@ -211,8 +221,126 @@
     return GetDataSavings(test_url.HostNoBrackets()) -
            data_savings_before_navigation;
   }
+
+ private:
+  void ScrollToAndWaitForScroll(unsigned int scroll_offset) {
+    ASSERT_TRUE(content::ExecuteScript(
+        browser()->tab_strip_model()->GetActiveWebContents(),
+        base::StringPrintf("window.scrollTo(0, %d);", scroll_offset)));
+    content::RenderFrameSubmissionObserver observer(
+        browser()->tab_strip_model()->GetActiveWebContents());
+    observer.WaitForScrollOffset(gfx::Vector2dF(0, scroll_offset));
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+struct SaveDataSavingsEstimate {
+  std::string host;
+  std::string data_savings_percent;
+};
+
+struct SaveDataSingleTestCase {
+  std::string test_host;
+  double expected_savings_percent;
+};
+
+std::string ConvertSaveDataSavingsEstimateToJson(
+    std::vector<SaveDataSavingsEstimate> estimates,
+    const net::EmbeddedTestServer& embedded_test_server) {
+  std::string origin_savings_estimate_json;
+  for (const auto& estimate : estimates) {
+    base::StringAppendF(&origin_savings_estimate_json, "\"%s\": %s,",
+                        embedded_test_server.GetURL(estimate.host, "/")
+                            .GetOrigin()
+                            .spec()
+                            .c_str(),
+                        estimate.data_savings_percent.c_str());
+  }
+  origin_savings_estimate_json.pop_back();
+  return "{" + origin_savings_estimate_json + "}";
+}
+
+struct SaveDataTestCase {
+  // One of the origin_savings_estimate fields will be populated.
+  std::string origin_savings_estimate_raw_json;
+  std::vector<SaveDataSavingsEstimate> origin_savings_estimate_list;
+
+  std::vector<SaveDataSingleTestCase> tests;
+} kSaveDataTestCases[] = {
+    // No savings recorded without field trial config.
+    {"", {}, {{"foo.com", 0.0}}},
+
+    // No savings recorded with invalid field trial parameter.
+    {"invalid json", {}, {{"foo.com", 0.0}}},
+
+    {"",
+     {{"saving.com", "10"}},
+     {{{"saving.com", 10.0}, {"notsaving.com", 0.0}}}},
+
+    {"",
+     {{"saving.com", "20"}, {"savingfloatingpoint.edu", "15.7"}},
+     {{{"saving.com", 20.0},
+       {"savingfloatingpoint.edu", 15.7},
+       {"notsaving.com", 0.0}}}}
+
+};
+
+// Browser tests with Lite mode not enabled.
+class SaveDataSavingsEstimateBrowserTest
+    : public DataSaverSiteBreakdownMetricsObserverBrowserTest,
+      public ::testing::WithParamInterface<SaveDataTestCase> {
+ public:
+  void SetUp() override {
+    ASSERT_TRUE(embedded_test_server()->Start());
+    const std::string estimates_json =
+        !GetParam().origin_savings_estimate_raw_json.empty()
+            ? GetParam().origin_savings_estimate_raw_json
+            : (!GetParam().origin_savings_estimate_list.empty()
+                   ? ConvertSaveDataSavingsEstimateToJson(
+                         GetParam().origin_savings_estimate_list,
+                         *embedded_test_server())
+                   : "");
+    if (!estimates_json.empty()) {
+      scoped_feature_list_.InitWithFeaturesAndParameters(
+          {{data_reduction_proxy::features::kReportSaveDataSavings,
+            {{"origin_savings_estimate", estimates_json}}}},
+          {});
+    }
+    DataSaverSiteBreakdownMetricsObserverBrowserTest::SetUp();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(SaveDataSavingsEstimateBrowserTest,
+                         SaveDataSavingsEstimateBrowserTest,
+                         ::testing::ValuesIn(kSaveDataTestCases));
+
+IN_PROC_BROWSER_TEST_P(SaveDataSavingsEstimateBrowserTest,
+                       NavigateToSimplePage) {
+  WaitForDBToInitialize();
+
+  for (const auto& test : GetParam().tests) {
+    GURL test_url(
+        embedded_test_server()->GetURL(test.test_host, "/google/google.html"));
+    std::string host = test_url.HostNoBrackets();
+    uint64_t data_usage_before_navigation = GetDataUsage(host);
+    uint64_t data_savings_before_navigation = GetDataSavings(host);
+    ui_test_utils::NavigateToURL(browser(), test_url);
+
+    base::RunLoop().RunUntilIdle();
+    // Navigate away to force the histogram recording.
+    ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+    double data_savings_percent =
+        100.0 * (GetDataSavings(host) - data_savings_before_navigation) /
+        (GetDataUsage(host) - data_usage_before_navigation);
+    EXPECT_NEAR(data_savings_percent, test.expected_savings_percent, 0.01);
+  }
+}
+
 IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTest,
                        NavigateToSimplePage) {
   const struct {
@@ -278,15 +406,14 @@
                       data_usage_before_navigation);
 }
 
-IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTest,
-                       LazyLoadImagesCSSBackgroundImage) {
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, LazyLoadImagesCSSBackgroundImage) {
   // 2 deferred images.
   EXPECT_EQ(10000 * 2,
             NavigateAndGetDataSavings("/lazyload/css-background-image.html",
                                       2 /* main html, favicon */));
 }
 
-IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTest,
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest,
                        LazyLoadImagesCSSBackgroundImageScrollRemovesSavings) {
   // Scrolling should remove the savings.
   EXPECT_EQ(0u, NavigateAndGetDataSavingsAfterScroll(
@@ -294,8 +421,7 @@
                     2 /* lazyloaded images */));
 }
 
-IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTest,
-                       LazyLoadImagesImgElement) {
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, LazyLoadImagesImgElement) {
   // Choose reasonable minimum, any savings is indicative of the mechanism
   // working.
   EXPECT_LE(
@@ -305,7 +431,7 @@
         10 /* main html, favicon, 8 images (2 eager, 4 placeholder, 2 full)*/));
 }
 
-IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTest,
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest,
                        LazyLoadImagesImgElementScrollRemovesSavings) {
   // Choose reasonable minimum, any savings is indicative of the mechanism
   // working.
@@ -314,15 +440,14 @@
                                                     2 /* lazyloaded image */));
 }
 
-IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTest,
-                       LazyLoadImagesImgWithDimension) {
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, LazyLoadImagesImgWithDimension) {
   // 1 deferred image.
   EXPECT_EQ(10000,
             NavigateAndGetDataSavings("/lazyload/img-with-dimension.html",
                                       3 /* main html, favicon, full image */));
 }
 
-IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTest,
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest,
                        LazyLoadImagesImgWithDimensionScrollRemovesSavings) {
   // Scrolling should remove the savings.
   EXPECT_EQ(0u, NavigateAndGetDataSavingsAfterScroll(
@@ -330,7 +455,7 @@
                     1 /* lazyloaded image */));
 }
 
-IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTestBase,
+IN_PROC_BROWSER_TEST_F(LazyLoadWithoutLiteModeBrowserTest,
                        NoSavingsRecordedWithoutLiteMode) {
   std::vector<std::string> test_urls = {
       "/google/google.html",
@@ -354,8 +479,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTest,
-                       LazyLoadImageDisabledInReload) {
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest, LazyLoadImageDisabledInReload) {
   ASSERT_TRUE(embedded_test_server()->Start());
   WaitForDBToInitialize();
   GURL test_url(
@@ -396,7 +520,7 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(DataSaverSiteBreakdownMetricsObserverBrowserTest,
+IN_PROC_BROWSER_TEST_F(LazyLoadBrowserTest,
                        DISABLED_LazyLoadFrameDisabledInReload) {
   net::EmbeddedTestServer cross_origin_server;
   cross_origin_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
diff --git a/chrome/browser/policy/chrome_browser_cloud_management_controller.cc b/chrome/browser/policy/chrome_browser_cloud_management_controller.cc
index 07fb7cf..632cc677 100644
--- a/chrome/browser/policy/chrome_browser_cloud_management_controller.cc
+++ b/chrome/browser/policy/chrome_browser_cloud_management_controller.cc
@@ -314,12 +314,16 @@
 
 void ChromeBrowserCloudManagementController::InvalidateDMTokenCallback(
     bool success) {
+  UMA_HISTOGRAM_BOOLEAN(
+      "Enterprise.MachineLevelUserCloudPolicyEnrollment.UnenrollSuccess",
+      success);
   if (success) {
     DVLOG(1) << "Successfully invalidated the DM token";
     InvalidatePolicies();
   } else {
     DVLOG(1) << "Failed to invalidate the DM token";
   }
+  NotifyBrowserUnenrolled(success);
 }
 
 void ChromeBrowserCloudManagementController::OnPolicyFetched(
@@ -347,6 +351,12 @@
   }
 }
 
+void ChromeBrowserCloudManagementController::NotifyBrowserUnenrolled(
+    bool succeeded) {
+  for (auto& observer : observers_)
+    observer.OnBrowserUnenrolled(succeeded);
+}
+
 bool ChromeBrowserCloudManagementController::GetEnrollmentTokenAndClientId(
     std::string* enrollment_token,
     std::string* client_id) {
diff --git a/chrome/browser/policy/chrome_browser_cloud_management_controller.h b/chrome/browser/policy/chrome_browser_cloud_management_controller.h
index 1473bbe..40c5a09 100644
--- a/chrome/browser/policy/chrome_browser_cloud_management_controller.h
+++ b/chrome/browser/policy/chrome_browser_cloud_management_controller.h
@@ -63,6 +63,9 @@
     // Called when policy enrollment is finished.
     // |succeeded| is true if |dm_token| is returned from the server.
     virtual void OnPolicyRegisterFinished(bool succeeded) {}
+
+    // Called when the browser has been unenrolled.
+    virtual void OnBrowserUnenrolled(bool succeeded) {}
   };
 
   // Directory name under the user-data-dir where the policy data is stored.
@@ -99,6 +102,7 @@
 
  protected:
   void NotifyPolicyRegisterFinished(bool succeeded);
+  void NotifyBrowserUnenrolled(bool succeeded);
 
  private:
   bool GetEnrollmentTokenAndClientId(std::string* enrollment_token,
diff --git a/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc b/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc
index e48f7723..c1515b7 100644
--- a/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc
+++ b/chrome/browser/policy/cloud/chrome_browser_cloud_management_browsertest.cc
@@ -74,6 +74,8 @@
 const char kInvalidDMToken[] = "invalid_dm_token";
 const char kEnrollmentResultMetrics[] =
     "Enterprise.MachineLevelUserCloudPolicyEnrollment.Result";
+const char kUnenrollmentSuccessMetrics[] =
+    "Enterprise.MachineLevelUserCloudPolicyEnrollment.UnenrollSuccess";
 const char kTestPolicyConfig[] = R"(
 {
   "google/chrome/machine-level-user" : {
@@ -546,17 +548,42 @@
                                             ::testing::Bool()));
 
 class MachineLevelUserCloudPolicyPolicyFetchTest
-    : public InProcessBrowserTest,
-      public ::testing::WithParamInterface<std::string> {
+    : public ChromeBrowserCloudManagementControllerObserver,
+      public InProcessBrowserTest,
+      public ::testing::WithParamInterface<std::tuple<std::string, bool>> {
  public:
   MachineLevelUserCloudPolicyPolicyFetchTest() {
     BrowserDMTokenStorage::SetForTesting(&storage_);
     storage_.SetEnrollmentToken(kEnrollmentToken);
     storage_.SetClientId(kClientID);
+    storage_.EnableStorage(storage_enabled());
     if (!dm_token().empty())
       storage_.SetDMToken(dm_token());
   }
 
+  void QuitOnUnenroll(base::RepeatingClosure quit_closure) {
+    quit_closure_ = std::move(quit_closure);
+  }
+
+  void OnBrowserUnenrolled(bool succeeded) override {
+    if (!quit_closure_.is_null()) {
+      EXPECT_FALSE(succeeded);
+      std::move(quit_closure_).Run();
+    }
+  }
+
+  void SetUpOnMainThread() override {
+    g_browser_process->browser_policy_connector()
+        ->chrome_browser_cloud_management_controller()
+        ->AddObserver(this);
+  }
+
+  void TearDownOnMainThread() override {
+    g_browser_process->browser_policy_connector()
+        ->chrome_browser_cloud_management_controller()
+        ->RemoveObserver(this);
+  }
+
   void SetUpInProcessBrowserTestFixture() override {
     SetUpTestServer();
     ASSERT_TRUE(test_server_->Start());
@@ -584,12 +611,17 @@
 
   DMToken retrieve_dm_token() { return storage_.RetrieveDMToken(); }
 
-  const std::string dm_token() const { return GetParam(); }
+  const std::string dm_token() const { return std::get<0>(GetParam()); }
+  bool storage_enabled() const { return std::get<1>(GetParam()); }
+
+ protected:
+  base::HistogramTester histogram_tester_;
 
  private:
   std::unique_ptr<LocalPolicyTestServer> test_server_;
   FakeBrowserDMTokenStorage storage_;
   base::ScopedTempDir temp_dir_;
+  base::RepeatingClosure quit_closure_;
 
   DISALLOW_COPY_AND_ASSIGN(MachineLevelUserCloudPolicyPolicyFetchTest);
 };
@@ -608,8 +640,17 @@
     std::unique_ptr<PolicyFetchCoreObserver> core_observer;
     std::unique_ptr<PolicyFetchStoreObserver> store_observer;
     if (dm_token() == kInvalidDMToken) {
-      core_observer = std::make_unique<PolicyFetchCoreObserver>(
-          manager->core(), run_loop.QuitClosure());
+      if (storage_enabled()) {
+        // |run_loop|'s QuitClosure will be called after the core is
+        // disconnected following unenrollment.
+        core_observer = std::make_unique<PolicyFetchCoreObserver>(
+            manager->core(), run_loop.QuitClosure());
+      } else {
+        // |run_loop|'s QuitClosure will be called after the browser attempts to
+        // unenroll from CBCM. This is necessary to quit the loop in the case
+        // the storage fails since the core is not disconnected.
+        QuitOnUnenroll(run_loop.QuitClosure());
+      }
     } else {
       store_observer = std::make_unique<PolicyFetchStoreObserver>(
           manager->store(), run_loop.QuitClosure());
@@ -637,24 +678,32 @@
       EXPECT_EQ(token.value(), "fake_device_management_token");
     else
       EXPECT_EQ(token.value(), kDMToken);
+
+    histogram_tester_.ExpectTotalCount(kUnenrollmentSuccessMetrics, 0);
   } else {
     EXPECT_EQ(0u, policy_map.size());
 
     // The token in storage should be invalid.
     DMToken token = retrieve_dm_token();
     EXPECT_TRUE(token.is_invalid());
+
+    histogram_tester_.ExpectUniqueSample(kUnenrollmentSuccessMetrics,
+                                         storage_enabled(), 1);
   }
 }
 
-// The tests here cover three cases:
+// The tests here cover three DM token cases combined with the storage
+// succeeding or failing:
 //  1) Start Chrome with a valid DM token but no policy cache. Chrome will
 //  load the policy from the DM server.
 //  2) Start Chrome with an invalid DM token. Chrome will hit the DM server and
 //  get an error. There should be no more cloud policy applied.
 //  3) Start Chrome without DM token. Chrome will register itself and fetch
 //  policy after it.
-INSTANTIATE_TEST_SUITE_P(MachineLevelUserCloudPolicyPolicyFetchTest,
-                         MachineLevelUserCloudPolicyPolicyFetchTest,
-                         ::testing::Values(kDMToken, kInvalidDMToken, ""));
+INSTANTIATE_TEST_SUITE_P(
+    MachineLevelUserCloudPolicyPolicyFetchTest,
+    MachineLevelUserCloudPolicyPolicyFetchTest,
+    ::testing::Combine(::testing::Values(kDMToken, kInvalidDMToken, ""),
+                       ::testing::Bool()));
 
 }  // namespace policy
diff --git a/chrome/browser/profiles/profile_window_browsertest.cc b/chrome/browser/profiles/profile_window_browsertest.cc
index b153c4f8..c72657d9 100644
--- a/chrome/browser/profiles/profile_window_browsertest.cc
+++ b/chrome/browser/profiles/profile_window_browsertest.cc
@@ -24,6 +24,8 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/find_bar/find_bar_state.h"
+#include "chrome/browser/ui/find_bar/find_bar_state_factory.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
 #include "chrome/browser/ui/user_manager.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -200,6 +202,38 @@
   ASSERT_EQ("", cookie);
 }
 
+IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, GuestClearsFindInPageCache) {
+  Browser* guest_browser = OpenGuestBrowser();
+  Profile* guest_profile = guest_browser->profile();
+
+  base::string16 fip_text =
+      base::ASCIIToUTF16("first guest session search text");
+  FindBarStateFactory::GetForProfile(guest_profile)
+      ->set_last_prepopulate_text(fip_text);
+
+  // Open a second guest window and close one. This should not affect the find
+  // in page cache as the guest session hasn't been ended.
+  profiles::FindOrCreateNewWindowForProfile(
+      guest_profile, chrome::startup::IS_NOT_PROCESS_STARTUP,
+      chrome::startup::IS_NOT_FIRST_RUN, true /*always_create*/);
+  CloseBrowserSynchronously(guest_browser);
+  EXPECT_EQ(fip_text, FindBarStateFactory::GetForProfile(guest_profile)
+                          ->last_prepopulate_text());
+
+  // Close the remaining guest browser window.
+  guest_browser = chrome::FindAnyBrowser(guest_profile, true);
+  EXPECT_TRUE(guest_browser);
+  CloseBrowserSynchronously(guest_browser);
+
+  // Open a new guest browser window. Since this is a separate session, the find
+  // in page text should have been cleared (along with all other browsing data).
+  profiles::FindOrCreateNewWindowForProfile(
+      guest_profile, chrome::startup::IS_NOT_PROCESS_STARTUP,
+      chrome::startup::IS_NOT_FIRST_RUN, true /*always_create*/);
+  EXPECT_EQ(base::string16(), FindBarStateFactory::GetForProfile(guest_profile)
+                                  ->last_prepopulate_text());
+}
+
 IN_PROC_BROWSER_TEST_F(ProfileWindowBrowserTest, GuestCannotSignin) {
   Browser* guest_browser = OpenGuestBrowser();
 
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index 4dd36e7..2bca0317 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -49,12 +49,19 @@
 
 js_library("customize_dialog") {
   deps = [
+    ":customize_themes",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
     "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
   ]
 }
 
+js_library("customize_themes") {
+  deps = [
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
 js_library("theme_icon") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
@@ -82,6 +89,12 @@
   html_type = "v3-ready"
 }
 
+polymer_modulizer("customize_themes") {
+  js_file = "customize_themes.js"
+  html_file = "customize_themes.html"
+  html_type = "v3-ready"
+}
+
 polymer_modulizer("theme_icon") {
   js_file = "theme_icon.js"
   html_file = "theme_icon.html"
@@ -92,6 +105,7 @@
   deps = [
     ":app_module",
     ":customize_dialog_module",
+    ":customize_themes_module",
     ":most_visited_module",
     ":theme_icon_module",
   ]
diff --git a/chrome/browser/resources/new_tab_page/customize_dialog.html b/chrome/browser/resources/new_tab_page/customize_dialog.html
index b64a54d..36547781 100644
--- a/chrome/browser/resources/new_tab_page/customize_dialog.html
+++ b/chrome/browser/resources/new_tab_page/customize_dialog.html
@@ -4,142 +4,120 @@
   }
 
   div[slot=body] {
-    align-items: center;
-    display: flex;
-    flex-direction: column;
-    min-height: 423px;
-  }
-
-  #thirdPartyThemeContainer {
-    width: 100%;
-  }
-
-  #thirdPartyTheme {
-    align-items: center;
-    border: 1px solid var(--google-grey-200);
-    border-radius: 5px;
     display: flex;
     flex-direction: row;
-    margin-bottom: 24px;
-    padding: 0 16px;
+    min-height: 423px;
+    padding-inline-start: 0;
   }
 
-  #thirdPartyBrushIcon {
-    -webkit-mask-image: url(icons/brush.svg);
-    -webkit-mask-repeat: no-repeat;
-    -webkit-mask-size: 100%;
-    background-color: var(--cr-secondary-text-color);
-    height: 24px;
-    margin-inline-end: 20px;
-    width: 24px;
+  #menu {
+    display: flex;
+    flex-direction: column;
+    margin-inline-end: 40px;
   }
 
-  #thirdPartyThemeNameContainer {
-    flex-grow: 1;
-    margin-inline-end: 24px;
+  .menu-item {
+    align-items: center;
+    border-radius: 0 16px 16px 0;
+    box-sizing: border-box;
+    cursor: pointer;
+    display: flex;
+    flex-direction: row;
+    font-size: 14px;
+    height: 32px;
+    margin-bottom: 16px;
+    outline: none;
+    width: 192px;
   }
 
-  #thirdPartyThemeName {
-    font-weight: bold;
+  :host-context([dir=rtl]) .menu-item {
+    border-radius: 16px 0 0 16px;
   }
 
-  #thirdPartyLink {
-    -webkit-mask-image: url(chrome://resources/images/open_in_new.svg);
+  .menu-item:hover {
+    background-color: rgba(var(--google-grey-900-rgb), .1);
+  }
+
+  .menu-item[selected] {
+    background-color: var(--google-blue-50);
+    color: var(--google-blue-refresh-700);
+  }
+
+  :host-context(.focus-outline-visible) .menu-item:focus {
+    border-color: rgba(var(--google-blue-600-rgb), .4);
+    border-style: solid solid solid none;
+    border-width: 2px;
+  }
+
+  :host-context(.focus-outline-visible[dir=rtl]) .menu-item:focus {
+    border-style: solid none solid solid;
+  }
+
+  .menu-item-icon {
     -webkit-mask-repeat: no-repeat;
     -webkit-mask-size: 100%;
     background-color: var(--cr-secondary-text-color);
     height: 20px;
-    margin-inline-end: 24px;
+    margin-inline-end: 16px;
+    margin-inline-start: 24px;
     width: 20px;
   }
 
-  #uninstallThirdPartyButton {
-    margin: 16px 0;
+  .menu-item[selected] .menu-item-icon {
+    background-color: var(--google-blue-refresh-700);
   }
 
-  #themesContainer {
-    display: inline-grid;
-    grid-column-gap: 25px;
-    grid-row-gap: 25px;
-    grid-template-columns: repeat(var(--num-theme-columns), auto);
-    outline-width: 0;
-    padding: 3px;
-    width: fit-content;
+  #backgroundsIcon {
+    -webkit-mask-image: url(icons/backgrounds.svg);
   }
 
-  :host-context(.focus-outline-visible) #themesContainer:focus {
-    box-shadow: inset 0 0 0 2px rgba(var(--google-blue-600-rgb), .4);
+  #shortcutsIcon {
+    -webkit-mask-image: url(icons/link.svg);
   }
 
-  ntp-theme-icon {
-    align-self: center;
-    justify-self: center;
-    outline-width: 0;
+  #themesIcon {
+    -webkit-mask-image: url(icons/colors.svg);
   }
 
-  :host-context(.focus-outline-visible) ntp-theme-icon:focus {
-    box-shadow: 0 0 0 2px rgba(var(--google-blue-600-rgb), .4);
-  }
-
-  #autogeneratedTheme {
-    --ntp-theme-icon-frame-color: var(--google-grey-refresh-100);
-    --ntp-theme-icon-active-tab-color: white;
-    --ntp-theme-icon-stroke-color: var(--google-grey-refresh-300);
-  }
-
-  #defaultTheme {
-    --ntp-theme-icon-frame-color: rgb(222, 225, 230);
-    --ntp-theme-icon-active-tab-color: white;
+  #pages {
+    align-items: center;
+    display: flex;
+    flex-direction: column;
+    flex-grow: 1;
   }
 </style>
-<cr-dialog id="dialog">
+<cr-dialog id="dialog" show-on-attach>
+  <!-- TODO(crbug.com/1040256): Currently, the sidebar scrolls in sync with the
+       page content area. Fix, so that the page content can scroll
+       separately. -->
   <div slot="body">
-    <div id="thirdPartyThemeContainer" hidden="[[!isThirdPartyTheme_(theme)]]">
-      <div id="thirdPartyTheme">
-        <div id="thirdPartyBrushIcon"></div>
-        <div id="thirdPartyThemeNameContainer">
-          <div id="thirdPartyThemeName" >
-            [[theme.info.thirdPartyThemeInfo.name]]
-          </div>
-          <div>
-            $i18n{thirdPartyThemeDescription}
-          </div>
-        </div>
-        <a id="thirdPartyLink" target="_blank"
-            href$="[[getThirdPartyLink_(theme.info.thirdPartyThemeInfo.id)]]">
-        </a>
-        <cr-button id="uninstallThirdPartyButton"
-            on-click="onUninstallThirdPartyThemeClick_">
-          $i18n{uninstallThirdPartyThemeButton}
-        </cr-button>
+    <iron-selector id="menu" selected-attribute="selected"
+        attr-for-selected="page-name" selected="{{selectedPage_}}"
+        on-keydown="onMenuItemKeyDown_">
+      <div class="menu-item" page-name="backgrounds" tabindex="0">
+        <div id="backgroundsIcon" class="menu-item-icon"></div>
+        $i18n{backgroundsMenuItem}
       </div>
-    </div>
-    <input id="colorPicker" type="color" on-change="onCustomFrameColorChange_"
-        hidden>
-    </input>
-    <div id="themesContainer" on-keydown="onThemesKeyDown_"
-        style="--num-theme-columns: [[numThemeColumns_]];">
-      <!-- TODO(crbug.com/1032327): Add color picker icon. -->
-      <ntp-theme-icon id="autogeneratedTheme" title="$i18n{colorPickerLabel}"
-          on-click="onAutogeneratedThemeClick_"
-          selected$="[[isThemeIconSelected_('autogenerated', theme)]]">
-      </ntp-theme-icon>
-      <ntp-theme-icon id="defaultTheme" title="$i18n{defaultThemeLabel}"
-          on-click="onDefaultThemeClick_"
-          selected$="[[isThemeIconSelected_('default', theme)]]">
-      </ntp-theme-icon>
-      <dom-repeat id="themes" items="[[chromeThemes_]]">
-        <template>
-          <ntp-theme-icon title="[[item.label]]" on-click="onChromeThemeClick_"
-              style="--ntp-theme-icon-frame-color:
-                  [[skColorToRgb_(item.colors.frame)]];
-                --ntp-theme-icon-active-tab-color:
-                  [[skColorToRgb_(item.colors.activeTab)]];"
-              selected$="[[isThemeIconSelected_(item.id, theme)]]">
-          </ntp-theme-icon>
-        </template>
-      </dom-repeat>
-    </div>
+      <div class="menu-item" page-name="shortcuts" tabindex="0">
+        <div id="shortcutsIcon" class="menu-item-icon"></div>
+        $i18n{shortcutsMenuItem}
+      </div>
+      <div class="menu-item" page-name="themes" tabindex="0">
+        <div id="themesIcon" class="menu-item-icon"></div>
+        $i18n{themesMenuItem}
+      </div>
+    </iron-selector>
+    <iron-pages id="pages" selected="[[selectedPage_]]"
+        attr-for-selected="page-name">
+      <!-- TODO(crbug.com/1032328): Add support for selecting background
+           image. -->
+      <div page-name="backgrounds">backgrounds</div>
+      <!-- TODO(crbug.com/1032333): Add support for selecting shortcuts vs most
+           visited. -->
+      <div page-name="shortcuts">shortcuts</div>
+      <ntp-customize-themes page-name="themes" theme="[[theme]]">
+      </ntp-customize-themes>
+    </iron-pages>
   </div>
   <div slot="button-container">
     <cr-button class="cancel-button" on-click="onCancelClick_">
diff --git a/chrome/browser/resources/new_tab_page/customize_dialog.js b/chrome/browser/resources/new_tab_page/customize_dialog.js
index 8d425f5..3dc8e3cb 100644
--- a/chrome/browser/resources/new_tab_page/customize_dialog.js
+++ b/chrome/browser/resources/new_tab_page/customize_dialog.js
@@ -4,18 +4,17 @@
 
 import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
 import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
-import './theme_icon.js';
+import './customize_themes.js';
+import 'chrome://resources/polymer/v3_0/iron-pages/iron-pages.js';
+import 'chrome://resources/polymer/v3_0/iron-selector/iron-selector.js';
 
 import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {BrowserProxy} from './browser_proxy.js';
-import {hexColorToSkColor, skColorToRgb} from './utils.js';
 
 /**
  * Dialog that lets the user customize the NTP such as the background color or
  * image.
- * TODO(crbug.com/1032328): Add support for selecting background image.
- * TODO(crbug.com/1032333): Add support for selecting shortcuts vs most visited.
  */
 class CustomizeDialogElement extends PolymerElement {
   static get is() {
@@ -29,19 +28,12 @@
   static get properties() {
     return {
       /** @type {!newTabPage.mojom.Theme} */
-      theme: {
-        type: Object,
-        observer: 'onThemeChange_',
-      },
+      theme: Object,
 
-      /** @private {!Array<!newTabPage.mojom.ChromeTheme>} */
-      chromeThemes_: Array,
-
-      /** @private {number} */
-      numThemeColumns_: {
-        type: Number,
-        readOnly: true,
-        value: 6,
+      /** @private */
+      selectedPage_: {
+        type: String,
+        value: 'backgrounds',
       },
     };
   }
@@ -52,15 +44,6 @@
     this.pageHandler_ = BrowserProxy.getInstance().handler;
   }
 
-  /** @override */
-  connectedCallback() {
-    super.connectedCallback();
-    this.pageHandler_.getChromeThemes().then(({chromeThemes}) => {
-      this.chromeThemes_ = chromeThemes;
-      this.$.dialog.showModal();
-    });
-  }
-
   /** @private */
   onCancelClick_() {
     this.pageHandler_.revertThemeChanges();
@@ -77,142 +60,13 @@
    * @param {!Event} e
    * @private
    */
-  onCustomFrameColorChange_(e) {
-    this.pageHandler_.applyAutogeneratedTheme(
-        hexColorToSkColor(e.target.value));
-  }
-
-  /** @private */
-  onAutogeneratedThemeClick_() {
-    this.$.colorPicker.click();
-  }
-
-  /** @private */
-  onDefaultThemeClick_() {
-    this.pageHandler_.applyDefaultTheme();
-  }
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onChromeThemeClick_(e) {
-    this.pageHandler_.applyChromeTheme(
-        this.$.themes.itemForElement(e.target).id);
-  }
-
-  /** private */
-  onThemeChange_() {
-    if (this.theme.type !== newTabPage.mojom.ThemeType.AUTOGENERATED) {
+  onMenuItemKeyDown_(e) {
+    if (!['Enter', ' '].includes(e.key)) {
       return;
     }
-    const rgbFrameColor =
-        skColorToRgb(this.theme.info.autogeneratedThemeColors.frame);
-    const rgbActiveTabColor =
-        skColorToRgb(this.theme.info.autogeneratedThemeColors.activeTab);
-    this.$.autogeneratedTheme.style.setProperty(
-        '--ntp-theme-icon-frame-color', rgbFrameColor);
-    this.$.autogeneratedTheme.style.setProperty(
-        '--ntp-theme-icon-stroke-color', rgbFrameColor);
-    this.$.autogeneratedTheme.style.setProperty(
-        '--ntp-theme-icon-active-tab-color', rgbActiveTabColor);
-  }
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onUninstallThirdPartyThemeClick_(e) {
-    this.pageHandler_.applyDefaultTheme();
-    this.pageHandler_.confirmThemeChanges();
-  }
-
-  /**
-   * @param {string|number} id
-   * @return {boolean}
-   * @private
-   */
-  isThemeIconSelected_(id) {
-    if (!this.theme) {
-      return false;
-    }
-    if (id === 'autogenerated') {
-      return this.theme.type === newTabPage.mojom.ThemeType.AUTOGENERATED;
-    } else if (id === 'default') {
-      return this.theme.type === newTabPage.mojom.ThemeType.DEFAULT;
-    } else {
-      return this.theme.type === newTabPage.mojom.ThemeType.CHROME &&
-          id === this.theme.info.chromeThemeId;
-    }
-  }
-
-  /**
-   * @return {boolean}
-   * @private
-   */
-  isThirdPartyTheme_() {
-    return this.theme.type === newTabPage.mojom.ThemeType.THIRD_PARTY;
-  }
-
-  /**
-   * @return {string}
-   * @private
-   */
-  getThirdPartyLink_() {
-    if (!this.isThirdPartyTheme_()) {
-      return '';
-    }
-    return 'https://chrome.google.com/webstore/detail/' +
-        this.theme.info.thirdPartyThemeInfo.id;
-  }
-
-  /**
-   * @param {skia.mojom.SkColor} skColor
-   * @return {string}
-   * @private
-   */
-  skColorToRgb_(skColor) {
-    return skColorToRgb(skColor);
-  }
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onThemesKeyDown_(e) {
-    if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
-      e.preventDefault();
-      const themeIcons =
-          Array.from(this.$.themesContainer.querySelectorAll('ntp-theme-icon'));
-      const currentIndex = themeIcons.indexOf(e.target);
-      const isRtl = window.getComputedStyle(this)['direction'] === 'rtl';
-      let delta = 0;
-      switch (e.key) {
-        case 'ArrowLeft':
-          delta = isRtl ? 1 : -1;
-          break;
-        case 'ArrowRight':
-          delta = isRtl ? -1 : 1;
-          break;
-        case 'ArrowUp':
-          delta = -this.numThemeColumns_;
-          break;
-        case 'ArrowDown':
-          delta = this.numThemeColumns_;
-          break;
-      }
-      const mod = function(m, n) {
-        return ((m % n) + n) % n;
-      };
-      const newIndex = mod(currentIndex + delta, themeIcons.length);
-      themeIcons[newIndex].focus();
-    }
-
-    if (['Enter', ' '].includes(e.key)) {
-      e.preventDefault();
-      e.stopPropagation();
-      e.target.click();
-    }
+    e.preventDefault();
+    e.stopPropagation();
+    this.selectedPage_ = e.target.getAttribute('page-name');
   }
 }
 
diff --git a/chrome/browser/resources/new_tab_page/customize_themes.html b/chrome/browser/resources/new_tab_page/customize_themes.html
new file mode 100644
index 0000000..d884a65
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/customize_themes.html
@@ -0,0 +1,129 @@
+<style>
+  #thirdPartyThemeContainer {
+    width: 100%;
+  }
+
+  #thirdPartyTheme {
+    align-items: center;
+    border: 1px solid var(--google-grey-200);
+    border-radius: 5px;
+    display: flex;
+    flex-direction: row;
+    margin-bottom: 24px;
+    padding: 0 16px;
+  }
+
+  #thirdPartyBrushIcon {
+    -webkit-mask-image: url(icons/brush.svg);
+    -webkit-mask-repeat: no-repeat;
+    -webkit-mask-size: 100%;
+    background-color: var(--cr-secondary-text-color);
+    height: 24px;
+    margin-inline-end: 20px;
+    width: 24px;
+  }
+
+  #thirdPartyThemeNameContainer {
+    flex-grow: 1;
+    margin-inline-end: 24px;
+  }
+
+  #thirdPartyThemeName {
+    font-weight: bold;
+  }
+
+  #thirdPartyLink {
+    -webkit-mask-image: url(chrome://resources/images/open_in_new.svg);
+    -webkit-mask-repeat: no-repeat;
+    -webkit-mask-size: 100%;
+    background-color: var(--cr-secondary-text-color);
+    height: 20px;
+    margin-inline-end: 24px;
+    width: 20px;
+  }
+
+  #uninstallThirdPartyButton {
+    margin: 16px 0;
+  }
+
+  #themesContainer {
+    display: inline-grid;
+    grid-column-gap: 20px;
+    grid-row-gap: 20px;
+    grid-template-columns: repeat(var(--num-theme-columns), auto);
+    outline-width: 0;
+    padding: 3px;
+    width: fit-content;
+  }
+
+  :host-context(.focus-outline-visible) #themesContainer:focus {
+    box-shadow: inset 0 0 0 2px rgba(var(--google-blue-600-rgb), .4);
+  }
+
+  ntp-theme-icon {
+    align-self: center;
+    justify-self: center;
+    outline-width: 0;
+  }
+
+  :host-context(.focus-outline-visible) ntp-theme-icon:focus {
+    box-shadow: 0 0 0 2px rgba(var(--google-blue-600-rgb), .4);
+  }
+
+  #autogeneratedTheme {
+    --ntp-theme-icon-frame-color: var(--google-grey-refresh-100);
+    --ntp-theme-icon-active-tab-color: white;
+    --ntp-theme-icon-stroke-color: var(--google-grey-refresh-300);
+  }
+
+  #defaultTheme {
+    --ntp-theme-icon-frame-color: rgb(222, 225, 230);
+    --ntp-theme-icon-active-tab-color: white;
+  }
+</style>
+<div id="thirdPartyThemeContainer" hidden="[[!isThirdPartyTheme_(theme)]]">
+  <div id="thirdPartyTheme">
+    <div id="thirdPartyBrushIcon"></div>
+    <div id="thirdPartyThemeNameContainer">
+      <div id="thirdPartyThemeName" >
+        [[theme.info.thirdPartyThemeInfo.name]]
+      </div>
+      <div>
+        $i18n{thirdPartyThemeDescription}
+      </div>
+    </div>
+    <a id="thirdPartyLink" target="_blank"
+        href$="[[getThirdPartyLink_(theme.info.thirdPartyThemeInfo.id)]]">
+    </a>
+    <cr-button id="uninstallThirdPartyButton"
+        on-click="onUninstallThirdPartyThemeClick_">
+      $i18n{uninstallThirdPartyThemeButton}
+    </cr-button>
+  </div>
+</div>
+<input id="colorPicker" type="color" on-change="onCustomFrameColorChange_"
+    hidden>
+</input>
+<div id="themesContainer" on-keydown="onThemesKeyDown_"
+    style="--num-theme-columns: [[numThemeColumns_]];">
+  <!-- TODO(crbug.com/1032327): Add color picker icon. -->
+  <ntp-theme-icon id="autogeneratedTheme" title="$i18n{colorPickerLabel}"
+      on-click="onAutogeneratedThemeClick_"
+      selected$="[[isThemeIconSelected_('autogenerated', theme)]]">
+  </ntp-theme-icon>
+  <ntp-theme-icon id="defaultTheme" title="$i18n{defaultThemeLabel}"
+      on-click="onDefaultThemeClick_"
+      selected$="[[isThemeIconSelected_('default', theme)]]">
+  </ntp-theme-icon>
+  <dom-repeat id="themes" items="[[chromeThemes_]]">
+    <template>
+      <ntp-theme-icon title="[[item.label]]" on-click="onChromeThemeClick_"
+          style="--ntp-theme-icon-frame-color:
+              [[skColorToRgb_(item.colors.frame)]];
+            --ntp-theme-icon-active-tab-color:
+              [[skColorToRgb_(item.colors.activeTab)]];"
+          selected$="[[isThemeIconSelected_(item.id, theme)]]">
+      </ntp-theme-icon>
+    </template>
+  </dom-repeat>
+</div>
diff --git a/chrome/browser/resources/new_tab_page/customize_themes.js b/chrome/browser/resources/new_tab_page/customize_themes.js
new file mode 100644
index 0000000..a6d945d2
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/customize_themes.js
@@ -0,0 +1,195 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import './theme_icon.js';
+
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {BrowserProxy} from './browser_proxy.js';
+import {hexColorToSkColor, skColorToRgb} from './utils.js';
+
+/** Element that lets the user configure the theme. */
+class CustomizeThemesElement extends PolymerElement {
+  static get is() {
+    return 'ntp-customize-themes';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      /** @type {!newTabPage.mojom.Theme} */
+      theme: {
+        type: Object,
+        observer: 'onThemeChange_',
+      },
+
+      /** @private {!Array<!newTabPage.mojom.ChromeTheme>} */
+      chromeThemes_: Array,
+
+      /** @private {number} */
+      numThemeColumns_: {
+        type: Number,
+        readOnly: true,
+        value: 6,
+      },
+    };
+  }
+
+  constructor() {
+    super();
+    /** @private {newTabPage.mojom.PageHandlerRemote} */
+    this.pageHandler_ = BrowserProxy.getInstance().handler;
+    this.pageHandler_.getChromeThemes().then(({chromeThemes}) => {
+      this.chromeThemes_ = chromeThemes;
+    });
+  }
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onCustomFrameColorChange_(e) {
+    this.pageHandler_.applyAutogeneratedTheme(
+        hexColorToSkColor(e.target.value));
+  }
+
+  /** @private */
+  onAutogeneratedThemeClick_() {
+    this.$.colorPicker.click();
+  }
+
+  /** @private */
+  onDefaultThemeClick_() {
+    this.pageHandler_.applyDefaultTheme();
+  }
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onChromeThemeClick_(e) {
+    this.pageHandler_.applyChromeTheme(
+        this.$.themes.itemForElement(e.target).id);
+  }
+
+  /** private */
+  onThemeChange_() {
+    if (this.theme.type !== newTabPage.mojom.ThemeType.AUTOGENERATED) {
+      return;
+    }
+    const rgbFrameColor =
+        skColorToRgb(this.theme.info.autogeneratedThemeColors.frame);
+    const rgbActiveTabColor =
+        skColorToRgb(this.theme.info.autogeneratedThemeColors.activeTab);
+    this.$.autogeneratedTheme.style.setProperty(
+        '--ntp-theme-icon-frame-color', rgbFrameColor);
+    this.$.autogeneratedTheme.style.setProperty(
+        '--ntp-theme-icon-stroke-color', rgbFrameColor);
+    this.$.autogeneratedTheme.style.setProperty(
+        '--ntp-theme-icon-active-tab-color', rgbActiveTabColor);
+  }
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onUninstallThirdPartyThemeClick_(e) {
+    this.pageHandler_.applyDefaultTheme();
+    this.pageHandler_.confirmThemeChanges();
+  }
+
+  /**
+   * @param {string|number} id
+   * @return {boolean}
+   * @private
+   */
+  isThemeIconSelected_(id) {
+    if (!this.theme) {
+      return false;
+    }
+    if (id === 'autogenerated') {
+      return this.theme.type === newTabPage.mojom.ThemeType.AUTOGENERATED;
+    } else if (id === 'default') {
+      return this.theme.type === newTabPage.mojom.ThemeType.DEFAULT;
+    } else {
+      return this.theme.type === newTabPage.mojom.ThemeType.CHROME &&
+          id === this.theme.info.chromeThemeId;
+    }
+  }
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  isThirdPartyTheme_() {
+    return this.theme.type === newTabPage.mojom.ThemeType.THIRD_PARTY;
+  }
+
+  /**
+   * @return {string}
+   * @private
+   */
+  getThirdPartyLink_() {
+    if (!this.isThirdPartyTheme_()) {
+      return '';
+    }
+    return 'https://chrome.google.com/webstore/detail/' +
+        this.theme.info.thirdPartyThemeInfo.id;
+  }
+
+  /**
+   * @param {skia.mojom.SkColor} skColor
+   * @return {string}
+   * @private
+   */
+  skColorToRgb_(skColor) {
+    return skColorToRgb(skColor);
+  }
+
+  /**
+   * @param {!Event} e
+   * @private
+   */
+  onThemesKeyDown_(e) {
+    if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) {
+      e.preventDefault();
+      const themeIcons =
+          Array.from(this.$.themesContainer.querySelectorAll('ntp-theme-icon'));
+      const currentIndex = themeIcons.indexOf(e.target);
+      const isRtl = window.getComputedStyle(this)['direction'] === 'rtl';
+      let delta = 0;
+      switch (e.key) {
+        case 'ArrowLeft':
+          delta = isRtl ? 1 : -1;
+          break;
+        case 'ArrowRight':
+          delta = isRtl ? -1 : 1;
+          break;
+        case 'ArrowUp':
+          delta = -this.numThemeColumns_;
+          break;
+        case 'ArrowDown':
+          delta = this.numThemeColumns_;
+          break;
+      }
+      const mod = function(m, n) {
+        return ((m % n) + n) % n;
+      };
+      const newIndex = mod(currentIndex + delta, themeIcons.length);
+      themeIcons[newIndex].focus();
+    }
+
+    if (['Enter', ' '].includes(e.key)) {
+      e.preventDefault();
+      e.stopPropagation();
+      e.target.click();
+    }
+  }
+}
+
+customElements.define(CustomizeThemesElement.is, CustomizeThemesElement);
diff --git a/chrome/browser/resources/new_tab_page/icons/backgrounds.svg b/chrome/browser/resources/new_tab_page/icons/backgrounds.svg
new file mode 100644
index 0000000..56d6075
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/icons/backgrounds.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M4 4h7V2H4c-1.1 0-2 .9-2 2v7h2V4zm6 9l-4 5h12l-3-4-2.03 2.71L10 13zm7-4.5c0-.83-.67-1.5-1.5-1.5S14 7.67 14 8.5s.67 1.5 1.5 1.5S17 9.33 17 8.5zM20 2h-7v2h7v7h2V4c0-1.1-.9-2-2-2zm0 18h-7v2h7c1.1 0 2-.9 2-2v-7h-2v7zM4 13H2v7c0 1.1.9 2 2 2h7v-2H4v-7z" id="a"/></defs><use xlink:href="#a"/></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/new_tab_page/icons/colors.svg b/chrome/browser/resources/new_tab_page/icons/colors.svg
new file mode 100644
index 0000000..e7f9fc4
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/icons/colors.svg
@@ -0,0 +1 @@
+<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M12 3a9 9 0 0 0 0 18c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z" id="a"/></defs><use xlink:href="#a"/></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/new_tab_page/icons/link.svg b/chrome/browser/resources/new_tab_page/icons/link.svg
new file mode 100644
index 0000000..6f1d5f4
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/icons/link.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="none" fill-rule="evenodd"><path d="M0 0h16v16H0z"/><path fill="#fff" fill-rule="nonzero" d="M1.52 8A2.482 2.482 0 0 1 4 5.52h3.2V4H4C1.792 4 0 5.792 0 8s1.792 4 4 4h3.2v-1.52H4A2.482 2.482 0 0 1 1.52 8zm3.28.8h6.4V7.2H4.8v1.6zM12 4H8.8v1.52H12A2.482 2.482 0 0 1 14.48 8 2.482 2.482 0 0 1 12 10.48H8.8V12H12c2.208 0 4-1.792 4-4s-1.792-4-4-4z"/></g></svg>
\ No newline at end of file
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page_resources.grd b/chrome/browser/resources/new_tab_page/new_tab_page_resources.grd
index ec5f97e..b6d4512 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page_resources.grd
+++ b/chrome/browser/resources/new_tab_page/new_tab_page_resources.grd
@@ -27,6 +27,9 @@
       <include name="IDR_NEW_TAB_PAGE_CUSTOMIZE_DIALOG_JS"
           file="${root_gen_dir}/chrome/browser/resources/new_tab_page/customize_dialog.js"
           use_base_dir="false" type="BINDATA" compress="gzip" />
+      <include name="IDR_NEW_TAB_PAGE_CUSTOMIZE_THEMES_JS"
+          file="${root_gen_dir}/chrome/browser/resources/new_tab_page/customize_themes.js"
+          use_base_dir="false" type="BINDATA" compress="gzip" />
       <include name="IDR_NEW_TAB_PAGE_THEME_ICON_JS"
           file="${root_gen_dir}/chrome/browser/resources/new_tab_page/theme_icon.js"
           use_base_dir="false" type="BINDATA" compress="gzip" />
@@ -34,6 +37,12 @@
           file="icons/brush.svg" type="BINDATA" compress="gzip" />
       <include name="IDR_NEW_TAB_PAGE_PENCIL_ICON_SVG"
           file="icons/icon_pencil.svg" type="BINDATA" compress="gzip" />
+      <include name="IDR_NEW_TAB_PAGE_LINK_ICON_SVG"
+          file="icons/link.svg" type="BINDATA" compress="gzip" />
+      <include name="IDR_NEW_TAB_PAGE_BACKGROUNDS_ICON_SVG"
+          file="icons/backgrounds.svg" type="BINDATA" compress="gzip" />
+      <include name="IDR_NEW_TAB_PAGE_COLORS_ICON_SVG"
+          file="icons/colors.svg" type="BINDATA" compress="gzip" />
     </includes>
     <structures>
       <structure name="IDR_NEW_TAB_PAGE_NEW_TAB_PAGE_HTML"
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.html b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.html
index d549c55..1237f26 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.html
@@ -5,9 +5,7 @@
 <link rel="import" href="chrome://resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html">
 <link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/cr_elements/shared_style_css.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-spinner/paper-spinner-lite.html">
 <link rel="import" href="../localized_link/localized_link.html">
 <link rel="import" href="../../i18n_setup.html">
 <link rel="import" href="cups_add_printer_dialog_elements.html">
@@ -15,66 +13,6 @@
 <link rel="import" href="cups_printer_shared_css.html">
 <link rel="import" href="cups_printers_browser_proxy.html">
 
-<dom-module id="add-printer-discovery-dialog">
-  <template>
-    <style include="cr-shared-style cups-printer-shared">
-      add-printer-list {
-        max-height: 310px;
-        overflow-y: auto;
-        position: absolute;
-        width: 100%;
-      }
-
-      #searchSpinner {
-        position: absolute;
-        top: 80%;
-      }
-
-      #searchSpinner paper-spinner-lite {
-        --paper-spinner-stroke-width: 2px;
-        height: 15px;
-        margin-inline-end: 3px;
-        margin-inline-start: 20px;
-        width: 15px;
-      }
-    </style>
-    <add-printer-dialog>
-      <div slot="dialog-title">$i18n{addPrintersNearbyTitle}</div>
-      <div slot="dialog-body">
-        <add-printer-list printers="[[discoveredPrinters]]"
-            selected-printer="{{selectedPrinter}}">
-        </add-printer-list>
-        <div class="center" id="noPrinterMessage"
-            hidden="[[discoveredPrinters.length]]">
-           $i18n{noPrinterNearbyMessage}
-        </div>
-        <div id="searchSpinner" hidden="[[!discovering_]]">
-          <paper-spinner-lite active="[[discovering_]]"></paper-spinner-lite>
-          <span>$i18n{searchingNearbyPrinters}</span>
-        </div>
-      </div>
-      <div slot="dialog-buttons">
-        <div>  <!-- Left group -->
-          <cr-button id="manuallyAddPrinterButton"
-              on-click="switchToManualAddDialog_">
-            $i18n{manuallyAddPrinterButtonText}
-          </cr-button>
-        </div>
-        <div>  <!-- Right group -->
-          <cr-button class="cancel-button" on-click="onCancelTap_">
-            $i18n{cancel}
-          </cr-button>
-          <cr-button class="action-button" id="addPrinterButton"
-              disabled="[[!canAddPrinter_(selectedPrinter)]]"
-              on-click="switchToConfiguringDialog_">
-            $i18n{addPrinterButtonText}
-          </cr-button>
-        </div>
-      </div>
-    </add-printer-dialog>
-  </template>
-</dom-module>
-
 <dom-module id="add-printer-manually-dialog">
   <template>
     <style include="cups-printer-shared"></style>
@@ -127,16 +65,12 @@
         </div>
       </div>
       <div slot="dialog-buttons">
-        <div>  <!-- Left group -->
-          <cr-button on-click="switchToDiscoveryDialog_"
-              hidden="[[enableUpdatedUi]]">
-            $i18n{discoverPrintersButtonText}
-          </cr-button>
-        </div>
-        <div>  <!-- Right group -->
+        <div>
           <cr-button class="cancel-button" on-click="onCancelTap_">
             $i18n{cancel}
           </cr-button>
+        </div>
+        <div>
           <cr-button id="addPrinterButton" class="action-button"
               on-click="addPressed_"
               disabled="[[!canAddPrinter_(newPrinter.*,
@@ -230,39 +164,10 @@
   </template>
 </dom-module>
 
-<dom-module id="add-printer-configuring-dialog">
-  <template>
-    <style include="cups-printer-shared">
-      [slot='dialog-body'] {
-        padding-top: 140px;
-        text-align: center;
-      }
-    </style>
-    <add-printer-dialog>
-      <div slot="dialog-title">[[dialogTitle]]</div>
-      <div slot="dialog-body">
-        <paper-spinner-lite active></paper-spinner-lite>
-        <div id="configuringMessage"></div>
-      </div>
-      <div slot="dialog-buttons">
-        <cr-button class="cancel-button" on-click="onCloseConfiguringTap_">
-          $i18n{close}
-        </cr-button>
-      </div>
-    </add-printer-dialog>
-  </template>
-</dom-module>
-
 <dom-module id="settings-cups-add-printer-dialog">
   <template>
     <style include="settings-shared"></style>
 
-    <!-- Printer Discovery Dialog -->
-    <template is="dom-if" if="[[showDiscoveryDialog_]]" restamp>
-      <add-printer-discovery-dialog selected-printer="{{newPrinter}}">
-      </add-printer-discovery-dialog>
-    </template>
-
     <!-- Manually Add Printer Dialog -->
     <template is="dom-if" if="[[showManuallyAddDialog_]]" restamp>
       <add-printer-manually-dialog new-printer="{{newPrinter}}"
@@ -270,14 +175,6 @@
       </add-printer-manually-dialog>
     </template>
 
-    <!-- Configuring Printer Dialog -->
-    <template is="dom-if" if="[[showConfiguringDialog_]]" restamp>
-      <add-printer-configuring-dialog
-          printer-name="[[newPrinter.printerName]]"
-          dialog-title="[[configuringDialogTitle]]">
-      </add-printer-configuring-dialog>
-    </template>
-
     <!-- Manufacturer and Model Dialog -->
     <template is="dom-if" if="[[showManufacturerDialog_]]" restamp>
       <add-printer-manufacturer-model-dialog active-printer="{{newPrinter}}">
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js
index b16efbf..f8faa14 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.js
@@ -6,11 +6,8 @@
  * @fileoverview 'settings-cups-add-printer-dialog' includes multiple dialogs to
  * set up a new CUPS printer.
  * Subdialogs include:
- * - 'add-printer-discovery-dialog' is a dialog showing discovered printers on
- *   the network that are available for setup.
  * - 'add-printer-manually-dialog' is a dialog in which user can manually enter
  *   the information to set up a new printer.
- * - 'add-printer-configuring-dialog' is the configuring-in-progress dialog.
  * - 'add-printer-manufacturer-model-dialog' is a dialog in which the user can
  *   manually select the manufacture and model of the new printer.
  */
@@ -20,20 +17,11 @@
  * @enum {string}
  */
 const AddPrinterDialogs = {
-  DISCOVERY: 'add-printer-discovery-dialog',
   MANUALLY: 'add-printer-manually-dialog',
-  CONFIGURING: 'add-printer-configuring-dialog',
   MANUFACTURER: 'add-printer-manufacturer-model-dialog',
 };
 
 /**
- * The maximum height of the discovered printers list when the searching spinner
- * is not showing.
- * @type {number}
- */
-const kPrinterListFullHeight = 350;
-
-/**
  * Return a reset CupsPrinterInfo object.
  *  @return {!CupsPrinterInfo}
  */
@@ -61,132 +49,6 @@
 }
 
 Polymer({
-  is: 'add-printer-discovery-dialog',
-
-  behaviors: [WebUIListenerBehavior],
-
-  properties: {
-    /** @type {!Array<!CupsPrinterInfo>|undefined} */
-    discoveredPrinters: {
-      type: Array,
-      value: () => [],
-    },
-
-    /** @type {!CupsPrinterInfo} */
-    selectedPrinter: {
-      type: Object,
-      notify: true,
-    },
-
-    discovering_: {
-      type: Boolean,
-      value: true,
-    },
-
-    /**
-     * TODO(jimmyxgong): Remove this feature flag conditional once feature
-     * is launched.
-     * @private
-     */
-    enableUpdatedUi: Boolean,
-  },
-
-  /** @override */
-  ready: function() {
-    if (this.enableUpdatedUi) {
-      return;
-    }
-
-    settings.CupsPrintersBrowserProxyImpl.getInstance()
-        .startDiscoveringPrinters();
-    this.addWebUIListener(
-        'on-nearby-printers-changed', this.onNearbyPrintersChanged_.bind(this));
-    this.addWebUIListener(
-        'on-printer-discovery-done', this.onPrinterDiscoveryDone_.bind(this));
-  },
-
-  close: function() {
-    this.$$('add-printer-dialog').close();
-  },
-
-  /**
-   * @param {!Array<!CupsPrinterInfo>} automaticPrinters
-   * @param {!Array<!CupsPrinterInfo>} discoveredPrinters
-   * @private
-   */
-  onNearbyPrintersChanged_: function(automaticPrinters, discoveredPrinters) {
-    this.discovering_ = true;
-    this.set(
-        'discoveredPrinters', automaticPrinters.concat(discoveredPrinters));
-  },
-
-  /** @private */
-  onPrinterDiscoveryDone_: function() {
-    this.discovering_ = false;
-    this.$$('add-printer-list').style.maxHeight = kPrinterListFullHeight + 'px';
-
-    if (!this.discoveredPrinters.length) {
-      this.selectedPrinter = getEmptyPrinter_();
-      this.fire('no-detected-printer');
-    }
-  },
-
-  /** @private */
-  stopDiscoveringPrinters_: function() {
-    settings.CupsPrintersBrowserProxyImpl.getInstance()
-        .stopDiscoveringPrinters();
-    this.discovering_ = false;
-  },
-
-  /** @private */
-  switchToManualAddDialog_: function() {
-    // We're abandoning discovery in favor of manual specification, so
-    // drop the selection if one exists.
-    this.selectedPrinter = getEmptyPrinter_();
-    this.close();
-    this.fire('open-manually-add-printer-dialog');
-
-    if (this.enableUpdatedUi) {
-      return;
-    }
-
-    this.stopDiscoveringPrinters_();
-  },
-
-  /** @private */
-  onCancelTap_: function() {
-    this.close();
-
-    if (this.enableUpdatedUi) {
-      return;
-    }
-
-    this.stopDiscoveringPrinters_();
-  },
-
-  /** @private */
-  switchToConfiguringDialog_: function() {
-    this.close();
-    this.fire('open-configuring-printer-dialog');
-
-    if (this.enableUpdatedUi) {
-      return;
-    }
-
-    this.stopDiscoveringPrinters_();
-  },
-
-  /**
-   * @param {?CupsPrinterInfo} selectedPrinter
-   * @return {boolean} Whether the add printer button is enabled.
-   * @private
-   */
-  canAddPrinter_: function(selectedPrinter) {
-    return !!selectedPrinter && !!selectedPrinter.printerName;
-  },
-});
-
-Polymer({
   is: 'add-printer-manually-dialog',
 
   properties: {
@@ -221,13 +83,6 @@
   ],
 
   /** @private */
-  switchToDiscoveryDialog_: function() {
-    this.newPrinter = getEmptyPrinter_();
-    this.$$('add-printer-dialog').close();
-    this.fire('open-discovery-printers-dialog');
-  },
-
-  /** @private */
   onCancelTap_: function() {
     this.$$('add-printer-dialog').close();
   },
@@ -608,30 +463,6 @@
 });
 
 Polymer({
-  is: 'add-printer-configuring-dialog',
-
-  properties: {
-    printerName: String,
-    dialogTitle: String,
-  },
-
-  /** @override */
-  attached: function() {
-    this.$.configuringMessage.textContent =
-        loadTimeData.getStringF('printerConfiguringMessage', this.printerName);
-  },
-
-  /** @private */
-  onCloseConfiguringTap_: function() {
-    this.close();
-  },
-
-  close: function() {
-    this.$$('add-printer-dialog').close();
-  },
-});
-
-Polymer({
   is: 'settings-cups-add-printer-dialog',
 
   properties: {
@@ -640,8 +471,6 @@
       type: Object,
     },
 
-    configuringDialogTitle: String,
-
     /** @private {string} */
     previousDialog_: String,
 
@@ -649,24 +478,12 @@
     currentDialog_: String,
 
     /** @private {boolean} */
-    showDiscoveryDialog_: {
-      type: Boolean,
-      value: false,
-    },
-
-    /** @private {boolean} */
     showManuallyAddDialog_: {
       type: Boolean,
       value: false,
     },
 
     /** @private {boolean} */
-    showConfiguringDialog_: {
-      type: Boolean,
-      value: false,
-    },
-
-    /** @private {boolean} */
     showManufacturerDialog_: {
       type: Boolean,
       value: false,
@@ -682,25 +499,18 @@
 
   listeners: {
     'open-manually-add-printer-dialog': 'openManuallyAddPrinterDialog_',
-    'open-configuring-printer-dialog': 'openConfiguringPrinterDialog_',
-    'open-discovery-printers-dialog': 'openDiscoveryPrintersDialog_',
     'open-manufacturer-model-dialog':
         'openManufacturerModelDialogForCurrentPrinter_',
     'no-detected-printer': 'onNoDetectedPrinter_',
   },
 
-  /** Opens the Add printer discovery dialog. */
+  /** Opens the Add manual printer dialog. */
   open: function() {
     this.resetData_();
-    if (this.enableUpdatedUi) {
-      // The updated UI will remove the discovery dialog. Open the manual
-      // dialog by default.
-      this.switchDialog_(
-          '', AddPrinterDialogs.MANUALLY, 'showManuallyAddDialog_');
-    } else {
-      this.switchDialog_(
-          '', AddPrinterDialogs.DISCOVERY, 'showDiscoveryDialog_');
-    }
+    // The updated UI will remove the discovery dialog. Open the manual
+    // dialog by default.
+    this.switchDialog_(
+        '', AddPrinterDialogs.MANUALLY, 'showManuallyAddDialog_');
   },
 
   /**
@@ -721,37 +531,6 @@
   },
 
   /** @private */
-  openDiscoveryPrintersDialog_: function() {
-    this.switchDialog_(
-        this.currentDialog_, AddPrinterDialogs.DISCOVERY,
-        'showDiscoveryDialog_');
-  },
-
-  /** @private */
-  switchToManufacturerDialog_: function() {
-    this.$$('add-printer-configuring-dialog').close();
-    this.openManufacturerModelDialogForCurrentPrinter_();
-  },
-
-  /** @private */
-  openConfiguringPrinterDialog_: function() {
-    this.switchDialog_(
-        this.currentDialog_, AddPrinterDialogs.CONFIGURING,
-        'showConfiguringDialog_');
-    if (this.previousDialog_ == AddPrinterDialogs.DISCOVERY) {
-      this.configuringDialogTitle =
-          loadTimeData.getString('addPrintersNearbyTitle');
-      settings.CupsPrintersBrowserProxyImpl.getInstance()
-          .addDiscoveredPrinter(this.newPrinter.printerId)
-          .then(
-              this.onAddingDiscoveredPrinterSucceeded_.bind(this),
-              this.manuallyAddDiscoveredPrinter_.bind(this));
-    } else {
-      assertNotReached('Opening configuring dialog from invalid place');
-    }
-  },
-
-  /** @private */
   openManufacturerModelDialogForCurrentPrinter_: function() {
     this.switchDialog_(
         this.currentDialog_, AddPrinterDialogs.MANUFACTURER,
@@ -765,18 +544,6 @@
         '', AddPrinterDialogs.MANUFACTURER, 'showManufacturerDialog_');
   },
 
-  /** @private */
-  onNoDetectedPrinter_: function() {
-    // If there is no detected printer, automatically open manually-add-printer
-    // dialog only when the user opens the discovery-dialog through the
-    // "ADD PRINTER" button.
-    if (!this.previousDialog_) {
-      this.$$('add-printer-discovery-dialog').close();
-      this.newPrinter = getEmptyPrinter_();
-      this.openManuallyAddPrinterDialog_();
-    }
-  },
-
   /**
    * Switch dialog from |fromDialog| to |toDialog|.
    * @param {string} fromDialog
@@ -797,42 +564,4 @@
       });
     });
   },
-
-  /**
-   * Handler for addDiscoveredPrinter.
-   * @param {!PrinterSetupResult} result
-   * @private
-   * */
-  onAddingDiscoveredPrinterSucceeded_: function(result) {
-    this.$$('add-printer-configuring-dialog').close();
-    this.fire(
-        'show-cups-printer-toast',
-        {resultCode: result, printerName: this.newPrinter.printerName});
-  },
-
-  /**
-   * Use the given printer as the starting point for a user-driven
-   * add of a printer.  This is called if we can't automatically configure
-   * the printer, and need more information from the user.
-   *
-   * @param {*} printer
-   * @private
-   */
-  manuallyAddDiscoveredPrinter_: function(printer) {
-    this.newPrinter = /** @type {CupsPrinterInfo} */ (printer);
-    this.switchToManufacturerDialog_();
-  },
-
-  /**
-   * @param {boolean} success
-   * @param {string} printerName
-   * @private
-   */
-  onAddPrinter_: function(success, printerName) {
-    // 'on-add-cups-printer' event might be triggered by editing an existing
-    // printer, in which case there is no configuring dialog.
-    if (this.$$('add-printer-configuring-dialog')) {
-      this.$$('add-printer-configuring-dialog').close();
-    }
-  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog_elements.html b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog_elements.html
index 1937bab5..d63d6cd 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog_elements.html
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog_elements.html
@@ -7,26 +7,6 @@
 <link rel="import" href="cups_printer_shared_css.html">
 <link rel="import" href="cups_printers_browser_proxy.html">
 
-<dom-module id="add-printer-list">
-  <template>
-    <style include="cups-printer-shared">
-      .list-item {
-        padding: 0 20px;
-      }
-    </style>
-    <div>
-      <array-selector id="arraySelector" items="[[printers]]"
-          selected="{{selectedPrinter}}">
-      </array-selector>
-      <template is="dom-repeat" items="[[printers]]" sort="sort_">
-        <button class="list-item" on-click="onSelect_">
-          [[item.printerName]]
-        </button>
-      </template>
-    </div>
-  </template>
-</dom-module>
-
 <dom-module id="add-printer-dialog">
   <template>
     <style include="settings-shared">
diff --git a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog_elements.js b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog_elements.js
index f527794..907ce4c 100644
--- a/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog_elements.js
+++ b/chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog_elements.js
@@ -2,43 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/** 'add-printers-list' is the list of discovered printers. */
-Polymer({
-  is: 'add-printer-list',
-
-  properties: {
-    /** @type {!Array<!CupsPrinterInfo>} */
-    printers: {
-      type: Array,
-      notify: true,
-    },
-
-    /** @type {!CupsPrinterInfo} */
-    selectedPrinter: {
-      type: Object,
-      notify: true,
-    },
-  },
-
-  /**
-   * @param {{model:Object}} event
-   * @private
-   */
-  onSelect_: function(event) {
-    this.selectedPrinter = event.model.item;
-  },
-
-  /**
-   * @param {!CupsPrinterInfo} first
-   * @param {!CupsPrinterInfo} second
-   * @return {number} The result of the comparison.
-   * @private
-   */
-  sort_: function(first, second) {
-    return settings.printing.alphabeticalSort(first, second);
-  },
-});
-
 /** 'add-printer-dialog' is the template of the Add Printer dialog. */
 Polymer({
   is: 'add-printer-dialog',
diff --git a/chrome/browser/resources/tab_strip/BUILD.gn b/chrome/browser/resources/tab_strip/BUILD.gn
index 3b755130..c95b0b72 100644
--- a/chrome/browser/resources/tab_strip/BUILD.gn
+++ b/chrome/browser/resources/tab_strip/BUILD.gn
@@ -12,6 +12,7 @@
     ":alert_indicators",
     ":custom_element",
     ":tab",
+    ":tab_group",
     ":tab_list",
     ":tab_strip_embedder_proxy",
     ":tab_strip_options",
@@ -45,6 +46,9 @@
   externs_list = [ "$externs_path/metrics_private.js" ]
 }
 
+js_library("tab_group") {
+}
+
 js_library("tab_list") {
   deps = [
     "//ui/webui/resources/js:util.m",
@@ -68,6 +72,7 @@
   deps = [
     ":alert_indicator_module",
     ":alert_indicators_module",
+    ":tab_group_module",
     ":tab_list_module",
     ":tab_module",
   ]
@@ -91,6 +96,12 @@
   html_type = "v3-ready"
 }
 
+polymer_modulizer("tab_group") {
+  js_file = "tab_group.js"
+  html_file = "tab_group.html"
+  html_type = "v3-ready"
+}
+
 polymer_modulizer("tab_list") {
   js_file = "tab_list.js"
   html_file = "tab_list.html"
diff --git a/chrome/browser/resources/tab_strip/tab_group.html b/chrome/browser/resources/tab_strip/tab_group.html
new file mode 100644
index 0000000..a64a8fa
--- /dev/null
+++ b/chrome/browser/resources/tab_strip/tab_group.html
@@ -0,0 +1,14 @@
+<style>
+:host {
+  display: block;
+}
+
+#tabs {
+  display: flex;
+  min-width: fit-content;
+}
+</style>
+
+<div id="tabs">
+  <slot></slot>
+</div>
diff --git a/chrome/browser/resources/tab_strip/tab_group.js b/chrome/browser/resources/tab_strip/tab_group.js
new file mode 100644
index 0000000..54e930fc
--- /dev/null
+++ b/chrome/browser/resources/tab_strip/tab_group.js
@@ -0,0 +1,13 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {CustomElement} from './custom_element.js';
+
+class TabGroupElement extends CustomElement {
+  static get template() {
+    return `{__html_template__}`;
+  }
+}
+
+customElements.define('tabstrip-tab-group', TabGroupElement);
diff --git a/chrome/browser/resources/tab_strip/tab_strip_resources.grd b/chrome/browser/resources/tab_strip/tab_strip_resources.grd
index c125f52..f3cccf5 100644
--- a/chrome/browser/resources/tab_strip/tab_strip_resources.grd
+++ b/chrome/browser/resources/tab_strip/tab_strip_resources.grd
@@ -28,6 +28,12 @@
           type="chrome_html"
           compress="gzip"/>
       <structure
+          name="IDR_TAB_STRIP_TAB_GROUP_JS"
+          file="${root_gen_dir}/chrome/browser/resources/tab_strip/tab_group.js"
+          use_base_dir="false"
+          type="chrome_html"
+          compress="gzip"/>
+      <structure
           name="IDR_TAB_STRIP_TAB_LIST_JS"
           file="${root_gen_dir}/chrome/browser/resources/tab_strip/tab_list.js"
           use_base_dir="false"
diff --git a/chrome/browser/resources/vr/OWNERS b/chrome/browser/resources/vr/OWNERS
index f8d52f3..49ca9b4b 100644
--- a/chrome/browser/resources/vr/OWNERS
+++ b/chrome/browser/resources/vr/OWNERS
@@ -1,6 +1,5 @@
 alcooper@chromium.org
 bialpio@chromium.org
-cjgrant@chromium.org
 tiborg@chromium.org
 
 # TEAM: xr-dev@chromium.org
diff --git a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
index 538a473..4c4616cb 100644
--- a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
@@ -151,9 +151,6 @@
       NOTREACHED();
       break;
   }
-  UMA_HISTOGRAM_ENUMERATION(
-      "SBIRS.PSSLoadResult", static_cast<uint32_t>(result),
-      static_cast<uint32_t>(PlatformStateStoreLoadResult::NUM_RESULTS));
   return value_dict;
 #else
   return nullptr;
@@ -164,7 +161,6 @@
 #if defined(USE_PLATFORM_STATE_STORE)
   std::string data;
   SerializeIncidentsSent(incidents_sent, &data);
-  UMA_HISTOGRAM_COUNTS_1M("SBIRS.PSSDataStoreSize", data.size());
   WriteStoreData(profile, data);
 #endif
 }
diff --git a/chrome/browser/settings/BUILD.gn b/chrome/browser/settings/BUILD.gn
index a7607eb..f3fe65cf 100644
--- a/chrome/browser/settings/BUILD.gn
+++ b/chrome/browser/settings/BUILD.gn
@@ -15,6 +15,7 @@
     "android/widget/java/src/org/chromium/chrome/browser/settings/ChromeBasePreference.java",
     "android/widget/java/src/org/chromium/chrome/browser/settings/ChromeImageViewPreference.java",
     "android/widget/java/src/org/chromium/chrome/browser/settings/ChromeSwitchPreference.java",
+    "android/widget/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java",
     "android/widget/java/src/org/chromium/chrome/browser/settings/TextMessagePreference.java",
   ]
   deps = [
diff --git a/chrome/android/java/res/layout/preference_spinner.xml b/chrome/browser/settings/android/java/res/layout/preference_spinner.xml
similarity index 100%
rename from chrome/android/java/res/layout/preference_spinner.xml
rename to chrome/browser/settings/android/java/res/layout/preference_spinner.xml
diff --git a/chrome/android/java/res/layout/preference_spinner_single_line.xml b/chrome/browser/settings/android/java/res/layout/preference_spinner_single_line.xml
similarity index 100%
rename from chrome/android/java/res/layout/preference_spinner_single_line.xml
rename to chrome/browser/settings/android/java/res/layout/preference_spinner_single_line.xml
diff --git a/chrome/android/java/res/layout/preference_spinner_single_line_item.xml b/chrome/browser/settings/android/java/res/layout/preference_spinner_single_line_item.xml
similarity index 100%
rename from chrome/android/java/res/layout/preference_spinner_single_line_item.xml
rename to chrome/browser/settings/android/java/res/layout/preference_spinner_single_line_item.xml
diff --git a/chrome/browser/settings/android/java/res/values/attrs.xml b/chrome/browser/settings/android/java/res/values/attrs.xml
index 0eccb76..31234f0 100644
--- a/chrome/browser/settings/android/java/res/values/attrs.xml
+++ b/chrome/browser/settings/android/java/res/values/attrs.xml
@@ -8,4 +8,11 @@
         <!-- The tint color for the icon set by android:icon. -->
         <attr name="iconTint" format="color" />
     </declare-styleable>
+
+    <declare-styleable name="SpinnerPreference">
+        <!-- Used to change the SpinnerPreference to display the TextView and the Spinner on the
+             same line. Devices with a smallest width of less than 360dp will still use two separate
+             lines. -->
+        <attr name="singleLine" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/chrome/browser/settings/android/java/res/values/styles.xml b/chrome/browser/settings/android/java/res/values/styles.xml
index 26f612d4..e28403ed 100644
--- a/chrome/browser/settings/android/java/res/values/styles.xml
+++ b/chrome/browser/settings/android/java/res/values/styles.xml
@@ -49,4 +49,11 @@
     </style>
 
     <style name="PreferenceLayout" parent="PreferenceLayoutBase" />
+
+    <style name="PreferenceSpinnerUnderlineView">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">1dp</item>
+        <item name="android:layout_marginTop">2dp</item>
+        <item name="android:background">@color/modern_grey_600</item>
+    </style>
 </resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java b/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java
similarity index 96%
rename from chrome/android/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java
rename to chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java
index 34c8068..01d32d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java
+++ b/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java
@@ -15,8 +15,6 @@
 import android.widget.Spinner;
 import android.widget.TextView;
 
-import org.chromium.chrome.R;
-
 /**
  * A preference that takes value from a specified list of objects, presented as a dropdown.
  */
@@ -93,8 +91,7 @@
         mSpinner = (Spinner) holder.findViewById(R.id.spinner);
         mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
             @Override
-            public void onItemSelected(
-                    AdapterView<?> parent, View view, int position, long id) {
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                 mSelectedIndex = position;
                 if (getOnPreferenceChangeListener() != null) {
                     getOnPreferenceChangeListener().onPreferenceChange(
diff --git a/chrome/browser/ui/find_bar/find_bar_controller.cc b/chrome/browser/ui/find_bar/find_bar_controller.cc
index be297e4..37a11c5 100644
--- a/chrome/browser/ui/find_bar/find_bar_controller.cc
+++ b/chrome/browser/ui/find_bar/find_bar_controller.cc
@@ -13,10 +13,6 @@
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
-#include "chrome/browser/ui/browser_finder.h"
-#include "chrome/browser/ui/browser_list.h"
-#include "chrome/browser/ui/browser_list_observer.h"
-#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/find_bar/find_bar.h"
 #include "chrome/browser/ui/find_bar/find_bar_state.h"
 #include "chrome/browser/ui/find_bar/find_bar_state_factory.h"
@@ -40,67 +36,13 @@
 // The minimum space between the FindInPage window and the search result.
 constexpr int kMinFindWndDistanceFromSelection = 5;
 
-// Tracks windows and makes sure that closing the last Guest browser window
-// clears the find pre-populate text.
-class FindBrowserListObserver : public BrowserListObserver {
- public:
-  FindBrowserListObserver() {
-    BrowserList::AddObserver(this);
-  }
-
-  ~FindBrowserListObserver() override { BrowserList::RemoveObserver(this); }
-
-  static void EnsureInstance() {
-    static base::NoDestructor<FindBrowserListObserver> the_instance;
-    the_instance.get();
-  }
-
- protected:
-  // BrowserListObserver:
-  void OnBrowserRemoved(Browser* browser) override {
-    Profile* const guest_profile = GetGuestProfile(browser);
-    if (!guest_profile)
-      return;
-
-    if (IsGuestWindowOpen())
-      return;
-
-    // Remove persistent find text across guest sessions. If we don't do this, a
-    // future guest session in this browser process might get its find text
-    // prepopulated with something that was searched in this session, which is a
-    // violation of privacy expectations.
-    FindBarState* const find_bar_state =
-        FindBarStateFactory::GetForProfile(guest_profile);
-    find_bar_state->set_last_prepopulate_text(base::string16());
-  }
-
- private:
-  // Returns a guest profile if the current browser has one, or nullptr
-  // otherwise.
-  static Profile* GetGuestProfile(Browser* browser) {
-    Profile* profile = browser->profile();
-    DCHECK(profile);
-    return profile->IsGuestSession() ? profile : nullptr;
-  }
-
-  static bool IsGuestWindowOpen() {
-    for (Browser* other : *BrowserList::GetInstance()) {
-      if (GetGuestProfile(other))
-        return true;
-    }
-    return false;
-  }
-};
-
 }  // namespace
 
 FindBarController::FindBarController(std::unique_ptr<FindBar> find_bar,
                                      Browser* browser)
     : find_bar_(std::move(find_bar)),
       browser_(browser),
-      find_bar_platform_helper_(FindBarPlatformHelper::Create(this)) {
-  FindBrowserListObserver::EnsureInstance();
-}
+      find_bar_platform_helper_(FindBarPlatformHelper::Create(this)) {}
 
 FindBarController::~FindBarController() {
   DCHECK(!web_contents_);
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_item.cc b/chrome/browser/ui/global_media_controls/cast_media_notification_item.cc
index 10726abc..db69063 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_item.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_item.cc
@@ -167,6 +167,25 @@
   session_controller_->OnMediaStatusUpdated(std::move(status));
 }
 
+void CastMediaNotificationItem::OnRouteUpdated(
+    const media_router::MediaRoute& route) {
+  DCHECK_EQ(route.media_route_id(), media_route_id_);
+  bool updated = false;
+  const base::string16 new_source_title =
+      base::UTF8ToUTF16(route.media_sink_name());
+  if (metadata_.source_title != new_source_title) {
+    metadata_.source_title = new_source_title;
+    updated = true;
+  }
+  const base::string16 new_artist = base::UTF8ToUTF16(route.description());
+  if (metadata_.artist != new_artist) {
+    metadata_.artist = new_artist;
+    updated = true;
+  }
+  if (updated && view_)
+    view_->UpdateWithMediaMetadata(metadata_);
+}
+
 mojo::PendingRemote<media_router::mojom::MediaStatusObserver>
 CastMediaNotificationItem::GetObserverPendingRemote() {
   return observer_receiver_.BindNewPipeAndPassRemote();
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_item.h b/chrome/browser/ui/global_media_controls/cast_media_notification_item.h
index ff70b94..1e8e81f 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_item.h
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_item.h
@@ -57,6 +57,8 @@
   void OnMediaStatusUpdated(
       media_router::mojom::MediaStatusPtr status) override;
 
+  void OnRouteUpdated(const media_router::MediaRoute& route);
+
   // Returns a pending remote bound to |this|. This should not be called more
   // than once per instance.
   mojo::PendingRemote<media_router::mojom::MediaStatusObserver>
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_provider.cc b/chrome/browser/ui/global_media_controls/cast_media_notification_provider.cc
index a6ddb09..973c7d5f 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_provider.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_provider.cc
@@ -49,9 +49,11 @@
             media_router::RouteControllerType::kGeneric) {
       continue;
     }
-    if (std::find_if(items_.begin(), items_.end(), [&route](const auto& item) {
+    auto item_it =
+        std::find_if(items_.begin(), items_.end(), [&route](const auto& item) {
           return item.first == route.media_route_id();
-        }) == items_.end()) {
+        });
+    if (item_it == items_.end()) {
       mojo::Remote<media_router::mojom::MediaController> controller_remote;
       mojo::PendingReceiver<media_router::mojom::MediaController>
           controller_receiver = controller_remote.BindNewPipeAndPassReceiver();
@@ -65,6 +67,8 @@
       router_->GetMediaController(
           route.media_route_id(), std::move(controller_receiver),
           it_pair.first->second.GetObserverPendingRemote());
+    } else {
+      item_it->second.OnRouteUpdated(route);
     }
   }
   if (HasItems() != had_items)
diff --git a/chrome/browser/ui/global_media_controls/cast_media_notification_provider_unittest.cc b/chrome/browser/ui/global_media_controls/cast_media_notification_provider_unittest.cc
index 1f7213ec..5e3f8f48 100644
--- a/chrome/browser/ui/global_media_controls/cast_media_notification_provider_unittest.cc
+++ b/chrome/browser/ui/global_media_controls/cast_media_notification_provider_unittest.cc
@@ -7,12 +7,24 @@
 #include "chrome/browser/media/router/test/mock_media_router.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/media_message_center/media_notification_controller.h"
+#include "components/media_message_center/media_notification_view.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using media_router::MediaRoute;
+using testing::_;
+
 namespace {
 
+MediaRoute CreateRoute(const std::string& route_id) {
+  media_router::MediaRoute route(route_id,
+                                 media_router::MediaSource("source_id"),
+                                 "sink_id", "description", true, true);
+  route.set_controller_type(media_router::RouteControllerType::kGeneric);
+  return route;
+}
+
 class MockMediaNotificationController
     : public media_message_center::MediaNotificationController {
  public:
@@ -28,6 +40,23 @@
   MOCK_METHOD1(LogMediaSessionActionButtonPressed, void(const std::string& id));
 };
 
+class MockMediaNotificationView
+    : public media_message_center::MediaNotificationView {
+ public:
+  MOCK_METHOD1(SetExpanded, void(bool));
+  MOCK_METHOD2(UpdateCornerRadius, void(int, int));
+  MOCK_METHOD1(SetForcedExpandedState, void(bool*));
+  MOCK_METHOD1(UpdateWithMediaSessionInfo,
+               void(const media_session::mojom::MediaSessionInfoPtr&));
+  MOCK_METHOD1(UpdateWithMediaMetadata,
+               void(const media_session::MediaMetadata&));
+  MOCK_METHOD1(
+      UpdateWithMediaActions,
+      void(const base::flat_set<media_session::mojom::MediaSessionAction>&));
+  MOCK_METHOD1(UpdateWithMediaArtwork, void(const gfx::ImageSkia&));
+  MOCK_METHOD1(UpdateWithFavicon, void(const gfx::ImageSkia&));
+};
+
 class MockClosure {
  public:
   MOCK_METHOD0(Run, void());
@@ -57,10 +86,7 @@
 
 TEST_F(CastMediaNotificationProviderTest, AddAndRemoveRoute) {
   const std::string route_id = "route-id-1";
-  media_router::MediaRoute route(route_id,
-                                 media_router::MediaSource("source_id"),
-                                 "sink_id", "description", true, true);
-  route.set_controller_type(media_router::RouteControllerType::kGeneric);
+  MediaRoute route = CreateRoute(route_id);
 
   EXPECT_CALL(items_changed_callback_, Run());
   notification_provider_->OnRoutesUpdated({route}, {});
@@ -73,3 +99,26 @@
   testing::Mock::VerifyAndClearExpectations(&items_changed_callback_);
   EXPECT_FALSE(notification_provider_->HasItems());
 }
+
+TEST_F(CastMediaNotificationProviderTest, UpdateRoute) {
+  const std::string route_id = "route-id-1";
+  MediaRoute route = CreateRoute(route_id);
+
+  notification_provider_->OnRoutesUpdated({route}, {});
+  auto* item = static_cast<CastMediaNotificationItem*>(
+      notification_provider_->GetNotificationItem(route_id).get());
+  MockMediaNotificationView view;
+  item->SetView(&view);
+
+  const std::string new_sink = "new sink";
+  const std::string new_description = "new description";
+  route.set_media_sink_name(new_sink);
+  route.set_description(new_description);
+
+  EXPECT_CALL(view, UpdateWithMediaMetadata(_))
+      .WillOnce([&](const media_session::MediaMetadata& metadata) {
+        EXPECT_EQ(base::UTF8ToUTF16(new_sink), metadata.source_title);
+        EXPECT_EQ(base::UTF8ToUTF16(new_description), metadata.artist);
+      });
+  notification_provider_->OnRoutesUpdated({route}, {});
+}
diff --git a/chrome/browser/ui/hid/hid_chooser_controller.cc b/chrome/browser/ui/hid/hid_chooser_controller.cc
index 322e951..d374bf1 100644
--- a/chrome/browser/ui/hid/hid_chooser_controller.cc
+++ b/chrome/browser/ui/hid/hid_chooser_controller.cc
@@ -47,7 +47,7 @@
 
 HidChooserController::~HidChooserController() {
   if (callback_)
-    std::move(callback_).Run(nullptr);
+    std::move(callback_).Run(std::vector<device::mojom::HidDeviceInfoPtr>());
 }
 
 bool HidChooserController::ShouldShowHelpButton() const {
@@ -68,7 +68,10 @@
 
 base::string16 HidChooserController::GetOption(size_t index) const {
   DCHECK_LT(index, devices_.size());
-  const device::mojom::HidDeviceInfo& device = *devices_[index];
+  auto it = devices_.begin();
+  std::advance(it, index);
+  DCHECK_GT(it->second.size(), 0u);
+  auto& device = *it->second[0];
   if (device.product_name.empty()) {
     return l10n_util::GetStringFUTF16(
         IDS_HID_CHOOSER_ITEM_WITHOUT_NAME,
@@ -87,8 +90,17 @@
   if (!chooser_context_)
     return false;
 
-  return chooser_context_->HasDevicePermission(
-      requesting_origin_, embedding_origin_, *devices_[index]);
+  auto it = devices_.begin();
+  std::advance(it, index);
+  DCHECK_GT(it->second.size(), 0u);
+  for (auto& device : it->second) {
+    if (!chooser_context_->HasDevicePermission(requesting_origin_,
+                                               embedding_origin_, *device)) {
+      return false;
+    }
+  }
+
+  return true;
 }
 
 void HidChooserController::Select(const std::vector<size_t>& indices) {
@@ -98,13 +110,18 @@
   DCHECK_LT(index, devices_.size());
 
   if (!chooser_context_) {
-    std::move(callback_).Run(nullptr);
+    std::move(callback_).Run(std::vector<device::mojom::HidDeviceInfoPtr>());
     return;
   }
 
-  chooser_context_->GrantDevicePermission(requesting_origin_, embedding_origin_,
-                                          *devices_[index]);
-  std::move(callback_).Run(std::move(devices_[index]));
+  auto it = devices_.begin();
+  std::advance(it, index);
+  DCHECK_GT(it->second.size(), 0u);
+  for (auto& device : it->second) {
+    chooser_context_->GrantDevicePermission(requesting_origin_,
+                                            embedding_origin_, *device);
+  }
+  std::move(callback_).Run(std::move(it->second));
 }
 
 void HidChooserController::Cancel() {
@@ -128,8 +145,18 @@
     if (ShouldExcludeDevice(*device))
       continue;
 
-    if (FilterMatchesAny(*device))
-      devices_.push_back(std::move(device));
+    if (FilterMatchesAny(*device)) {
+      // A single physical device may expose multiple HID interfaces, each
+      // represented by a HidDeviceInfo object. When a device exposes multiple
+      // HID interfaces, the HidDeviceInfo objects will share a common
+      // |physical_device_id|. Group these devices so that a single chooser item
+      // is shown for each physical device. If a device's physical device ID is
+      // empty, use its GUID instead.
+      const std::string& key = device->physical_device_id.empty()
+                                   ? device->guid
+                                   : device->physical_device_id;
+      devices_[key].push_back(std::move(device));
+    }
   }
 
   if (view())
diff --git a/chrome/browser/ui/hid/hid_chooser_controller.h b/chrome/browser/ui/hid/hid_chooser_controller.h
index 106229b5..3a8926e 100644
--- a/chrome/browser/ui/hid/hid_chooser_controller.h
+++ b/chrome/browser/ui/hid/hid_chooser_controller.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_HID_HID_CHOOSER_CONTROLLER_H_
 #define CHROME_BROWSER_UI_HID_HID_CHOOSER_CONTROLLER_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
@@ -63,7 +64,12 @@
   // The lifetime of the chooser context is tied to the browser context used to
   // create it, and may be destroyed while the chooser is still active.
   base::WeakPtr<HidChooserContext> chooser_context_;
-  std::vector<device::mojom::HidDeviceInfoPtr> devices_;
+
+  // Information about connected devices and their HID interfaces. A single
+  // physical device may expose multiple HID interfaces. Keys are physical
+  // device IDs, values are collections of HidDeviceInfo objects representing
+  // the HID interfaces hosted by the physical device.
+  std::map<std::string, std::vector<device::mojom::HidDeviceInfoPtr>> devices_;
 
   base::WeakPtrFactory<HidChooserController> weak_factory_{this};
 
diff --git a/chrome/browser/ui/hid/hid_chooser_controller_unittest.cc b/chrome/browser/ui/hid/hid_chooser_controller_unittest.cc
index 211b91d..a4e02bb 100644
--- a/chrome/browser/ui/hid/hid_chooser_controller_unittest.cc
+++ b/chrome/browser/ui/hid/hid_chooser_controller_unittest.cc
@@ -337,3 +337,49 @@
 
   EXPECT_EQ(3u, hid_chooser_controller->NumOptions());
 }
+
+TEST_F(HidChooserControllerTest, OneItemForSamePhysicalDevice) {
+  auto hid_chooser_controller = CreateHidChooserControllerWithoutFilters();
+
+  base::RunLoop run_loop;
+  fake_hid_chooser_view_.set_options_initialized_quit_closure(
+      run_loop.QuitClosure());
+
+  // These two devices have the same physical device ID and should be coalesced
+  // into a single chooser item.
+  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
+                            device::mojom::kPageGenericDesktop,
+                            device::mojom::kGenericDesktopGamePad);
+  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[0], 1, 1, "a", "001",
+                            device::mojom::kPageSimulation, 5);
+
+  // This device has the same info as the first device except for the physical
+  // device ID. It should have a separate chooser item.
+  CreateAndAddFakeHidDevice(kTestPhysicalDeviceIds[1], 1, 1, "a", "001",
+                            device::mojom::kPageGenericDesktop,
+                            device::mojom::kGenericDesktopGamePad);
+
+  run_loop.Run();
+
+  EXPECT_EQ(2u, hid_chooser_controller->NumOptions());
+}
+
+TEST_F(HidChooserControllerTest, NoMergeWithEmptyPhysicalDeviceId) {
+  auto hid_chooser_controller = CreateHidChooserControllerWithoutFilters();
+
+  base::RunLoop run_loop;
+  fake_hid_chooser_view_.set_options_initialized_quit_closure(
+      run_loop.QuitClosure());
+
+  // These two devices have an empty string for the physical device ID and
+  // should not be coalesced.
+  CreateAndAddFakeHidDevice("", 1, 1, "a", "001",
+                            device::mojom::kPageGenericDesktop,
+                            device::mojom::kGenericDesktopGamePad);
+  CreateAndAddFakeHidDevice("", 1, 1, "a", "001",
+                            device::mojom::kPageSimulation, 5);
+
+  run_loop.Run();
+
+  EXPECT_EQ(2u, hid_chooser_controller->NumOptions());
+}
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index 37082e4..a17d4256 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -1619,6 +1619,17 @@
   // TODO(msw): Test that AltGr+V does not paste.
 }
 
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, EditSearchEngines) {
+  OmniboxView* omnibox_view = nullptr;
+  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
+  EXPECT_TRUE(chrome::ExecuteCommand(browser(), IDC_EDIT_SEARCH_ENGINES));
+  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
+  const std::string target_url =
+      std::string(chrome::kChromeUISettingsURL) + chrome::kSearchEnginesSubPage;
+  EXPECT_EQ(ASCIIToUTF16(target_url), omnibox_view->GetText());
+  EXPECT_FALSE(omnibox_view->model()->popup_model()->IsOpen());
+}
+
 // Flaky test. The below suggestions are in a random order, and the injected
 // keys may or may not have registered. Probably https://crbug.com/751031,
 // but I believe the whole input mechanism needs to be re-architected.
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
index 3f9f26b..f568233 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
@@ -84,6 +84,7 @@
 #include "ui/views/controls/button/button.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/layout/animating_layout_manager.h"
+#include "ui/views/layout/animating_layout_manager_test_util.h"
 #include "ui/views/test/widget_test.h"
 
 using base::Bucket;
@@ -503,15 +504,10 @@
   void WaitForAnimationToComplete() {
     if (base::FeatureList::IsEnabled(
             features::kAutofillEnableToolbarStatusChip)) {
-      // Wait for animations to finish.
-      base::RunLoop loop;
-      static_cast<views::AnimatingLayoutManager*>(
+      views::test::WaitForAnimatingLayoutManager(
           BrowserView::GetBrowserViewForBrowser(browser())
               ->toolbar()
-              ->toolbar_account_icon_container()
-              ->GetLayoutManager())
-          ->PostOrQueueAction(loop.QuitClosure());
-      loop.Run();
+              ->toolbar_account_icon_container());
     }
   }
 
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
index 26fb56f3..ee9c48de 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -81,6 +81,7 @@
 #include "ui/views/controls/styled_label.h"
 #include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/layout/animating_layout_manager.h"
+#include "ui/views/layout/animating_layout_manager_test_util.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/window/non_client_view.h"
@@ -787,12 +788,8 @@
 
   void WaitForAnimationToEnd() {
     auto* const animating_layout = GetAnimatingLayoutManager();
-    if (animating_layout) {
-      // Wait for animations to finish.
-      base::RunLoop loop;
-      animating_layout->PostOrQueueAction(loop.QuitClosure());
-      loop.Run();
-    }
+    if (animating_layout)
+      views::test::WaitForAnimatingLayoutManager(animating_layout);
   }
 
   network::TestURLLoaderFactory* test_url_loader_factory() {
@@ -814,11 +811,10 @@
       return nullptr;
     }
 
-    return static_cast<views::AnimatingLayoutManager*>(
+    return views::test::GetAnimatingLayoutManager(
         BrowserView::GetBrowserViewForBrowser(browser())
             ->toolbar()
-            ->toolbar_account_icon_container()
-            ->GetLayoutManager());
+            ->toolbar_account_icon_container());
   }
 
   std::unique_ptr<autofill::EventWaiter<DialogEvent>> event_waiter_;
diff --git a/chrome/browser/ui/views/confirm_bubble_views.cc b/chrome/browser/ui/views/confirm_bubble_views.cc
index 5f25a23a..4b7ceb01c 100644
--- a/chrome/browser/ui/views/confirm_bubble_views.cc
+++ b/chrome/browser/ui/views/confirm_bubble_views.cc
@@ -119,13 +119,10 @@
   }
 }
 
-void ConfirmBubbleViews::ViewHierarchyChanged(
-    const views::ViewHierarchyChangedDetails& details) {
-  if (details.is_add && details.child == this && GetWidget()) {
-    GetWidget()->GetRootView()->GetViewAccessibility().OverrideDescribedBy(
-        label_);
-  }
-  DialogDelegateView::ViewHierarchyChanged(details);
+void ConfirmBubbleViews::AddedToWidget() {
+  GetWidget()->GetRootView()->GetViewAccessibility().OverrideDescribedBy(
+      label_);
+  DialogDelegateView::AddedToWidget();
 }
 
 namespace chrome {
diff --git a/chrome/browser/ui/views/confirm_bubble_views.h b/chrome/browser/ui/views/confirm_bubble_views.h
index 5f9e735..8791688a 100644
--- a/chrome/browser/ui/views/confirm_bubble_views.h
+++ b/chrome/browser/ui/views/confirm_bubble_views.h
@@ -49,8 +49,7 @@
   void ButtonPressed(views::Button* sender, const ui::Event& event) override;
 
   // views::View implementation.
-  void ViewHierarchyChanged(
-      const views::ViewHierarchyChangedDetails& details) override;
+  void AddedToWidget() override;
 
  private:
   // The model to customize this bubble view.
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc b/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
index 98f7fb5..c8a39836a 100644
--- a/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
+++ b/chrome/browser/ui/views/extensions/extensions_menu_view_browsertest.cc
@@ -29,6 +29,7 @@
 #include "extensions/test/test_extension_dir.h"
 #include "net/dns/mock_host_resolver.h"
 #include "ui/views/layout/animating_layout_manager.h"
+#include "ui/views/layout/animating_layout_manager_test_util.h"
 #include "ui/views/test/widget_test.h"
 #include "ui/views/view_class_properties.h"
 
@@ -83,9 +84,8 @@
           extensions::ExtensionContextMenuModel::UNINSTALL, 0);
 
       // Wait for animations to finish so that the dialog should be showing.
-      base::RunLoop loop;
-      GetAnimatingLayoutManager()->PostOrQueueAction(loop.QuitClosure());
-      loop.Run();
+      views::test::WaitForAnimatingLayoutManager(
+          GetExtensionsToolbarContainer());
     }
   }
 
@@ -150,9 +150,8 @@
     } else {
       // After dismissal the icon should become invisible.
       // Wait for animations to finish.
-      base::RunLoop loop;
-      GetAnimatingLayoutManager()->PostOrQueueAction(loop.QuitClosure());
-      loop.Run();
+      views::test::WaitForAnimatingLayoutManager(
+          GetExtensionsToolbarContainer());
 
       // The extension should still be present in the ExtensionRegistry (not
       // uninstalled) when the uninstall dialog is dismissed.
@@ -181,11 +180,6 @@
         ->extensions_container();
   }
 
-  views::AnimatingLayoutManager* GetAnimatingLayoutManager() {
-    return static_cast<views::AnimatingLayoutManager*>(
-        GetExtensionsToolbarContainer()->GetLayoutManager());
-  }
-
   static std::vector<ExtensionsMenuItemView*> GetExtensionsMenuItemView() {
     return ExtensionsMenuView::GetExtensionsMenuViewForTesting()
         ->extensions_menu_items_for_testing();
@@ -218,9 +212,7 @@
         ->OnMouseReleased(click_event);
 
     // Wait for animations to finish.
-    base::RunLoop loop;
-    GetAnimatingLayoutManager()->PostOrQueueAction(loop.QuitClosure());
-    loop.Run();
+    views::test::WaitForAnimatingLayoutManager(GetExtensionsToolbarContainer());
   }
 
   std::string ui_test_name_;
@@ -289,9 +281,7 @@
   VerifyUi();
 
   ExtensionsToolbarContainer* const extensions_container =
-      BrowserView::GetBrowserViewForBrowser(browser())
-          ->toolbar()
-          ->extensions_container();
+      GetExtensionsToolbarContainer();
 
   EXPECT_EQ(nullptr, extensions_container->GetPoppedOutAction());
   EXPECT_TRUE(GetVisibleToolbarActionViews().empty());
@@ -309,10 +299,7 @@
   extensions_container->HideActivePopup();
 
   // Wait for animations to finish.
-  base::RunLoop loop;
-  extensions_container->animating_layout_manager()->PostOrQueueAction(
-      loop.QuitClosure());
-  loop.Run();
+  views::test::WaitForAnimatingLayoutManager(extensions_container);
 
   // After dismissing the popup there should no longer be a popped-out action
   // and the icon should no longer be visible in the extensions container.
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
index e0eaf0e..63529eed 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.cc
@@ -46,7 +46,10 @@
     g_browser_process->profile_manager()->
         GetProfileAttributesStorage().AddObserver(this);
   }
-  MaybeObserveTabstrip();
+  if (browser_view_->tabstrip()) {
+    DCHECK(!tab_strip_observer_.IsObserving(browser_view_->tabstrip()));
+    tab_strip_observer_.Add(browser_view_->tabstrip());
+  }
 }
 
 BrowserNonClientFrameView::~BrowserNonClientFrameView() {
@@ -58,7 +61,6 @@
 }
 
 void BrowserNonClientFrameView::OnBrowserViewInitViewsComplete() {
-  MaybeObserveTabstrip();
   UpdateMinimumSize();
 }
 
@@ -385,13 +387,6 @@
 }
 #endif
 
-void BrowserNonClientFrameView::MaybeObserveTabstrip() {
-  if (browser_view_->tabstrip()) {
-    DCHECK(!tab_strip_observer_.IsObserving(browser_view_->tabstrip()));
-    tab_strip_observer_.Add(browser_view_->tabstrip());
-  }
-}
-
 const ui::ThemeProvider*
 BrowserNonClientFrameView::GetThemeProviderForProfile() const {
   // Because the frame's accessor reads the ThemeProvider from the profile and
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
index e0a92ab5..62df95c 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view.h
@@ -177,8 +177,6 @@
   int GetSystemMenuY() const override;
 #endif
 
-  void MaybeObserveTabstrip();
-
   // Gets a theme provider that should be non-null even before we're added to a
   // view hierarchy.
   const ui::ThemeProvider* GetThemeProviderForProfile() const;
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index c6c1235..52abc1fc 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -430,6 +430,70 @@
   browser_->tab_strip_model()->AddObserver(this);
   immersive_mode_controller_ = chrome::CreateImmersiveModeController();
   md_observer_.Add(ui::MaterialDesignController::GetInstance());
+
+  // Top container holds tab strip region and toolbar and lives at the front of
+  // the view hierarchy.
+  top_container_ = AddChildView(std::make_unique<TopContainerView>(this));
+  tab_strip_region_view_ =
+      top_container_->AddChildView(std::make_unique<TabStripRegionView>());
+
+  // TabStrip takes ownership of the controller.
+  auto tabstrip_controller = std::make_unique<BrowserTabStripController>(
+      browser_->tab_strip_model(), this);
+  BrowserTabStripController* tabstrip_controller_ptr =
+      tabstrip_controller.get();
+  tabstrip_ = tab_strip_region_view_->AddChildView(std::make_unique<TabStrip>(
+      std::move(tabstrip_controller)));  // Takes ownership.
+  tabstrip_controller_ptr->InitFromModel(tabstrip_);
+
+  // Create WebViews early so |webui_tab_strip_| can observe their size.
+  auto devtools_web_view =
+      std::make_unique<views::WebView>(browser_->profile());
+  devtools_web_view->SetID(VIEW_ID_DEV_TOOLS_DOCKED);
+  devtools_web_view->SetVisible(false);
+
+  auto contents_web_view =
+      std::make_unique<ContentsWebView>(browser_->profile());
+  contents_web_view->SetID(VIEW_ID_TAB_CONTAINER);
+  contents_web_view->SetEmbedFullscreenWidgetMode(true);
+
+  auto contents_container = std::make_unique<views::View>();
+  devtools_web_view_ =
+      contents_container->AddChildView(std::move(devtools_web_view));
+  contents_web_view_ =
+      contents_container->AddChildView(std::move(contents_web_view));
+  contents_container->SetLayoutManager(std::make_unique<ContentsLayoutManager>(
+      devtools_web_view_, contents_web_view_));
+
+  toolbar_ = top_container_->AddChildView(
+      std::make_unique<ToolbarView>(browser_.get(), this));
+
+  contents_separator_ =
+      top_container_->AddChildView(std::make_unique<ContentsSeparator>());
+
+  web_contents_close_handler_ =
+      std::make_unique<WebContentsCloseHandler>(contents_web_view_);
+
+  contents_container_ = AddChildView(std::move(contents_container));
+  set_contents_view(contents_container_);
+
+  // InfoBarContainer needs to be added as a child here for drop-shadow, but
+  // needs to come after toolbar in focus order (see EnsureFocusOrder()).
+  infobar_container_ =
+      AddChildView(std::make_unique<InfoBarContainerView>(this));
+
+  InitStatusBubble();
+
+  // Create do-nothing view for the sake of controlling the z-order of the find
+  // bar widget.
+  find_bar_host_view_ = AddChildView(std::make_unique<View>());
+
+#if defined(OS_WIN)
+  // Create a custom JumpList and add it to an observer of TabRestoreService
+  // so we can update the custom JumpList when a tab is added or removed.
+  if (JumpList::Enabled())
+    load_complete_listener_ = std::make_unique<LoadCompleteListener>(this);
+#endif
 }
 
 BrowserView::~BrowserView() {
@@ -2479,19 +2543,80 @@
 
 void BrowserView::ViewHierarchyChanged(
     const views::ViewHierarchyChangedDetails& details) {
-  if (details.child != this)
-    return;
+  // Override here in order to suppress the call to
+  // views::ClientView::ViewHierarchyChanged();
+}
 
-  // On removal, this class may not have a widget anymore, so go to the parent.
-  auto* widget = details.is_add ? GetWidget() : details.parent->GetWidget();
-  if (!widget)
-    return;
+void BrowserView::AddedToWidget() {
+  views::ClientView::AddedToWidget();
 
-  bool init = !initialized_ && details.is_add;
-  if (init) {
-    InitViews();
-    initialized_ = true;
+#if defined(OS_CHROMEOS)
+  // TopControlsSlideController must be initialized here in AddedToWidget()
+  // rather than Init() as it depends on the browser frame being ready.
+  if (IsBrowserTypeNormal()) {
+    DCHECK(frame_);
+    top_controls_slide_controller_ =
+        std::make_unique<TopControlsSlideControllerChromeOS>(this);
   }
+#endif
+
+  GetWidget()->AddObserver(this);
+
+  // Stow a pointer to this object onto the window handle so that we can get at
+  // it later when all we have is a native view.
+  GetWidget()->SetNativeWindowProperty(kBrowserViewKey, this);
+
+  // Stow a pointer to the browser's profile onto the window handle so that we
+  // can get it later when all we have is a native view.
+  GetWidget()->SetNativeWindowProperty(Profile::kProfileKey,
+                                       browser_->profile());
+
+#if defined(USE_AURA)
+  // Stow a pointer to the browser's profile onto the window handle so
+  // that windows will be styled with the appropriate NativeTheme.
+  SetThemeProfileForWindow(GetNativeWindow(), browser_->profile());
+#endif
+
+  toolbar_->Init();
+
+  LoadAccelerators();
+
+  // |immersive_mode_controller_| may depend on the presence of a Widget, so it
+  // is initialized here.
+  immersive_mode_controller_->Init(this);
+  immersive_mode_controller_->AddObserver(this);
+
+  // See https://crbug.com/993502.
+  views::View* web_footer_experiment = nullptr;
+  if (base::FeatureList::IsEnabled(features::kWebFooterExperiment)) {
+    web_footer_experiment = AddChildView(
+        std::make_unique<WebFooterExperimentView>(browser_->profile()));
+  }
+
+  // TODO(https://crbug.com/1036519): Remove BrowserViewLayout dependence on
+  // Widget and move to the constructor.
+  auto browser_view_layout = std::make_unique<BrowserViewLayout>(
+      std::make_unique<BrowserViewLayoutDelegateImpl>(this),
+      GetWidget()->GetNativeView(), this, top_container_,
+      tab_strip_region_view_, tabstrip_, toolbar_, infobar_container_,
+      contents_container_, immersive_mode_controller_.get(),
+      web_footer_experiment, contents_separator_);
+  SetLayoutManager(std::move(browser_view_layout));
+
+  EnsureFocusOrder();
+
+  // This browser view may already have a custom button provider set (e.g the
+  // hosted app frame).
+  if (!toolbar_button_provider_)
+    SetToolbarButtonProvider(toolbar_);
+
+  frame_->OnBrowserViewInitViewsComplete();
+  frame_->GetFrameView()->UpdateMinimumSize();
+  using_native_frame_ = frame_->ShouldUseNativeFrame();
+
+  MaybeInitializeWebUITabStrip();
+
+  initialized_ = true;
 }
 
 void BrowserView::PaintChildren(const views::PaintInfo& paint_info) {
@@ -2570,131 +2695,6 @@
   ToolbarSizeChanged(is_animating);
 }
 
-void BrowserView::InitViews() {
-  // TopControlsSlideController must be initialized here in InitViews() rather
-  // than Init() as it depends on the browser frame being ready.
-#if defined(OS_CHROMEOS)
-  if (IsBrowserTypeNormal()) {
-    DCHECK(frame_);
-    top_controls_slide_controller_ =
-        std::make_unique<TopControlsSlideControllerChromeOS>(this);
-  }
-#endif
-
-  GetWidget()->AddObserver(this);
-
-  // Stow a pointer to this object onto the window handle so that we can get at
-  // it later when all we have is a native view.
-  GetWidget()->SetNativeWindowProperty(kBrowserViewKey, this);
-
-  // Stow a pointer to the browser's profile onto the window handle so that we
-  // can get it later when all we have is a native view.
-  GetWidget()->SetNativeWindowProperty(Profile::kProfileKey,
-                                       browser_->profile());
-
-#if defined(USE_AURA)
-  // Stow a pointer to the browser's profile onto the window handle so
-  // that windows will be styled with the appropriate NativeTheme.
-  SetThemeProfileForWindow(GetNativeWindow(), browser_->profile());
-#endif
-
-  LoadAccelerators();
-
-  // Top container holds tab strip region and toolbar and lives at the front of
-  // the view hierarchy.
-  top_container_ = new TopContainerView(this);
-  AddChildView(top_container_);
-  tab_strip_region_view_ = new TabStripRegionView();
-  top_container_->AddChildView(tab_strip_region_view_);
-
-  // TabStrip takes ownership of the controller.
-  BrowserTabStripController* tabstrip_controller =
-      new BrowserTabStripController(browser_->tab_strip_model(), this);
-  tabstrip_ =
-      new TabStrip(std::unique_ptr<TabStripController>(tabstrip_controller));
-  tab_strip_region_view_->AddChildView(tabstrip_);  // Takes ownership.
-  tabstrip_controller->InitFromModel(tabstrip_);
-
-  // Create WebViews early so |webui_tab_strip_| can observe their size.
-  devtools_web_view_ = new views::WebView(browser_->profile());
-  devtools_web_view_->SetID(VIEW_ID_DEV_TOOLS_DOCKED);
-  devtools_web_view_->SetVisible(false);
-
-  contents_web_view_ = new ContentsWebView(browser_->profile());
-  contents_web_view_->SetID(VIEW_ID_TAB_CONTAINER);
-  contents_web_view_->SetEmbedFullscreenWidgetMode(true);
-
-  contents_container_ = new views::View();
-  contents_container_->AddChildView(devtools_web_view_);
-  contents_container_->AddChildView(contents_web_view_);
-  contents_container_->SetLayoutManager(std::make_unique<ContentsLayoutManager>(
-      devtools_web_view_, contents_web_view_));
-
-  toolbar_ = top_container_->AddChildView(
-      std::make_unique<ToolbarView>(browser_.get(), this));
-  toolbar_->Init();
-
-  contents_separator_ =
-      top_container_->AddChildView(std::make_unique<ContentsSeparator>());
-
-  // This browser view may already have a custom button provider set (e.g the
-  // hosted app frame).
-  if (!toolbar_button_provider_)
-    SetToolbarButtonProvider(toolbar_);
-
-  web_contents_close_handler_.reset(
-      new WebContentsCloseHandler(contents_web_view_));
-
-  AddChildView(contents_container_);
-  set_contents_view(contents_container_);
-
-  // InfoBarContainer needs to be added as a child here for drop-shadow, but
-  // needs to come after toolbar in focus order (see EnsureFocusOrder()).
-  infobar_container_ = new InfoBarContainerView(this);
-  AddChildView(infobar_container_);
-
-  InitStatusBubble();
-
-  // Create do-nothing view for the sake of controlling the z-order of the find
-  // bar widget.
-  find_bar_host_view_ = new View();
-  AddChildView(find_bar_host_view_);
-
-  immersive_mode_controller_->Init(this);
-  immersive_mode_controller_->AddObserver(this);
-
-  // See https://crbug.com/993502.
-  views::View* web_footer_experiment = nullptr;
-  if (base::FeatureList::IsEnabled(features::kWebFooterExperiment)) {
-    web_footer_experiment = AddChildView(
-        std::make_unique<WebFooterExperimentView>(browser_->profile()));
-  }
-
-  auto browser_view_layout = std::make_unique<BrowserViewLayout>(
-      std::make_unique<BrowserViewLayoutDelegateImpl>(this),
-      GetWidget()->GetNativeView(), this, top_container_,
-      tab_strip_region_view_, tabstrip_, toolbar_, infobar_container_,
-      contents_container_, immersive_mode_controller_.get(),
-      web_footer_experiment, contents_separator_);
-  SetLayoutManager(std::move(browser_view_layout));
-
-  EnsureFocusOrder();
-
-#if defined(OS_WIN)
-  // Create a custom JumpList and add it to an observer of TabRestoreService
-  // so we can update the custom JumpList when a tab is added or removed.
-  if (JumpList::Enabled()) {
-    load_complete_listener_ = std::make_unique<LoadCompleteListener>(this);
-  }
-#endif
-
-  frame_->OnBrowserViewInitViewsComplete();
-  frame_->GetFrameView()->UpdateMinimumSize();
-  using_native_frame_ = frame_->ShouldUseNativeFrame();
-
-  MaybeInitializeWebUITabStrip();
-}
-
 void BrowserView::MaybeInitializeWebUITabStrip() {
 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
   if (browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP) &&
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 4bf8b2c..dde5b3b 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -498,6 +498,7 @@
   void OnGestureEvent(ui::GestureEvent* event) override;
   void ViewHierarchyChanged(
       const views::ViewHierarchyChangedDetails& details) override;
+  void AddedToWidget() override;
   void PaintChildren(const views::PaintInfo& paint_info) override;
   void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
   void ChildPreferredSizeChanged(View* child) override;
@@ -579,9 +580,6 @@
   // affected.
   void RevealTabStripIfNeeded();
 
-  // Constructs and initializes the child views.
-  void InitViews();
-
   // Make sure the WebUI tab strip exists if it should.
   void MaybeInitializeWebUITabStrip();
 
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
index 768b0fa..740fa3f 100644
--- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.cc
@@ -54,12 +54,8 @@
 namespace {
 
 std::unique_ptr<views::ImageButton> CreateCloseButton(
-    views::ButtonListener* listener,
-    SkColor color) {
+    views::ButtonListener* listener) {
   auto close_button = CreateVectorImageButton(listener);
-  SetImageFromVectorIconWithColor(
-      close_button.get(), vector_icons::kCloseRoundedIcon,
-      GetLayoutConstant(LOCATION_BAR_ICON_SIZE), color);
   close_button->SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_CLOSE));
   close_button->SetBorder(views::CreateEmptyBorder(
       gfx::Insets(GetLayoutConstant(LOCATION_BAR_CHILD_INTERIOR_PADDING))));
@@ -85,7 +81,7 @@
 // page.
 class CustomTabBarTitleOriginView : public views::View {
  public:
-  explicit CustomTabBarTitleOriginView(SkColor background_color) {
+  CustomTabBarTitleOriginView() {
     auto title_label = std::make_unique<views::Label>(
         base::string16(), CONTEXT_BODY_TEXT_LARGE,
         views::style::TextStyle::STYLE_PRIMARY);
@@ -94,7 +90,6 @@
         views::style::STYLE_SECONDARY,
         gfx::DirectionalityMode::DIRECTIONALITY_AS_URL);
 
-    title_label->SetBackgroundColor(background_color);
     title_label->SetElideBehavior(gfx::ElideBehavior::ELIDE_TAIL);
     title_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
     title_label->SetProperty(views::kFlexBehaviorKey,
@@ -102,7 +97,6 @@
                                  views::MinimumFlexSizeRule::kScaleToMinimum,
                                  views::MaximumFlexSizeRule::kPreferred));
 
-    location_label->SetBackgroundColor(background_color);
     location_label->SetElideBehavior(gfx::ElideBehavior::ELIDE_HEAD);
     location_label->SetHorizontalAlignment(
         gfx::HorizontalAlignment::ALIGN_LEFT);
@@ -126,6 +120,11 @@
     location_label_->SetVisible(!location.empty());
   }
 
+  void SetColors(SkColor background_color) {
+    title_label_->SetBackgroundColor(background_color);
+    location_label_->SetBackgroundColor(background_color);
+  }
+
   int GetMinimumWidth() const {
     // As labels are not multi-line, the layout will calculate a minimum size
     // that would fit the entire text (potentially a long url). Instead, set a
@@ -174,38 +173,16 @@
       delegate_(delegate),
       browser_(browser_view->browser()) {
   set_context_menu_controller(this);
-  base::Optional<SkColor> optional_theme_color =
-      browser_->app_controller()->GetThemeColor();
-
-  const bool dark_mode = GetNativeTheme()->ShouldUseDarkColors();
-  const SkColor default_frame_color =
-#if defined(OS_CHROMEOS)
-      // Ash system frames differ from ChromeOS browser frames.
-      ash::kDefaultFrameColor;
-#else
-      ThemeProperties::GetDefaultColor(ThemeProperties::COLOR_FRAME, false,
-                                       dark_mode);
-#endif
-
-  title_bar_color_ = optional_theme_color.value_or(default_frame_color);
-
-  background_color_ = dark_mode ? default_frame_color : SK_ColorWHITE;
-
-  SetBackground(views::CreateSolidBackground(background_color_));
-
-  const SkColor foreground_color =
-      color_utils::GetColorWithMaxContrast(background_color_);
 
   const gfx::FontList& font_list = views::style::GetFont(
       CONTEXT_OMNIBOX_PRIMARY, views::style::STYLE_PRIMARY);
 
-  close_button_ = AddChildView(CreateCloseButton(this, foreground_color));
+  close_button_ = AddChildView(CreateCloseButton(this));
 
   location_icon_view_ =
       AddChildView(std::make_unique<LocationIconView>(font_list, this, this));
 
-  auto title_origin_view =
-      std::make_unique<CustomTabBarTitleOriginView>(background_color_);
+  auto title_origin_view = std::make_unique<CustomTabBarTitleOriginView>();
   title_origin_view->SetProperty(
       views::kFlexBehaviorKey, views::FlexSpecification::ForSizeRule(
                                    views::MinimumFlexSizeRule::kScaleToMinimum,
@@ -232,55 +209,6 @@
   return kViewClassName;
 }
 
-void CustomTabBarView::TabChangedAt(content::WebContents* contents,
-                                    int index,
-                                    TabChangeType change_type) {
-  if (!contents)
-    return;
-
-  // If the toolbar should not be shown don't update the UI, as the toolbar may
-  // be animating out and it looks messy.
-  Browser* browser = chrome::FindBrowserWithWebContents(contents);
-  if (!browser->app_controller()->ShouldShowCustomTabBar())
-    return;
-
-  content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
-  base::string16 title, location;
-  if (entry) {
-    title = Browser::FormatTitleForDisplay(entry->GetTitleForDisplay());
-    if (ShouldDisplayUrl(contents))
-      location = url_formatter::FormatUrl(entry->GetVirtualURL().GetOrigin(),
-                                          url_formatter::kFormatUrlOmitDefaults,
-                                          net::UnescapeRule::NORMAL, nullptr,
-                                          nullptr, nullptr);
-  }
-
-  title_origin_view_->Update(title, location);
-  location_icon_view_->Update(/*suppress animations = */ false);
-
-  // Hide location icon if we're already hiding the origin.
-  location_icon_view_->SetVisible(!location.empty());
-
-  last_title_ = title;
-  last_location_ = location;
-
-  web_app::AppBrowserController* app_controller =
-      chrome::FindBrowserWithWebContents(contents)->app_controller();
-  const bool started_in_scope =
-      app_controller->IsUrlInAppScope(app_controller->initial_url());
-
-  // Only show the 'X' button if:
-  // a) The current url is not in scope (no point showing a back to app button
-  // while in scope).
-  // And b), if the window started in scope (this is
-  // important for popup windows, which may be opened outside the app).
-  close_button_->SetVisible(
-      started_in_scope &&
-      !app_controller->IsUrlInAppScope(contents->GetLastCommittedURL()));
-
-  Layout();
-}
-
 gfx::Size CustomTabBarView::CalculatePreferredSize() const {
   // ToolbarView::GetMinimumSize() uses the preferred size of its children, so
   // tell it the minimum size this control will fit into (its layout will
@@ -330,27 +258,83 @@
   SchedulePaint();
 }
 
-void CustomTabBarView::ShowContextMenuForViewImpl(
-    views::View* source,
-    const gfx::Point& point,
-    ui::MenuSourceType source_type) {
-  if (!context_menu_model_) {
-    context_menu_model_ = std::make_unique<ui::SimpleMenuModel>(this);
-    context_menu_model_->AddItemWithStringId(IDC_COPY_URL, IDS_COPY_URL);
-  }
-  context_menu_runner_ = std::make_unique<views::MenuRunner>(
-      context_menu_model_.get(),
-      views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU);
-  context_menu_runner_->RunMenuAt(
-      views::View::GetWidget(), nullptr, gfx::Rect(point, gfx::Size()),
-      views::MenuAnchorPosition::kTopLeft, source_type);
+void CustomTabBarView::OnThemeChanged() {
+  base::Optional<SkColor> optional_theme_color =
+      browser_->app_controller()->GetThemeColor();
+
+  const bool dark_mode = GetNativeTheme()->ShouldUseDarkColors();
+  const SkColor default_frame_color =
+#if defined(OS_CHROMEOS)
+      // Ash system frames differ from ChromeOS browser frames.
+      ash::kDefaultFrameColor;
+#else
+      ThemeProperties::GetDefaultColor(ThemeProperties::COLOR_FRAME, false,
+                                       dark_mode);
+#endif
+
+  title_bar_color_ = optional_theme_color.value_or(default_frame_color);
+
+  background_color_ = dark_mode ? default_frame_color : SK_ColorWHITE;
+
+  SetBackground(views::CreateSolidBackground(background_color_));
+
+  const SkColor foreground_color =
+      color_utils::GetColorWithMaxContrast(background_color_);
+
+  SetImageFromVectorIconWithColor(
+      close_button_, vector_icons::kCloseRoundedIcon,
+      GetLayoutConstant(LOCATION_BAR_ICON_SIZE), foreground_color);
+
+  title_origin_view_->SetColors(background_color_);
 }
 
-void CustomTabBarView::ExecuteCommand(int command_id, int event_flags) {
-  if (command_id == IDC_COPY_URL) {
-    base::RecordAction(base::UserMetricsAction("CopyCustomTabBarUrl"));
-    chrome::ExecuteCommand(browser_, command_id);
+void CustomTabBarView::TabChangedAt(content::WebContents* contents,
+                                    int index,
+                                    TabChangeType change_type) {
+  if (!contents)
+    return;
+
+  // If the toolbar should not be shown don't update the UI, as the toolbar may
+  // be animating out and it looks messy.
+  Browser* browser = chrome::FindBrowserWithWebContents(contents);
+  if (!browser->app_controller()->ShouldShowCustomTabBar())
+    return;
+
+  content::NavigationEntry* entry = contents->GetController().GetVisibleEntry();
+  base::string16 title, location;
+  if (entry) {
+    title = Browser::FormatTitleForDisplay(entry->GetTitleForDisplay());
+    if (ShouldDisplayUrl(contents))
+      location = url_formatter::FormatUrl(entry->GetVirtualURL().GetOrigin(),
+                                          url_formatter::kFormatUrlOmitDefaults,
+                                          net::UnescapeRule::NORMAL, nullptr,
+                                          nullptr, nullptr);
   }
+
+  title_origin_view_->Update(title, location);
+  location_icon_view_->Update(/*suppress animations = */ false);
+
+  // Hide location icon if we're already hiding the origin.
+  location_icon_view_->SetVisible(!location.empty());
+
+  last_title_ = title;
+  last_location_ = location;
+
+  web_app::AppBrowserController* app_controller =
+      chrome::FindBrowserWithWebContents(contents)->app_controller();
+  const bool started_in_scope =
+      app_controller->IsUrlInAppScope(app_controller->initial_url());
+
+  // Only show the 'X' button if:
+  // a) The current url is not in scope (no point showing a back to app button
+  // while in scope).
+  // And b), if the window started in scope (this is
+  // important for popup windows, which may be opened outside the app).
+  close_button_->SetVisible(
+      started_in_scope &&
+      !app_controller->IsUrlInAppScope(contents->GetLastCommittedURL()));
+
+  Layout();
 }
 
 SkColor CustomTabBarView::GetIconLabelBubbleSurroundingForegroundColor() const {
@@ -373,6 +357,11 @@
 
 void CustomTabBarView::OnLocationIconDragged(const ui::MouseEvent& event) {}
 
+SkColor CustomTabBarView::GetSecurityChipColor(
+    security_state::SecurityLevel security_level) const {
+  return GetOmniboxSecurityChipColor(GetThemeProvider(), security_level);
+}
+
 bool CustomTabBarView::ShowPageInfoDialog() {
   return ::ShowPageInfoDialog(
       GetWebContents(),
@@ -381,11 +370,8 @@
       bubble_anchor_util::Anchor::kCustomTabBar);
 }
 
-SkColor CustomTabBarView::GetSecurityChipColor(
-    security_state::SecurityLevel security_level) const {
-  return GetOmniboxSecurityChipColor(
-      &ThemeService::GetThemeProviderForProfile(browser_->profile()),
-      security_level);
+const LocationBarModel* CustomTabBarView::GetLocationBarModel() const {
+  return delegate_->GetLocationBarModel();
 }
 
 gfx::ImageSkia CustomTabBarView::GetLocationIcon(
@@ -396,10 +382,6 @@
       GetSecurityChipColor(GetLocationBarModel()->GetSecurityLevel()));
 }
 
-const LocationBarModel* CustomTabBarView::GetLocationBarModel() const {
-  return delegate_->GetLocationBarModel();
-}
-
 void CustomTabBarView::ButtonPressed(views::Button* sender,
                                      const ui::Event& event) {
   GoBackToApp();
@@ -409,6 +391,11 @@
   GoBackToApp();
 }
 
+bool CustomTabBarView::IsShowingOriginForTesting() const {
+  return title_origin_view_ != nullptr &&
+         title_origin_view_->IsShowingOriginForTesting();
+}
+
 void CustomTabBarView::GoBackToApp() {
   content::WebContents* web_contents = GetWebContents();
   web_app::AppBrowserController* app_controller =
@@ -456,7 +443,25 @@
   GetFocusManager()->SetFocusedView(location_icon_view_);
 }
 
-bool CustomTabBarView::IsShowingOriginForTesting() const {
-  return title_origin_view_ != nullptr &&
-         title_origin_view_->IsShowingOriginForTesting();
+void CustomTabBarView::ExecuteCommand(int command_id, int event_flags) {
+  if (command_id == IDC_COPY_URL) {
+    base::RecordAction(base::UserMetricsAction("CopyCustomTabBarUrl"));
+    chrome::ExecuteCommand(browser_, command_id);
+  }
+}
+
+void CustomTabBarView::ShowContextMenuForViewImpl(
+    views::View* source,
+    const gfx::Point& point,
+    ui::MenuSourceType source_type) {
+  if (!context_menu_model_) {
+    context_menu_model_ = std::make_unique<ui::SimpleMenuModel>(this);
+    context_menu_model_->AddItemWithStringId(IDC_COPY_URL, IDS_COPY_URL);
+  }
+  context_menu_runner_ = std::make_unique<views::MenuRunner>(
+      context_menu_model_.get(),
+      views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU);
+  context_menu_runner_->RunMenuAt(
+      views::View::GetWidget(), nullptr, gfx::Rect(point, gfx::Size()),
+      views::MenuAnchorPosition::kTopLeft, source_type);
 }
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
index bd41350..19250c5 100644
--- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
+++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view.h
@@ -13,7 +13,6 @@
 #include "ui/base/models/simple_menu_model.h"
 #include "ui/views/accessible_pane_view.h"
 #include "ui/views/context_menu_controller.h"
-#include "ui/views/controls/button/button.h"
 
 namespace gfx {
 class Rect;
@@ -22,6 +21,7 @@
 namespace views {
 class FlexLayout;
 class MenuRunner;
+class ImageButton;
 }
 
 class CustomTabBarTitleOriginView;
@@ -47,20 +47,19 @@
 
   LocationIconView* location_icon_view() { return location_icon_view_; }
 
-  // views::View:
+  // views::AccessiblePaneView:
   gfx::Rect GetAnchorBoundsInScreen() const override;
   const char* GetClassName() const override;
+  gfx::Size CalculatePreferredSize() const override;
+  void OnPaintBackground(gfx::Canvas* canvas) override;
+  void ChildPreferredSizeChanged(views::View* child) override;
+  void OnThemeChanged() override;
 
   // TabstripModelObserver:
   void TabChangedAt(content::WebContents* contents,
                     int index,
                     TabChangeType change_type) override;
 
-  // views::View:
-  gfx::Size CalculatePreferredSize() const override;
-  void OnPaintBackground(gfx::Canvas* canvas) override;
-  void ChildPreferredSizeChanged(views::View* child) override;
-
   // IconLabelBubbleView::Delegate:
   SkColor GetIconLabelBubbleSurroundingForegroundColor() const override;
   SkColor GetIconLabelBubbleBackgroundColor() const override;
@@ -83,7 +82,7 @@
   // Methods for testing.
   base::string16 title_for_testing() const { return last_title_; }
   base::string16 location_for_testing() const { return last_location_; }
-  views::Button* close_button_for_testing() const { return close_button_; }
+  views::ImageButton* close_button_for_testing() const { return close_button_; }
   ui::SimpleMenuModel* context_menu_for_testing() const {
     return context_menu_model_.get();
   }
@@ -113,7 +112,7 @@
   base::string16 last_title_;
   base::string16 last_location_;
 
-  views::Button* close_button_ = nullptr;
+  views::ImageButton* close_button_ = nullptr;
   LocationBarView::Delegate* delegate_ = nullptr;
   LocationIconView* location_icon_view_ = nullptr;
   CustomTabBarTitleOriginView* title_origin_view_ = nullptr;
diff --git a/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc b/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc
index 4e8a161..1c0d0aa 100644
--- a/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc
+++ b/chrome/browser/ui/views/location_bar/custom_tab_bar_view_browsertest.cc
@@ -26,6 +26,7 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "net/dns/mock_host_resolver.h"
 #include "ui/base/clipboard/clipboard.h"
+#include "ui/views/controls/button/image_button.h"
 
 namespace {
 
diff --git a/chrome/browser/ui/views/location_bar/location_bar_view.cc b/chrome/browser/ui/views/location_bar/location_bar_view.cc
index 6cde51f..fb306e9 100644
--- a/chrome/browser/ui/views/location_bar/location_bar_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_bar_view.cc
@@ -305,8 +305,8 @@
 }
 
 SkColor LocationBarView::GetColor(OmniboxPart part) const {
-  return GetOmniboxColor(&ThemeService::GetThemeProviderForProfile(profile_),
-                         part);
+  DCHECK(GetWidget());
+  return GetOmniboxColor(GetThemeProvider(), part);
 }
 
 SkColor LocationBarView::GetOpaqueBorderColor() const {
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view.cc b/chrome/browser/ui/views/location_bar/location_icon_view.cc
index dc1a34d5..2ec53c2 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_icon_view.cc
@@ -36,7 +36,6 @@
   DCHECK(delegate_);
 
   SetID(VIEW_ID_LOCATION_ICON);
-  Update(true);
   SetUpForAnimation();
 
   // Readability is guaranteed by the omnibox theme.
@@ -101,6 +100,10 @@
   node_data->role = ax::mojom::Role::kPopUpButton;
 }
 
+void LocationIconView::AddedToWidget() {
+  Update(true);
+}
+
 int LocationIconView::GetMinimumLabelTextWidth() const {
   int width = 0;
 
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view.h b/chrome/browser/ui/views/location_bar/location_icon_view.h
index 8c5c4d3..6975f6a 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view.h
+++ b/chrome/browser/ui/views/location_bar/location_icon_view.h
@@ -72,6 +72,7 @@
   bool IsBubbleShowing() const override;
   bool OnMousePressed(const ui::MouseEvent& event) override;
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
+  void AddedToWidget() override;
 
   // Returns what the minimum width for the label text.
   int GetMinimumLabelTextWidth() const;
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
index 9d0f7c9..b25b94d 100644
--- a/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc
@@ -18,6 +18,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/events/event_utils.h"
+#include "ui/views/layout/animating_layout_manager_test_util.h"
 
 // The param indicates if the feature showing password icon in the new toolbar
 // status chip is enabled.
@@ -64,22 +65,18 @@
 
   void WaitForAnimationToEnd() {
     auto* const animating_layout = GetAnimatingLayoutManager();
-    if (animating_layout) {
-      // Wait for animations to finish.
-      base::RunLoop loop;
-      animating_layout->PostOrQueueAction(loop.QuitClosure());
-      loop.Run();
-    }
+    if (animating_layout)
+      views::test::WaitForAnimatingLayoutManager(animating_layout);
   }
 
  private:
   views::AnimatingLayoutManager* GetAnimatingLayoutManager() {
-    return GetParam() ? static_cast<views::AnimatingLayoutManager*>(
-                            BrowserView::GetBrowserViewForBrowser(browser())
-                                ->toolbar()
-                                ->toolbar_account_icon_container()
-                                ->GetLayoutManager())
-                      : nullptr;
+    if (!GetParam())
+      return nullptr;
+    return views::test::GetAnimatingLayoutManager(
+        BrowserView::GetBrowserViewForBrowser(browser())
+            ->toolbar()
+            ->toolbar_account_icon_container());
   }
 
   void ReduceAnimationTime() {
diff --git a/chrome/browser/ui/views/update_recommended_message_box.cc b/chrome/browser/ui/views/update_recommended_message_box.cc
index b49ca24e..79fe5720 100644
--- a/chrome/browser/ui/views/update_recommended_message_box.cc
+++ b/chrome/browser/ui/views/update_recommended_message_box.cc
@@ -21,10 +21,6 @@
 #include "chromeos/dbus/power/power_manager_client.h"
 #endif
 
-#if defined(OS_MACOSX)
-#include "chrome/browser/first_run/upgrade_util_mac.h"
-#endif
-
 ////////////////////////////////////////////////////////////////////////////////
 // UpdateRecommendedMessageBox, public:
 
@@ -63,11 +59,6 @@
 }
 
 bool UpdateRecommendedMessageBox::Accept() {
-#if defined(OS_MACOSX)
-  if (!upgrade_util::ShouldContinueToRelaunchForUpgrade())
-    return false;  // Leave the dialog up for the user to return to.
-#endif             // OS_MACOSX
-
   chrome::AttemptRelaunch();
   return true;
 }
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
index 6edb9a3..9dbe446 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom
@@ -68,9 +68,9 @@
   skia.mojom.SkColor background_color;
   skia.mojom.SkColor shortcut_background_color;
   skia.mojom.SkColor shortcut_text_color;
-  // Additional info about the theme depending on the type.
-  // That should be optional since only some themes require it. However, making
-  // this field optional crashes JS.
+  // TODO(crbug.com/1040682): Additional info about the theme depending on the
+  // type. That should be optional since only some themes require it. However,
+  // making this field optional crashes JS.
   ThemeInfo info;
 };
 
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
index bb4e10a8..02997606 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.cc
@@ -23,6 +23,11 @@
   auto theme = new_tab_page::mojom::Theme::New();
   if (ntp_theme.using_default_theme) {
     theme->type = new_tab_page::mojom::ThemeType::DEFAULT;
+    // TODO(crbug.com/1040682): This info has no meaning for the default theme
+    // and shouldn't be used. We set it here to prevent a crash where mojo is
+    // complaing about an unset info. However, we cannot make the field optional
+    // as that is crashing JS. Once the JS crash is solved remove this line.
+    theme->info = new_tab_page::mojom::ThemeInfo::NewChromeThemeId(-1);
   } else if (ntp_theme.color_id == -1) {
     theme->type = new_tab_page::mojom::ThemeType::THIRD_PARTY;
     auto info = new_tab_page::mojom::ThirdPartyThemeInfo::New();
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
index 0daa96d..1cd72ab 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -58,11 +58,14 @@
       {"urlField", IDS_NTP_CUSTOM_LINKS_URL},
 
       // Customize button and dialog.
+      {"backgroundsMenuItem", IDS_NTP_CUSTOMIZE_MENU_BACKGROUND_LABEL},
       {"cancelButton", IDS_CANCEL},
       {"colorPickerLabel", IDS_NTP_CUSTOMIZE_COLOR_PICKER_LABEL},
       {"customizeButton", IDS_NTP_CUSTOMIZE_BUTTON_LABEL},
       {"defaultThemeLabel", IDS_NTP_CUSTOMIZE_DEFAULT_LABEL},
       {"doneButton", IDS_DONE},
+      {"shortcutsMenuItem", IDS_NTP_CUSTOMIZE_MENU_SHORTCUTS_LABEL},
+      {"themesMenuItem", IDS_NTP_CUSTOMIZE_MENU_COLOR_LABEL},
       {"thirdPartyThemeDescription", IDS_NTP_CUSTOMIZE_3PT_THEME_DESC},
       {"uninstallThirdPartyThemeButton", IDS_NTP_CUSTOMIZE_3PT_THEME_UNINSTALL},
   };
diff --git a/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc b/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
index 6c11c87..8a02491a 100644
--- a/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
+++ b/chrome/browser/ui/webui/settings/browser_lifetime_handler.cc
@@ -19,10 +19,6 @@
 #include "components/user_manager/user_manager.h"
 #endif  // defined(OS_CHROMEOS)
 
-#if defined(OS_MACOSX)
-#include "chrome/browser/first_run/upgrade_util_mac.h"
-#endif
-
 namespace settings {
 
 namespace {
@@ -86,11 +82,6 @@
 
 void BrowserLifetimeHandler::HandleRelaunch(
     const base::ListValue* args) {
-#if defined(OS_MACOSX)
-  if (!upgrade_util::ShouldContinueToRelaunchForUpgrade())
-    return;
-#endif  // OS_MACOSX
-
   chrome::AttemptRelaunch();
 }
 
diff --git a/chrome/browser/updates/update_notification_service_impl.cc b/chrome/browser/updates/update_notification_service_impl.cc
index fb8dcd6f..0d6ff4a 100644
--- a/chrome/browser/updates/update_notification_service_impl.cc
+++ b/chrome/browser/updates/update_notification_service_impl.cc
@@ -52,7 +52,7 @@
 }
 
 bool UpdateNotificationServiceImpl::IsReadyToDisplay() const {
-  if (config_->is_enabled)
+  if (!config_->is_enabled)
     return false;
 
   auto last_shown_timestamp = updates::GetLastShownTimeStamp();
diff --git a/chrome/browser/vr/BUILD.gn b/chrome/browser/vr/BUILD.gn
index 2c0974b..6189d79 100644
--- a/chrome/browser/vr/BUILD.gn
+++ b/chrome/browser/vr/BUILD.gn
@@ -599,7 +599,6 @@
 
   public_deps = [
     ":vr_common",
-    ":vr_test_pak",
     ":vr_ui",
     "//base/test:test_support",
     "//cc:test_support",
@@ -620,8 +619,8 @@
     # doesn't propagate to individual test executable targets.
   ]
 
-  data = [
-    "$root_out_dir/vr_test.pak",
+  data_deps = [
+    ":vr_test_pak",
   ]
 }
 
diff --git a/chrome/browser/vr/OWNERS b/chrome/browser/vr/OWNERS
index 0eae439..80f90b5 100644
--- a/chrome/browser/vr/OWNERS
+++ b/chrome/browser/vr/OWNERS
@@ -1,4 +1,3 @@
-cjgrant@chromium.org
 mthiesse@chromium.org
 tiborg@chromium.org
 vollick@chromium.org
diff --git a/chrome/browser/vr/testapp/BUILD.gn b/chrome/browser/vr/testapp/BUILD.gn
index 83b7b21..9e4bfdb 100644
--- a/chrome/browser/vr/testapp/BUILD.gn
+++ b/chrome/browser/vr/testapp/BUILD.gn
@@ -23,7 +23,6 @@
 
   deps = [
     ":assets_component_version_header",
-    ":vr_testapp_pak",
     ":vr_testapp_resources",
     "//chrome/browser/vr:vr_common",
     "//chrome/browser/vr:vr_test_support",
@@ -41,6 +40,9 @@
     "//ui/ozone",
     "//ui/platform_window",
   ]
+  data_deps = [
+    ":vr_testapp_pak",
+  ]
 }
 
 process_version("assets_component_version_header") {
diff --git a/chrome/browser/win/conflicts/module_database.cc b/chrome/browser/win/conflicts/module_database.cc
index d968e866..0456b78 100644
--- a/chrome/browser/win/conflicts/module_database.cc
+++ b/chrome/browser/win/conflicts/module_database.cc
@@ -15,6 +15,7 @@
 #include "base/task/post_task.h"
 #include "chrome/browser/win/conflicts/module_database_observer.h"
 #include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 
 #if defined(GOOGLE_CHROME_BUILD)
 #include "base/feature_list.h"
@@ -111,17 +112,17 @@
   static constexpr base::Feature kDistinctModuleDatabaseSequence{
       "DistinctModuleDatabaseSequence", base::FEATURE_ENABLED_BY_DEFAULT};
 
-  static base::LazySequencedTaskRunner g_ui_task_runner =
-      LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(
-          base::TaskTraits(content::BrowserThread::UI));
   static base::LazySequencedTaskRunner g_distinct_task_runner =
       LAZY_SEQUENCED_TASK_RUNNER_INITIALIZER(
           base::TaskTraits(base::ThreadPool(), base::TaskPriority::BEST_EFFORT,
                            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN));
 
+  // A new task runner to the UI thread can be "created" every time in the
+  // disabled arm, in practice it's always the same task runner (it doesn't need
+  // a lazy instance to a privately owned sequence).
   return base::FeatureList::IsEnabled(kDistinctModuleDatabaseSequence)
              ? g_distinct_task_runner.Get()
-             : g_ui_task_runner.Get();
+             : base::CreateSequencedTaskRunner({content::BrowserThread::UI});
 }
 
 // static
diff --git a/chrome/chrome_cleaner/engines/broker/engine_requests_no_blocking_unittest.cc b/chrome/chrome_cleaner/engines/broker/engine_requests_no_blocking_unittest.cc
index b403bd73..6a3c45e 100644
--- a/chrome/chrome_cleaner/engines/broker/engine_requests_no_blocking_unittest.cc
+++ b/chrome/chrome_cleaner/engines/broker/engine_requests_no_blocking_unittest.cc
@@ -437,9 +437,7 @@
 };
 
 MULTIPROCESS_TEST_MAIN(EngineRequestsNoBlocking) {
-  // COM can't be initialized inside the sandbox.
-  base::test::TaskEnvironment task_environment(
-      base::test::TaskEnvironment::ThreadPoolCOMEnvironment::NONE);
+  base::test::TaskEnvironment task_environment;
 
   auto child_process = SetupSandboxedChildProcess();
   if (!child_process)
@@ -522,6 +520,11 @@
     : public ::testing::TestWithParam<const char*> {};
 
 TEST_P(EngineRequestsNoBlockingTest, TestRequest) {
+  // All of these tests fail when run on win8 bots so return right away.
+  // TODO(crbug.com/947576): Find out why and re-enable them.
+  if (base::win::GetVersion() == base::win::Version::WIN8)
+    return;
+
   base::test::TaskEnvironment task_environment;
 
   // This event will be shared between the parent and child processes. The
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index 0e0d5831..0125d75 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -310,6 +310,9 @@
     if (enable_hidpi) {
       public_deps += [ ":${target_name}_200_percent" ]
     }
+    if (!defined(invoker.copy_data_to_bundle) || !invoker.copy_data_to_bundle) {
+      data_deps = public_deps
+    }
     if (defined(invoker.public_deps)) {
       public_deps += invoker.public_deps
     }
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn
index c953f239..d2d87639 100644
--- a/chrome/common/BUILD.gn
+++ b/chrome/common/BUILD.gn
@@ -136,8 +136,6 @@
     "mac/launchd.mm",
     "mac/service_management.h",
     "mac/service_management.mm",
-    "mac/staging_watcher.h",
-    "mac/staging_watcher.mm",
     "media/media_resource_provider.cc",
     "media/media_resource_provider.h",
     "media_galleries/metadata_types.h",
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 58b2920..cd17fcd 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -318,10 +318,7 @@
   }, {
     "channel": "stable",
     "contexts": ["webui"],
-    "matches": [
-      "chrome://extensions/*",
-      "chrome://extensions-frame/*"  // TODO(dbeam): still needed?
-    ]
+    "matches": ["chrome://extensions/*"]
   }],
   // All devtools APIs are implemented by hand, so don't compile them.
   "devtools.inspectedWindow": {
@@ -439,7 +436,7 @@
     "internal": true,
     "channel": "stable",
     "contexts": ["webui"],
-    "matches": ["chrome://extensions-frame/*", "chrome://extensions/*"]
+    "matches": ["chrome://extensions/*"]
   },
   // This is not a real API, only here for documentation purposes.
   // See http://crbug.com/275944 for background.
diff --git a/chrome/common/mac/staging_watcher.h b/chrome/common/mac/staging_watcher.h
deleted file mode 100644
index 14d9acc..0000000
--- a/chrome/common/mac/staging_watcher.h
+++ /dev/null
@@ -1,72 +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 CHROME_COMMON_MAC_STAGING_WATCHER_H_
-#define CHROME_COMMON_MAC_STAGING_WATCHER_H_
-
-#import <Foundation/Foundation.h>
-
-// Chrome update works by staging a copy of Chrome near to the current bundle,
-// and then applying it when Chrome restarts to apply the update. Currently,
-// this state of "update pending" is indicated outside of Keystone by a key in
-// the CFPreferences.
-
-using StagingKeyChangedObserver = void (^)(BOOL stagingKeySet);
-
-// An object to observe the staging key. It can be used in either of two ways:
-// 1. To wait for the staging key to clear.
-// 2. To notify when the staging key changes state.
-@interface CrStagingKeyWatcher : NSObject
-
-// On macOS 10.11 and earlier, polling is used, and |pollingTime| specifies the
-// frequency of the polling. On macOS 10.12 and later, no polling is performed
-// and |pollingTime| is ignored.
-- (instancetype)initWithPollingTime:(NSTimeInterval)pollingTime;
-
-// Returns a boolean indicating whether or not the staging key is set.
-- (BOOL)isStagingKeySet;
-
-// Returns a boolean indicating whether or not the staging key is set. This will
-// not return the correct answer in testing.
-+ (BOOL)isStagingKeySet;
-
-// Returns the path to the staged update, or nil if there is no staging key set.
-- (NSString*)stagingLocation;
-
-// Returns the path to the staged update, or nil if there is no staging key set.
-// This will not return the correct answer in testing.
-+ (NSString*)stagingLocation;
-
-// Sleeps until the staging key is clear. If there is no staging key set,
-// returns immediately.
-- (void)waitForStagingKeyToClear;
-
-// Sets a block to be called when the staging key changes, and starts observing.
-// Only one observer may be set for a given CrStagingKeyWatcher; calling this
-// method again replaces the current observer.
-- (void)setStagingKeyChangedObserver:(StagingKeyChangedObserver)block;
-
-@end
-
-@interface CrStagingKeyWatcher (TestingInterface)
-
-// The designated initializer. Allows a non-default NSUserDefaults to be
-// specified. Also allows the use of KVO to be disabled to allow the macOS 10.11
-// and earlier code path to be tested on 10.12 and later.
-- (instancetype)initWithUserDefaults:(NSUserDefaults*)defaults
-                         pollingTime:(NSTimeInterval)pollingTime
-                disableKVOForTesting:(BOOL)disableKVOForTesting;
-
-// Returns whether the last call to -waitForStagingKeyToClear blocked or
-// returned immediately.
-- (BOOL)lastWaitWasBlockedForTesting;
-
-// Returns the NSUserDefaults key that is used to indicate staging. The value to
-// be used is a dictionary of strings, with the key being the file path to the
-// existing bundle, and the value being the file path to the staged bundle.
-+ (NSString*)stagingKeyForTesting;
-
-@end
-
-#endif  // CHROME_COMMON_MAC_STAGING_WATCHER_H_
diff --git a/chrome/common/mac/staging_watcher.mm b/chrome/common/mac/staging_watcher.mm
deleted file mode 100644
index c316b11e..0000000
--- a/chrome/common/mac/staging_watcher.mm
+++ /dev/null
@@ -1,191 +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 "chrome/common/mac/staging_watcher.h"
-
-#include "base/mac/bundle_locations.h"
-#include "base/mac/foundation_util.h"
-#include "base/mac/mac_util.h"
-#include "base/mac/scoped_block.h"
-#include "base/mac/scoped_nsobject.h"
-
-// Best documentation / Is unofficial documentation
-//
-// Required reading for CFPreferences/NSUserDefaults is at
-// <http://dscoder.com/defaults.html>, a post by David "Catfish Man" Smith, who
-// re-wrote NSUserDefaults for 10.12 and iPad Classroom support. It is important
-// to note that KVO only notifies for changes made by other programs starting
-// with that rewrite in 10.12. In macOS 10.11 and earlier, polling is the only
-// option. Note that NSUserDefaultsDidChangeNotification never notifies about
-// changes made by other programs, not even in 10.12 and later.
-//
-// On the other hand, KVO notification was broken in the NSUserDefaults rewrite
-// for 10.14; see:
-// - https://twitter.com/Catfish_Man/status/1116185288257105925
-// - rdar://49812220
-// The bug is that a change from "no value" to "value present" doesn't notify.
-// To work around that, a default is registered to ensure that there never is
-// a "no value" situation.
-
-namespace {
-
-NSString* const kStagingKey = @"UpdatePending";
-
-}  // namespace
-
-@interface CrStagingKeyWatcher () {
-  base::scoped_nsobject<NSUserDefaults> _defaults;
-  NSTimeInterval _pollingTime;
-  base::scoped_nsobject<NSTimer> _pollingTimer;
-  BOOL _observing;
-  base::mac::ScopedBlock<StagingKeyChangedObserver> _callback;
-  BOOL _lastStagingKeyValue;
-
-  BOOL _lastWaitWasBlockedForTesting;
-}
-
-+ (NSString*)stagingLocationWithUserDefaults:(NSUserDefaults*)defaults;
-
-@end
-
-@implementation CrStagingKeyWatcher
-
-- (instancetype)initWithPollingTime:(NSTimeInterval)pollingTime {
-  return [self initWithUserDefaults:[NSUserDefaults standardUserDefaults]
-                        pollingTime:pollingTime
-               disableKVOForTesting:NO];
-}
-
-- (instancetype)initWithUserDefaults:(NSUserDefaults*)defaults
-                         pollingTime:(NSTimeInterval)pollingTime
-                disableKVOForTesting:(BOOL)disableKVOForTesting {
-  if ((self = [super init])) {
-    _pollingTime = pollingTime;
-    _defaults.reset(defaults, base::scoped_policy::RETAIN);
-    [_defaults registerDefaults:@{kStagingKey : @[]}];
-    _lastStagingKeyValue = [self isStagingKeySet];
-    if (base::mac::IsAtLeastOS10_12() && !disableKVOForTesting) {
-      // If a change is made in another process (which is the use case here),
-      // the prior value is never provided in the observation callback change
-      // dictionary, whether or not NSKeyValueObservingOptionPrior is specified.
-      // Therefore, pass in 0 for the NSKeyValueObservingOptions and rely on
-      // keeping the previous value in |lastStagingKeyValue_|.
-      [_defaults addObserver:self
-                  forKeyPath:kStagingKey
-                     options:0
-                     context:nullptr];
-      _observing = YES;
-    }
-  }
-  return self;
-}
-
-+ (NSString*)stagingLocationWithUserDefaults:(NSUserDefaults*)defaults {
-  NSDictionary<NSString*, id>* stagedPathPairs =
-      [defaults dictionaryForKey:kStagingKey];
-  if (!stagedPathPairs)
-    return nil;
-
-  NSString* appPath = [base::mac::OuterBundle() bundlePath];
-
-  return base::mac::ObjCCast<NSString>([stagedPathPairs objectForKey:appPath]);
-}
-
-- (BOOL)isStagingKeySet {
-  return [self stagingLocation] != nil;
-}
-
-+ (BOOL)isStagingKeySet {
-  return [self stagingLocation] != nil;
-}
-
-- (NSString*)stagingLocation {
-  return [CrStagingKeyWatcher stagingLocationWithUserDefaults:_defaults];
-}
-
-+ (NSString*)stagingLocation {
-  return [self
-      stagingLocationWithUserDefaults:[NSUserDefaults standardUserDefaults]];
-}
-
-- (void)waitForStagingKeyToClear {
-  if (![self isStagingKeySet]) {
-    _lastWaitWasBlockedForTesting = NO;
-    return;
-  }
-
-  NSRunLoop* runloop = [NSRunLoop currentRunLoop];
-  if (_observing) {
-    _callback.reset(
-        ^(BOOL stagingKeySet) {
-          CFRunLoopStop([runloop getCFRunLoop]);
-        },
-        base::scoped_policy::RETAIN);
-
-    while ([self isStagingKeySet] && [runloop runMode:NSDefaultRunLoopMode
-                                           beforeDate:[NSDate distantFuture]]) {
-      /* run! */
-    }
-  } else {
-    while ([self isStagingKeySet] &&
-           [runloop
-                  runMode:NSDefaultRunLoopMode
-               beforeDate:[NSDate dateWithTimeIntervalSinceNow:_pollingTime]]) {
-      /* run! */
-    }
-  }
-
-  _lastWaitWasBlockedForTesting = YES;
-}
-
-- (void)setStagingKeyChangedObserver:(StagingKeyChangedObserver)block {
-  _callback.reset(block, base::scoped_policy::RETAIN);
-
-  if (_observing) {
-    // Nothing to be done; the observation is already started.
-  } else {
-    _pollingTimer.reset(
-        [NSTimer scheduledTimerWithTimeInterval:_pollingTime
-                                         target:self
-                                       selector:@selector(timerFired:)
-                                       userInfo:nil
-                                        repeats:YES],
-        base::scoped_policy::RETAIN);
-  }
-}
-
-- (void)timerFired:(NSTimer*)timer {
-  [self observeValueForKeyPath:nil ofObject:nil change:nil context:nil];
-}
-
-- (void)dealloc {
-  if (_observing)
-    [_defaults removeObserver:self forKeyPath:kStagingKey context:nullptr];
-  if (_pollingTimer)
-    [_pollingTimer invalidate];
-
-  [super dealloc];
-}
-
-- (BOOL)lastWaitWasBlockedForTesting {
-  return _lastWaitWasBlockedForTesting;
-}
-
-+ (NSString*)stagingKeyForTesting {
-  return kStagingKey;
-}
-
-- (void)observeValueForKeyPath:(NSString*)keyPath
-                      ofObject:(id)object
-                        change:(NSDictionary*)change
-                       context:(void*)context {
-  BOOL isStagingKeySet = [self isStagingKeySet];
-  if (isStagingKeySet == _lastStagingKeyValue)
-    return;
-
-  _lastStagingKeyValue = isStagingKeySet;
-  _callback.get()([self isStagingKeySet]);
-}
-
-@end
diff --git a/chrome/common/mac/staging_watcher_unittest.mm b/chrome/common/mac/staging_watcher_unittest.mm
deleted file mode 100644
index e3eb86a..0000000
--- a/chrome/common/mac/staging_watcher_unittest.mm
+++ /dev/null
@@ -1,181 +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 "chrome/common/mac/staging_watcher.h"
-
-#include <dispatch/dispatch.h>
-
-#include "base/mac/bundle_locations.h"
-#include "base/mac/mac_util.h"
-#include "base/mac/scoped_nsobject.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-enum class KVOOrNot { kUseKVO, kDontUseKVO };
-
-class StagingKeyWatcherTest : public testing::TestWithParam<KVOOrNot> {
- public:
-  StagingKeyWatcherTest() = default;
-  ~StagingKeyWatcherTest() = default;
-
- protected:
-  void SetUp() override {
-    testingBundleID_.reset([[NSString alloc]
-        initWithFormat:@"org.chromium.StagingKeyWatcherTest.%d", getpid()]);
-    defaults_.reset(
-        [[NSUserDefaults alloc] initWithSuiteName:testingBundleID_]);
-
-    [defaults_ removeObjectForKey:[CrStagingKeyWatcher stagingKeyForTesting]];
-  }
-
-  void TearDown() override {
-    [defaults_ removeObjectForKey:[CrStagingKeyWatcher stagingKeyForTesting]];
-  }
-
-  base::scoped_nsobject<CrStagingKeyWatcher> CreateKeyWatcher() {
-    base::scoped_nsobject<CrStagingKeyWatcher> keyWatcher(
-        [[CrStagingKeyWatcher alloc]
-            initWithUserDefaults:defaults_
-                     pollingTime:0.5
-            disableKVOForTesting:(GetParam() == KVOOrNot::kDontUseKVO)]);
-
-    return keyWatcher;
-  }
-
-  void SetDefaultsValue(id value) {
-    [defaults_ setObject:value
-                  forKey:[CrStagingKeyWatcher stagingKeyForTesting]];
-  }
-
-  void ClearDefaultsValueInSeparateProcess() {
-    [NSTask launchedTaskWithLaunchPath:@"/usr/bin/defaults"
-                             arguments:@[
-                               @"delete", testingBundleID_.get(),
-                               [CrStagingKeyWatcher stagingKeyForTesting]
-                             ]];
-  }
-
-  void SetDefaultsValueInSeparateProcess() {
-    NSString* appPath = [base::mac::OuterBundle() bundlePath];
-
-    [NSTask launchedTaskWithLaunchPath:@"/usr/bin/defaults"
-                             arguments:@[
-                               @"write", testingBundleID_.get(),
-                               [CrStagingKeyWatcher stagingKeyForTesting],
-                               @"-dict", appPath, appPath
-                             ]];
-  }
-
- private:
-  base::scoped_nsobject<NSString> testingBundleID_;
-  base::scoped_nsobject<NSUserDefaults> defaults_;
-};
-
-INSTANTIATE_TEST_SUITE_P(KVOandNot,
-                         StagingKeyWatcherTest,
-                         testing::Values(KVOOrNot::kUseKVO,
-                                         KVOOrNot::kDontUseKVO));
-
-}  // namespace
-
-TEST_P(StagingKeyWatcherTest, NoBlockingWhenNoKey) {
-  base::scoped_nsobject<CrStagingKeyWatcher> watcher = CreateKeyWatcher();
-  [watcher waitForStagingKeyToClear];
-  ASSERT_FALSE([watcher lastWaitWasBlockedForTesting]);
-}
-
-TEST_P(StagingKeyWatcherTest, NoBlockingWhenWrongKeyType) {
-  SetDefaultsValue(@"this is not a dictionary");
-
-  base::scoped_nsobject<CrStagingKeyWatcher> watcher = CreateKeyWatcher();
-  [watcher waitForStagingKeyToClear];
-  ASSERT_FALSE([watcher lastWaitWasBlockedForTesting]);
-}
-
-TEST_P(StagingKeyWatcherTest, NoBlockingWhenArrayType) {
-  SetDefaultsValue(@[ @3, @1, @4, @1, @5 ]);
-
-  base::scoped_nsobject<CrStagingKeyWatcher> watcher = CreateKeyWatcher();
-  [watcher waitForStagingKeyToClear];
-  ASSERT_FALSE([watcher lastWaitWasBlockedForTesting]);
-}
-
-TEST_P(StagingKeyWatcherTest, NoBlockingWhenEmptyArray) {
-  SetDefaultsValue(@[]);
-
-  base::scoped_nsobject<CrStagingKeyWatcher> watcher = CreateKeyWatcher();
-  [watcher waitForStagingKeyToClear];
-  ASSERT_FALSE([watcher lastWaitWasBlockedForTesting]);
-}
-
-TEST_P(StagingKeyWatcherTest, NoBlockingWhenEmptyDictionary) {
-  SetDefaultsValue(@{});
-
-  base::scoped_nsobject<CrStagingKeyWatcher> watcher = CreateKeyWatcher();
-  [watcher waitForStagingKeyToClear];
-  ASSERT_FALSE([watcher lastWaitWasBlockedForTesting]);
-}
-
-TEST_P(StagingKeyWatcherTest, BlockFunctionality) {
-  NSString* appPath = [base::mac::OuterBundle() bundlePath];
-  SetDefaultsValue(@{appPath : appPath});
-
-  NSRunLoop* runloop = [NSRunLoop currentRunLoop];
-  ASSERT_EQ(nil, [runloop currentMode]);
-
-  dispatch_async(dispatch_get_main_queue(), ^{
-    ASSERT_NE(nil, [runloop currentMode]);
-    ClearDefaultsValueInSeparateProcess();
-  });
-
-  base::scoped_nsobject<CrStagingKeyWatcher> watcher = CreateKeyWatcher();
-  [watcher waitForStagingKeyToClear];
-  ASSERT_TRUE([watcher lastWaitWasBlockedForTesting]);
-}
-
-TEST_P(StagingKeyWatcherTest, CallbackOnKeySet) {
-  // The staging key begins not set.
-
-  base::scoped_nsobject<CrStagingKeyWatcher> watcher = CreateKeyWatcher();
-  NSRunLoop* runloop = [NSRunLoop currentRunLoop];
-
-  __block bool observerCalled = false;
-  [watcher setStagingKeyChangedObserver:^(BOOL stagingKeySet) {
-    observerCalled = true;
-    CFRunLoopStop([runloop getCFRunLoop]);
-  }];
-
-  SetDefaultsValueInSeparateProcess();
-  ASSERT_FALSE([watcher isStagingKeySet]);
-  while (!observerCalled && [runloop runMode:NSDefaultRunLoopMode
-                                  beforeDate:[NSDate distantFuture]]) {
-    /* run! */
-  }
-
-  EXPECT_TRUE([watcher isStagingKeySet]);
-}
-
-TEST_P(StagingKeyWatcherTest, CallbackOnKeyUnset) {
-  NSString* appPath = [base::mac::OuterBundle() bundlePath];
-  SetDefaultsValue(@{appPath : appPath});
-
-  base::scoped_nsobject<CrStagingKeyWatcher> watcher = CreateKeyWatcher();
-  NSRunLoop* runloop = [NSRunLoop currentRunLoop];
-
-  __block bool observerCalled = false;
-  [watcher setStagingKeyChangedObserver:^(BOOL stagingKeySet) {
-    observerCalled = true;
-    CFRunLoopStop([runloop getCFRunLoop]);
-  }];
-
-  ClearDefaultsValueInSeparateProcess();
-  ASSERT_TRUE([watcher isStagingKeySet]);
-  while (!observerCalled && [runloop runMode:NSDefaultRunLoopMode
-                                  beforeDate:[NSDate distantFuture]]) {
-    /* run! */
-  }
-
-  EXPECT_FALSE([watcher isStagingKeySet]);
-}
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 1b746a4..7ca0422 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -13,7 +13,6 @@
 import("//build/toolchain/toolchain.gni")
 import("//build/util/version.gni")
 import("//chrome/browser/buildflags.gni")
-import("//chrome/chrome_repack_locales.gni")
 import("//chrome/common/features.gni")
 import("//chrome/test/base/js2gtest.gni")
 import("//chromeos/assistant/assistant.gni")
@@ -682,7 +681,6 @@
       "//base/test:test_support",
       "//base/util/memory_pressure:test_support",
       "//build:branding_buildflags",
-      "//chrome:browser_tests_pak",
       "//chrome:packed_resources",
       "//chrome:resources",
       "//chrome:strings",
@@ -770,6 +768,7 @@
 
     # Runtime dependencies
     data_deps = [
+      "//chrome:browser_tests_pak",
       "//chrome/browser/resources/media/mei_preload:component",
 
       # TODO(thakis): Why do these need copying in browser_tests?
@@ -817,7 +816,6 @@
       "//third_party/simplejson/",
       "//third_party/tlslite/",
       "//ui/webui/resources/",
-      "$root_out_dir/browser_tests.pak",
     ]
     data += js2gtest_js_libraries
 
@@ -1534,12 +1532,7 @@
     }
 
     if (!is_mac) {
-      data += [
-        "$root_out_dir/locales/",
-        "$root_out_dir/chrome_100_percent.pak",
-        "$root_out_dir/chrome_200_percent.pak",
-        "$root_out_dir/resources.pak",
-      ]
+      data_deps += [ "//chrome:packed_resources" ]
     }
 
     if (enable_captive_portal_detection) {
@@ -2534,7 +2527,7 @@
       }
     }
     if (enable_kaleidoscope) {
-      deps += [ "../browser/media/kaleidoscope/internal:browser_tests" ]
+      deps += [ "../browser/media/kaleidoscope/internal:browser_tests_js" ]
     }
     if (enable_legacy_desktop_in_product_help) {
       sources += [
@@ -3473,7 +3466,6 @@
     "../common/ini_parser_unittest.cc",
     "../common/mac/mock_launchd.h",
     "../common/mac/mock_launchd.mm",
-    "../common/mac/staging_watcher_unittest.mm",
     "../common/media_router/issue_unittest.cc",
     "../common/media_router/media_route_unittest.cc",
     "../common/media_router/media_sink_unittest.cc",
@@ -3603,12 +3595,7 @@
     ]
   }
   if (is_linux || is_win) {
-    data += [
-      "$root_out_dir/chrome_100_percent.pak",
-      "$root_out_dir/chrome_200_percent.pak",
-      "$root_out_dir/locales/en-US.pak",
-      "$root_out_dir/resources.pak",
-    ]
+    data_deps += [ "//chrome:packed_resources" ]
   }
   if (is_win) {
     data_deps += [ "//chrome" ]
@@ -5120,7 +5107,7 @@
     if (is_android) {
       deps += [ "//chrome/android:chrome_apk_paks" ]
     } else {
-      deps += [ "//chrome:packed_resources" ]
+      data_deps += [ "//chrome:packed_resources" ]
     }
   }
   if (is_win || is_mac || is_chromeos) {
@@ -5696,18 +5683,8 @@
       "$root_out_dir/test_case.html.mock-http-headers",
       "$root_out_dir/test_page.css",
       "$root_out_dir/test_page.css.mock-http-headers",
-      "$root_out_dir/ui_test.pak",
     ]
     data += js2gtest_js_libraries
-    if (is_linux || is_win) {
-      data += [
-        "$root_out_dir/chrome_100_percent.pak",
-        "$root_out_dir/chrome_200_percent.pak",
-        "$root_out_dir/locales/en-US.pak",
-        "$root_out_dir/locales/fr.pak",
-        "$root_out_dir/resources.pak",
-      ]
-    }
     if (is_linux) {
       data += [ "$root_out_dir/libppapi_tests.so" ]
     }
@@ -5754,7 +5731,6 @@
       "//ui/base:test_support",
       "//ui/base/clipboard:clipboard_test_support",
       "//ui/events:events_interactive_ui_tests",
-      "//ui/resources:ui_test_pak",
       "//ui/web_dialogs:test_support",
     ]
 
@@ -5766,7 +5742,11 @@
     data_deps = [
       "//ppapi:ppapi_tests",
       "//third_party/mesa_headers",
+      "//ui/resources:ui_test_pak_data",
     ]
+    if (is_linux || is_win) {
+      data_deps += [ "//chrome:packed_resources" ]
+    }
 
     if (use_aura) {
       sources += [ "../browser/ui/views/drag_and_drop_interactive_uitest.cc" ]
@@ -6252,15 +6232,6 @@
       "//testing/xvfb.py",
     ]
 
-    if (is_linux || is_win) {
-      data += [
-        "$root_out_dir/chrome_100_percent.pak",
-        "$root_out_dir/chrome_200_percent.pak",
-        "$root_out_dir/locales/en-US.pak",
-        "$root_out_dir/resources.pak",
-      ]
-    }
-
     # TODO(phajdan.jr): Only temporary, to make transition easier.
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
 
@@ -6290,6 +6261,10 @@
       "//third_party/mesa_headers",
     ]
 
+    if (is_linux || is_win) {
+      data_deps += [ "//chrome:packed_resources" ]
+    }
+
     if (is_mac) {
       # Dictionary sync is disabled on Mac.
       sources -= [
@@ -6490,11 +6465,8 @@
     ]
 
     if (is_linux || is_win) {
-      data += [
-        "$root_out_dir/chrome_100_percent.pak",
-        "$root_out_dir/chrome_200_percent.pak",
-        "$root_out_dir/locales/en-US.pak",
-        "$root_out_dir/resources.pak",
+      data_deps = [
+        "//chrome:packed_resources",
       ]
     }
 
@@ -6666,11 +6638,8 @@
       ]
     }
     if (!is_mac) {
-      data += [
-        "$root_out_dir/locales/",
-        "$root_out_dir/chrome_100_percent.pak",
-        "$root_out_dir/chrome_200_percent.pak",
-        "$root_out_dir/resources.pak",
+      data_deps = [
+        "//chrome:packed_resources",
       ]
     }
   }
@@ -6853,7 +6822,6 @@
 
     deps = [
       "//base",
-      "//chrome:packed_resources",
       "//chrome/test:browser_tests_runner",
       "//device/base",
       "//services/service_manager/sandbox",
@@ -6870,11 +6838,8 @@
     ]
 
     if (!is_mac) {
-      data += [
-        "$root_out_dir/locales/",
-        "$root_out_dir/chrome_100_percent.pak",
-        "$root_out_dir/chrome_200_percent.pak",
-        "$root_out_dir/resources.pak",
+      data_deps = [
+        "//chrome:packed_resources",
       ]
     }
   }
diff --git a/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.js b/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.js
index a2fa2fd5..c467deb 100644
--- a/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.js
+++ b/chrome/test/data/webui/new_tab_page/customize_dialog_focus_test.js
@@ -4,184 +4,44 @@
 
 import 'chrome://new-tab-page/customize_dialog.js';
 
-import {BrowserProxy} from 'chrome://new-tab-page/browser_proxy.js';
-import {assertFocus, keydown, TestProxy} from 'chrome://test/new_tab_page/test_support.js';
-import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
+import {keydown} from 'chrome://test/new_tab_page/test_support.js';
+import {flushTasks} from 'chrome://test/test_util.m.js';
 
 suite('NewTabPageCustomizeDialogFocusTest', () => {
   /** @type {!CustomizeDialogElement} */
   let customizeDialog;
 
-  /** @type {TestProxy} */
-  let testProxy;
-
-  function queryThemeIcons() {
-    return customizeDialog.shadowRoot.querySelectorAll(
-        '#themesContainer ntp-theme-icon');
-  }
-
   setup(async () => {
     PolymerTest.clearBody();
 
-    testProxy = new TestProxy();
-    BrowserProxy.instance_ = testProxy;
-
-    const colors = {frame: {value: 0xff000000}, activeTab: {value: 0xff0000ff}};
-    const themes = [];
-    for (let i = 0; i < 10; ++i) {
-      themes.push({id: i, label: `theme_${i}`, colors});
-    }
-    testProxy.handler.setResultFor('getChromeThemes', Promise.resolve({
-      chromeThemes: themes,
-    }));
     customizeDialog = document.createElement('ntp-customize-dialog');
     document.body.appendChild(customizeDialog);
     await flushTasks();
   });
 
-  test('right focuses right theme icon', async () => {
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[0], 'ArrowRight');
-
-    // Assert.
-    assertFocus(themeIcons[1]);
-  });
-
-  test('right wrap around focuses first theme icon', async () => {
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[themeIcons.length - 1], 'ArrowRight');
-
-    // Assert.
-    assertFocus(themeIcons[0]);
-  });
-
-  test('left focuses left theme icon', async () => {
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[1], 'ArrowLeft');
-
-    // Assert.
-    assertFocus(themeIcons[0]);
-  });
-
-  test('left wrap around focuses last theme icon', async () => {
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[0], 'ArrowLeft');
-
-    // Assert.
-    assertFocus(themeIcons[themeIcons.length - 1]);
-  });
-
-  test('right focuses left theme icon in RTL', async () => {
+  test('space selects focused menu item', async () => {
     // Arrange.
-    customizeDialog.dir = 'rtl';
+    const menuItem = customizeDialog.shadowRoot.querySelector(
+        '.menu-item[page-name="themes"');
+    menuItem.focus();
 
     // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[1], 'ArrowRight');
+    keydown(menuItem, ' ');
 
     // Assert.
-    assertFocus(themeIcons[0]);
+    assertEquals(customizeDialog.$.menu.selected, 'themes');
   });
 
-  test('right wrap around focuses last theme icon in RTL', async () => {
+  test('enter selects focused menu item', async () => {
     // Arrange.
-    customizeDialog.dir = 'rtl';
+    const menuItem = customizeDialog.shadowRoot.querySelector(
+        '.menu-item[page-name="shortcuts"');
+    menuItem.focus();
 
     // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[0], 'ArrowRight');
+    keydown(menuItem, 'Enter');
 
     // Assert.
-    assertFocus(themeIcons[themeIcons.length - 1]);
-  });
-
-  test('left focuses right theme icon in RTL', async () => {
-    // Arrange.
-    customizeDialog.dir = 'rtl';
-
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[0], 'ArrowLeft');
-
-    // Assert.
-    assertFocus(themeIcons[1]);
-  });
-
-  test('left wrap around focuses first theme icon in RTL', async () => {
-    // Arrange.
-    customizeDialog.dir = 'rtl';
-
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[themeIcons.length - 1], 'ArrowLeft');
-
-    // Assert.
-    assertFocus(themeIcons[0]);
-  });
-
-  test('down focuses below theme icon', async () => {
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[0], 'ArrowDown');
-
-    // Assert.
-    assertFocus(themeIcons[6]);
-  });
-
-  test('up focuses above theme icon', async () => {
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[6], 'ArrowUp');
-
-    // Assert.
-    assertFocus(themeIcons[0]);
-  });
-
-  test('down wrap around focuses top theme icon', async () => {
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[6], 'ArrowDown');
-
-    // Assert.
-    assertFocus(themeIcons[0]);
-  });
-
-  test('up wrap around focuses bottom theme icon', async () => {
-    // Act.
-    const themeIcons = queryThemeIcons();
-    keydown(themeIcons[0], 'ArrowDown');
-
-    // Assert.
-    assertFocus(themeIcons[6]);
-  });
-
-  test('enter clicks focused theme icon', async () => {
-    // Arrange.
-    const themeIcon = queryThemeIcons()[0];
-    themeIcon.focus();
-    const themeIconClicked = eventToPromise('click', themeIcon);
-
-    // Act.
-    keydown(themeIcon, 'Enter');
-
-    // Assert.
-    await themeIconClicked;
-  });
-
-  test('space clicks focused theme icon', async () => {
-    // Arrange.
-    const themeIcon = queryThemeIcons()[0];
-    themeIcon.focus();
-    const themeIconClicked = eventToPromise('click', themeIcon);
-
-    // Act.
-    keydown(themeIcon, ' ');
-
-    // Assert.
-    await themeIconClicked;
+    assertEquals(customizeDialog.$.menu.selected, 'shortcuts');
   });
 });
diff --git a/chrome/test/data/webui/new_tab_page/customize_dialog_test.js b/chrome/test/data/webui/new_tab_page/customize_dialog_test.js
index d8ef0eb..b18ba4df7 100644
--- a/chrome/test/data/webui/new_tab_page/customize_dialog_test.js
+++ b/chrome/test/data/webui/new_tab_page/customize_dialog_test.js
@@ -4,261 +4,42 @@
 
 import 'chrome://new-tab-page/customize_dialog.js';
 
-import {BrowserProxy} from 'chrome://new-tab-page/browser_proxy.js';
-import {assertStyle, TestProxy} from 'chrome://test/new_tab_page/test_support.js';
 import {flushTasks} from 'chrome://test/test_util.m.js';
 
 suite('NewTabPageCustomizeDialogTest', () => {
-  /** @type {TestProxy} */
-  let testProxy;
+  /** @type {!CustomizeDialogElement} */
+  let customizeDialog;
 
-  /** @return {!CustomizeDialogElement} */
-  function createCustomizeDialog() {
-    const customizeDialog = document.createElement('ntp-customize-dialog');
-    document.body.appendChild(customizeDialog);
-    return customizeDialog;
-  }
-
-
-  /** @return {!CustomizeDialogElement} */
-  function createCustomizeDialogWithThemes(themes) {
-    testProxy.handler.setResultFor('getChromeThemes', Promise.resolve({
-      chromeThemes: themes,
-    }));
-    return createCustomizeDialog();
-  }
-
-  setup(() => {
+  setup(async () => {
     PolymerTest.clearBody();
 
-    testProxy = new TestProxy();
-    BrowserProxy.instance_ = testProxy;
+    customizeDialog = document.createElement('ntp-customize-dialog');
+    document.body.appendChild(customizeDialog);
+    await flushTasks();
   });
 
-  test('opening dialog shows theme tiles', async () => {
-    // Act.
-    const themes = [
-      {
-        id: 0,
-        label: 'theme_0',
-        colors: {
-          frame: {value: 0xff000000},      // white.
-          activeTab: {value: 0xff0000ff},  // blue.
-        },
-      },
-      {
-        id: 1,
-        label: 'theme_1',
-        colors: {
-          frame: {value: 0xffff0000},      // red.
-          activeTab: {value: 0xff00ff00},  // green.
-        },
-      },
-    ];
-    const getChromeThemesCalled =
-        testProxy.handler.whenCalled('getChromeThemes');
+  test('creating customize dialog opens cr dialog', () => {
+    // Assert.
+    assertTrue(customizeDialog.$.dialog.open);
+  });
 
+  test('background page selected at start', () => {
+    // Assert.
+    const shownPages =
+        customizeDialog.shadowRoot.querySelectorAll('#pages .iron-selected');
+    assertEquals(shownPages.length, 1);
+    assertEquals(shownPages[0].getAttribute('page-name'), 'backgrounds');
+  });
+
+  test('selecting menu item shows page', async () => {
     // Act.
-    const customizeDialog = createCustomizeDialogWithThemes(themes);
-    await getChromeThemesCalled;
+    customizeDialog.$.menu.select('themes');
     await flushTasks();
 
     // Assert.
-    const tiles = customizeDialog.shadowRoot.querySelectorAll('ntp-theme-icon');
-    assertEquals(tiles.length, 4);
-    assertEquals(tiles[2].getAttribute('title'), 'theme_0');
-    assertStyle(tiles[2], '--ntp-theme-icon-frame-color', 'rgb(0, 0, 0)');
-    assertStyle(
-        tiles[2], '--ntp-theme-icon-active-tab-color', 'rgb(0, 0, 255)');
-    assertEquals(tiles[3].getAttribute('title'), 'theme_1');
-    assertStyle(tiles[3], '--ntp-theme-icon-frame-color', 'rgb(255, 0, 0)');
-    assertStyle(
-        tiles[3], '--ntp-theme-icon-active-tab-color', 'rgb(0, 255, 0)');
-  });
-
-  test('clicking default theme calls applying default theme', async () => {
-    // Arrange.
-    const customizeDialog = createCustomizeDialog();
-    const applyDefaultThemeCalled =
-        testProxy.handler.whenCalled('applyDefaultTheme');
-
-    // Act.
-    customizeDialog.$.defaultTheme.click();
-
-    // Assert.
-    await applyDefaultThemeCalled;
-  });
-
-  test('selecting color calls applying autogenerated theme', async () => {
-    // Arrange.
-    const customizeDialog = createCustomizeDialog();
-    const applyAutogeneratedThemeCalled =
-        testProxy.handler.whenCalled('applyAutogeneratedTheme');
-
-    // Act.
-    customizeDialog.$.colorPicker.value = '#ff0000';
-    customizeDialog.$.colorPicker.dispatchEvent(new Event('change'));
-
-    // Assert.
-    const {value} = await applyAutogeneratedThemeCalled;
-    assertEquals(value, 0xffff0000);
-  });
-
-  test('setting autogenerated theme selects and updates icon', async () => {
-    // Arrange.
-    const customizeDialog = createCustomizeDialog();
-
-    // Act.
-    customizeDialog.theme = {
-      type: newTabPage.mojom.ThemeType.AUTOGENERATED,
-      info: {
-        autogeneratedThemeColors: {
-          frame: {value: 0xffff0000},
-          activeTab: {value: 0xff0000ff},
-        },
-      },
-      backgroundColor: {value: 0xffff0000},
-      shortcutBackgroundColor: {value: 0xff00ff00},
-      shortcutTextColor: {value: 0xff0000ff},
-    };
-    await flushTasks();
-
-    // Assert.
-    const selectedIcons =
-        customizeDialog.shadowRoot.querySelectorAll('ntp-theme-icon[selected]');
-    assertEquals(selectedIcons.length, 1);
-    assertEquals(selectedIcons[0], customizeDialog.$.autogeneratedTheme);
-    assertStyle(
-        selectedIcons[0], '--ntp-theme-icon-frame-color', 'rgb(255, 0, 0)');
-    assertStyle(
-        selectedIcons[0], '--ntp-theme-icon-active-tab-color',
-        'rgb(0, 0, 255)');
-  });
-
-  test('setting default theme selects and updates icon', async () => {
-    // Arrange.
-    const customizeDialog = createCustomizeDialog();
-
-    // Act.
-    customizeDialog.theme = {
-      type: newTabPage.mojom.ThemeType.DEFAULT,
-      info: {chromeThemeId: 0},
-      backgroundColor: {value: 0xffff0000},
-      shortcutBackgroundColor: {value: 0xff00ff00},
-      shortcutTextColor: {value: 0xff0000ff},
-    };
-    await flushTasks();
-
-    // Assert.
-    const selectedIcons =
-        customizeDialog.shadowRoot.querySelectorAll('ntp-theme-icon[selected]');
-    assertEquals(selectedIcons.length, 1);
-    assertEquals(selectedIcons[0], customizeDialog.$.defaultTheme);
-  });
-
-  test('setting Chrome theme selects and updates icon', async () => {
-    // Arrange.
-    const themes = [
-      {
-        id: 0,
-        label: 'foo',
-        colors: {
-          frame: {value: 0xff000000},
-          activeTab: {value: 0xff0000ff},
-        },
-      },
-    ];
-    const customizeDialog = createCustomizeDialogWithThemes(themes);
-
-    // Act.
-    customizeDialog.theme = {
-      type: newTabPage.mojom.ThemeType.CHROME,
-      info: {chromeThemeId: 0},
-      backgroundColor: {value: 0xffff0000},
-      shortcutBackgroundColor: {value: 0xff00ff00},
-      shortcutTextColor: {value: 0xff0000ff},
-    };
-    await flushTasks();
-
-    // Assert.
-    const selectedIcons =
-        customizeDialog.shadowRoot.querySelectorAll('ntp-theme-icon[selected]');
-    assertEquals(selectedIcons.length, 1);
-    assertEquals(selectedIcons[0].getAttribute('title'), 'foo');
-  });
-
-  test('setting third-party theme shows uninstall UI', async () => {
-    // Arrange.
-    const customizeDialog = createCustomizeDialog();
-
-    // Act.
-    customizeDialog.theme = {
-      type: newTabPage.mojom.ThemeType.THIRD_PARTY,
-      info: {
-        thirdPartyThemeInfo: {
-          id: 'foo',
-          name: 'bar',
-        },
-      },
-      backgroundColor: {value: 0xffff0000},
-      shortcutBackgroundColor: {value: 0xff00ff00},
-      shortcutTextColor: {value: 0xff0000ff},
-    };
-    await testProxy.callbackRouterRemote.$.flushForTesting();
-
-    // Assert.
-    assertStyle(customizeDialog.$.thirdPartyThemeContainer, 'display', 'block');
-    assertEquals(
-        customizeDialog.$.thirdPartyThemeName.textContent.trim(), 'bar');
-    assertEquals(
-        customizeDialog.$.thirdPartyLink.getAttribute('href'),
-        'https://chrome.google.com/webstore/detail/foo');
-  });
-
-  test('setting non-third-party theme hides uninstall UI', async () => {
-    // Arrange.
-    const customizeDialog = createCustomizeDialog();
-
-    // Act.
-    customizeDialog.theme = {
-      type: newTabPage.mojom.ThemeType.DEFAULT,
-      info: {chromeThemeId: 0},
-      backgroundColor: {value: 0xffff0000},
-      shortcutBackgroundColor: {value: 0xff00ff00},
-      shortcutTextColor: {value: 0xff0000ff},
-    };
-    await testProxy.callbackRouterRemote.$.flushForTesting();
-
-    // Assert.
-    assertStyle(customizeDialog.$.thirdPartyThemeContainer, 'display', 'none');
-  });
-
-  test('uninstalling third-party theme sets default theme', async () => {
-    // Arrange.
-    const customizeDialog = createCustomizeDialog();
-    customizeDialog.theme = {
-      type: newTabPage.mojom.ThemeType.THIRD_PARTY,
-      info: {
-        thirdPartyThemeInfo: {
-          id: 'foo',
-          name: 'bar',
-        },
-      },
-      backgroundColor: {value: 0xffff0000},
-      shortcutBackgroundColor: {value: 0xff00ff00},
-      shortcutTextColor: {value: 0xff0000ff},
-    };
-    await testProxy.callbackRouterRemote.$.flushForTesting();
-    const applyDefaultThemeCalled =
-        testProxy.handler.whenCalled('applyDefaultTheme');
-    const confirmThemeChangesCalled =
-        testProxy.handler.whenCalled('confirmThemeChanges');
-
-    // Act.
-    customizeDialog.$.uninstallThirdPartyButton.click();
-
-    // Assert.
-    await applyDefaultThemeCalled;
-    await confirmThemeChangesCalled;
+    const shownPages =
+        customizeDialog.shadowRoot.querySelectorAll('#pages .iron-selected');
+    assertEquals(shownPages.length, 1);
+    assertEquals(shownPages[0].getAttribute('page-name'), 'themes');
   });
 });
diff --git a/chrome/test/data/webui/new_tab_page/customize_themes_focus_test.js b/chrome/test/data/webui/new_tab_page/customize_themes_focus_test.js
new file mode 100644
index 0000000..be37c5f
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/customize_themes_focus_test.js
@@ -0,0 +1,187 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://new-tab-page/customize_themes.js';
+
+import {BrowserProxy} from 'chrome://new-tab-page/browser_proxy.js';
+import {assertFocus, keydown, TestProxy} from 'chrome://test/new_tab_page/test_support.js';
+import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
+
+suite('NewTabPageCustomizeThemesFocusTest', () => {
+  /** @type {!customizeThemesElement} */
+  let customizeThemes;
+
+  /** @type {TestProxy} */
+  let testProxy;
+
+  function queryThemeIcons() {
+    return customizeThemes.shadowRoot.querySelectorAll(
+        '#themesContainer ntp-theme-icon');
+  }
+
+  setup(async () => {
+    PolymerTest.clearBody();
+
+    testProxy = new TestProxy();
+    BrowserProxy.instance_ = testProxy;
+
+    const colors = {frame: {value: 0xff000000}, activeTab: {value: 0xff0000ff}};
+    const themes = [];
+    for (let i = 0; i < 10; ++i) {
+      themes.push({id: i, label: `theme_${i}`, colors});
+    }
+    testProxy.handler.setResultFor('getChromeThemes', Promise.resolve({
+      chromeThemes: themes,
+    }));
+    customizeThemes = document.createElement('ntp-customize-themes');
+    document.body.appendChild(customizeThemes);
+    await flushTasks();
+  });
+
+  test('right focuses right theme icon', async () => {
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[0], 'ArrowRight');
+
+    // Assert.
+    assertFocus(themeIcons[1]);
+  });
+
+  test('right wrap around focuses first theme icon', async () => {
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[themeIcons.length - 1], 'ArrowRight');
+
+    // Assert.
+    assertFocus(themeIcons[0]);
+  });
+
+  test('left focuses left theme icon', async () => {
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[1], 'ArrowLeft');
+
+    // Assert.
+    assertFocus(themeIcons[0]);
+  });
+
+  test('left wrap around focuses last theme icon', async () => {
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[0], 'ArrowLeft');
+
+    // Assert.
+    assertFocus(themeIcons[themeIcons.length - 1]);
+  });
+
+  test('right focuses left theme icon in RTL', async () => {
+    // Arrange.
+    customizeThemes.dir = 'rtl';
+
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[1], 'ArrowRight');
+
+    // Assert.
+    assertFocus(themeIcons[0]);
+  });
+
+  test('right wrap around focuses last theme icon in RTL', async () => {
+    // Arrange.
+    customizeThemes.dir = 'rtl';
+
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[0], 'ArrowRight');
+
+    // Assert.
+    assertFocus(themeIcons[themeIcons.length - 1]);
+  });
+
+  test('left focuses right theme icon in RTL', async () => {
+    // Arrange.
+    customizeThemes.dir = 'rtl';
+
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[0], 'ArrowLeft');
+
+    // Assert.
+    assertFocus(themeIcons[1]);
+  });
+
+  test('left wrap around focuses first theme icon in RTL', async () => {
+    // Arrange.
+    customizeThemes.dir = 'rtl';
+
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[themeIcons.length - 1], 'ArrowLeft');
+
+    // Assert.
+    assertFocus(themeIcons[0]);
+  });
+
+  test('down focuses below theme icon', async () => {
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[0], 'ArrowDown');
+
+    // Assert.
+    assertFocus(themeIcons[6]);
+  });
+
+  test('up focuses above theme icon', async () => {
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[6], 'ArrowUp');
+
+    // Assert.
+    assertFocus(themeIcons[0]);
+  });
+
+  test('down wrap around focuses top theme icon', async () => {
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[6], 'ArrowDown');
+
+    // Assert.
+    assertFocus(themeIcons[0]);
+  });
+
+  test('up wrap around focuses bottom theme icon', async () => {
+    // Act.
+    const themeIcons = queryThemeIcons();
+    keydown(themeIcons[0], 'ArrowDown');
+
+    // Assert.
+    assertFocus(themeIcons[6]);
+  });
+
+  test('enter clicks focused theme icon', async () => {
+    // Arrange.
+    const themeIcon = queryThemeIcons()[0];
+    themeIcon.focus();
+    const themeIconClicked = eventToPromise('click', themeIcon);
+
+    // Act.
+    keydown(themeIcon, 'Enter');
+
+    // Assert.
+    await themeIconClicked;
+  });
+
+  test('space clicks focused theme icon', async () => {
+    // Arrange.
+    const themeIcon = queryThemeIcons()[0];
+    themeIcon.focus();
+    const themeIconClicked = eventToPromise('click', themeIcon);
+
+    // Act.
+    keydown(themeIcon, ' ');
+
+    // Assert.
+    await themeIconClicked;
+  });
+});
diff --git a/chrome/test/data/webui/new_tab_page/customize_themes_test.js b/chrome/test/data/webui/new_tab_page/customize_themes_test.js
new file mode 100644
index 0000000..7480342a
--- /dev/null
+++ b/chrome/test/data/webui/new_tab_page/customize_themes_test.js
@@ -0,0 +1,263 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://new-tab-page/customize_themes.js';
+
+import {BrowserProxy} from 'chrome://new-tab-page/browser_proxy.js';
+import {assertStyle, TestProxy} from 'chrome://test/new_tab_page/test_support.js';
+import {flushTasks} from 'chrome://test/test_util.m.js';
+
+suite('NewTabPageCustomizeThemesTest', () => {
+  /** @type {TestProxy} */
+  let testProxy;
+
+  /** @return {!CustomizeThemesElement} */
+  function createCustomizeThemes() {
+    const customizeThemes = document.createElement('ntp-customize-themes');
+    document.body.appendChild(customizeThemes);
+    return customizeThemes;
+  }
+
+  /** @return {!CustomizeThemesElement} */
+  function createCustomizeThemesWithThemes(themes) {
+    testProxy.handler.setResultFor('getChromeThemes', Promise.resolve({
+      chromeThemes: themes,
+    }));
+    return createCustomizeThemes();
+  }
+
+  setup(() => {
+    PolymerTest.clearBody();
+
+    testProxy = new TestProxy();
+    BrowserProxy.instance_ = testProxy;
+  });
+
+  test('creating element shows theme tiles', async () => {
+    // Act.
+    const themes = [
+      {
+        id: 0,
+        label: 'theme_0',
+        colors: {
+          frame: {value: 0xff000000},      // white.
+          activeTab: {value: 0xff0000ff},  // blue.
+        },
+      },
+      {
+        id: 1,
+        label: 'theme_1',
+        colors: {
+          frame: {value: 0xffff0000},      // red.
+          activeTab: {value: 0xff00ff00},  // green.
+        },
+      },
+    ];
+    const getChromeThemesCalled =
+        testProxy.handler.whenCalled('getChromeThemes');
+
+    // Act.
+    const customizeThemes = createCustomizeThemesWithThemes(themes);
+    await getChromeThemesCalled;
+    await flushTasks();
+
+    // Assert.
+    const tiles = customizeThemes.shadowRoot.querySelectorAll('ntp-theme-icon');
+    assertEquals(tiles.length, 4);
+    assertEquals(tiles[2].getAttribute('title'), 'theme_0');
+    assertStyle(tiles[2], '--ntp-theme-icon-frame-color', 'rgb(0, 0, 0)');
+    assertStyle(
+        tiles[2], '--ntp-theme-icon-active-tab-color', 'rgb(0, 0, 255)');
+    assertEquals(tiles[3].getAttribute('title'), 'theme_1');
+    assertStyle(tiles[3], '--ntp-theme-icon-frame-color', 'rgb(255, 0, 0)');
+    assertStyle(
+        tiles[3], '--ntp-theme-icon-active-tab-color', 'rgb(0, 255, 0)');
+  });
+
+  test('clicking default theme calls applying default theme', async () => {
+    // Arrange.
+    const customizeThemes = createCustomizeThemes();
+    const applyDefaultThemeCalled =
+        testProxy.handler.whenCalled('applyDefaultTheme');
+
+    // Act.
+    customizeThemes.$.defaultTheme.click();
+
+    // Assert.
+    await applyDefaultThemeCalled;
+  });
+
+  test('selecting color calls applying autogenerated theme', async () => {
+    // Arrange.
+    const customizeThemes = createCustomizeThemes();
+    const applyAutogeneratedThemeCalled =
+        testProxy.handler.whenCalled('applyAutogeneratedTheme');
+
+    // Act.
+    customizeThemes.$.colorPicker.value = '#ff0000';
+    customizeThemes.$.colorPicker.dispatchEvent(new Event('change'));
+
+    // Assert.
+    const {value} = await applyAutogeneratedThemeCalled;
+    assertEquals(value, 0xffff0000);
+  });
+
+  test('setting autogenerated theme selects and updates icon', async () => {
+    // Arrange.
+    const customizeThemes = createCustomizeThemes();
+
+    // Act.
+    customizeThemes.theme = {
+      type: newTabPage.mojom.ThemeType.AUTOGENERATED,
+      info: {
+        autogeneratedThemeColors: {
+          frame: {value: 0xffff0000},
+          activeTab: {value: 0xff0000ff},
+        },
+      },
+      backgroundColor: {value: 0xffff0000},
+      shortcutBackgroundColor: {value: 0xff00ff00},
+      shortcutTextColor: {value: 0xff0000ff},
+    };
+    await flushTasks();
+
+    // Assert.
+    const selectedIcons =
+        customizeThemes.shadowRoot.querySelectorAll('ntp-theme-icon[selected]');
+    assertEquals(selectedIcons.length, 1);
+    assertEquals(selectedIcons[0], customizeThemes.$.autogeneratedTheme);
+    assertStyle(
+        selectedIcons[0], '--ntp-theme-icon-frame-color', 'rgb(255, 0, 0)');
+    assertStyle(
+        selectedIcons[0], '--ntp-theme-icon-active-tab-color',
+        'rgb(0, 0, 255)');
+  });
+
+  test('setting default theme selects and updates icon', async () => {
+    // Arrange.
+    const customizeThemes = createCustomizeThemes();
+
+    // Act.
+    customizeThemes.theme = {
+      type: newTabPage.mojom.ThemeType.DEFAULT,
+      info: {chromeThemeId: 0},
+      backgroundColor: {value: 0xffff0000},
+      shortcutBackgroundColor: {value: 0xff00ff00},
+      shortcutTextColor: {value: 0xff0000ff},
+    };
+    await flushTasks();
+
+    // Assert.
+    const selectedIcons =
+        customizeThemes.shadowRoot.querySelectorAll('ntp-theme-icon[selected]');
+    assertEquals(selectedIcons.length, 1);
+    assertEquals(selectedIcons[0], customizeThemes.$.defaultTheme);
+  });
+
+  test('setting Chrome theme selects and updates icon', async () => {
+    // Arrange.
+    const themes = [
+      {
+        id: 0,
+        label: 'foo',
+        colors: {
+          frame: {value: 0xff000000},
+          activeTab: {value: 0xff0000ff},
+        },
+      },
+    ];
+    const customizeThemes = createCustomizeThemesWithThemes(themes);
+
+    // Act.
+    customizeThemes.theme = {
+      type: newTabPage.mojom.ThemeType.CHROME,
+      info: {chromeThemeId: 0},
+      backgroundColor: {value: 0xffff0000},
+      shortcutBackgroundColor: {value: 0xff00ff00},
+      shortcutTextColor: {value: 0xff0000ff},
+    };
+    await flushTasks();
+
+    // Assert.
+    const selectedIcons =
+        customizeThemes.shadowRoot.querySelectorAll('ntp-theme-icon[selected]');
+    assertEquals(selectedIcons.length, 1);
+    assertEquals(selectedIcons[0].getAttribute('title'), 'foo');
+  });
+
+  test('setting third-party theme shows uninstall UI', async () => {
+    // Arrange.
+    const customizeThemes = createCustomizeThemes();
+
+    // Act.
+    customizeThemes.theme = {
+      type: newTabPage.mojom.ThemeType.THIRD_PARTY,
+      info: {
+        thirdPartyThemeInfo: {
+          id: 'foo',
+          name: 'bar',
+        },
+      },
+      backgroundColor: {value: 0xffff0000},
+      shortcutBackgroundColor: {value: 0xff00ff00},
+      shortcutTextColor: {value: 0xff0000ff},
+    };
+    await testProxy.callbackRouterRemote.$.flushForTesting();
+
+    // Assert.
+    assertStyle(customizeThemes.$.thirdPartyThemeContainer, 'display', 'block');
+    assertEquals(
+        customizeThemes.$.thirdPartyThemeName.textContent.trim(), 'bar');
+    assertEquals(
+        customizeThemes.$.thirdPartyLink.getAttribute('href'),
+        'https://chrome.google.com/webstore/detail/foo');
+  });
+
+  test('setting non-third-party theme hides uninstall UI', async () => {
+    // Arrange.
+    const customizeThemes = createCustomizeThemes();
+
+    // Act.
+    customizeThemes.theme = {
+      type: newTabPage.mojom.ThemeType.DEFAULT,
+      info: {chromeThemeId: 0},
+      backgroundColor: {value: 0xffff0000},
+      shortcutBackgroundColor: {value: 0xff00ff00},
+      shortcutTextColor: {value: 0xff0000ff},
+    };
+    await testProxy.callbackRouterRemote.$.flushForTesting();
+
+    // Assert.
+    assertStyle(customizeThemes.$.thirdPartyThemeContainer, 'display', 'none');
+  });
+
+  test('uninstalling third-party theme sets default theme', async () => {
+    // Arrange.
+    const customizeThemes = createCustomizeThemes();
+    customizeThemes.theme = {
+      type: newTabPage.mojom.ThemeType.THIRD_PARTY,
+      info: {
+        thirdPartyThemeInfo: {
+          id: 'foo',
+          name: 'bar',
+        },
+      },
+      backgroundColor: {value: 0xffff0000},
+      shortcutBackgroundColor: {value: 0xff00ff00},
+      shortcutTextColor: {value: 0xff0000ff},
+    };
+    await testProxy.callbackRouterRemote.$.flushForTesting();
+    const applyDefaultThemeCalled =
+        testProxy.handler.whenCalled('applyDefaultTheme');
+    const confirmThemeChangesCalled =
+        testProxy.handler.whenCalled('confirmThemeChanges');
+
+    // Act.
+    customizeThemes.$.uninstallThirdPartyButton.click();
+
+    // Assert.
+    await applyDefaultThemeCalled;
+    await confirmThemeChangesCalled;
+  });
+});
diff --git a/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js b/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
index d152068..9b83dede 100644
--- a/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
+++ b/chrome/test/data/webui/new_tab_page/new_tab_page_browsertest.js
@@ -64,6 +64,18 @@
 });
 
 // eslint-disable-next-line no-var
+var NewTabPageCustomizeThemesTest = class extends NewTabPageBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_themes_test.js';
+  }
+};
+
+TEST_F('NewTabPageCustomizeThemesTest', 'All', function() {
+  mocha.run();
+});
+
+// eslint-disable-next-line no-var
 var NewTabPageThemeIconTest = class extends NewTabPageBrowserTest {
   /** @override */
   get browsePreload() {
diff --git a/chrome/test/data/webui/new_tab_page/new_tab_page_interactive_test.js b/chrome/test/data/webui/new_tab_page/new_tab_page_interactive_test.js
index 981a292..e511364 100644
--- a/chrome/test/data/webui/new_tab_page/new_tab_page_interactive_test.js
+++ b/chrome/test/data/webui/new_tab_page/new_tab_page_interactive_test.js
@@ -51,3 +51,16 @@
 TEST_F('NewTabPageCustomizeDialogFocusTest', 'All', function() {
   mocha.run();
 });
+
+// eslint-disable-next-line no-var
+var NewTabPageCustomizeThemesFocusTest =
+    class extends NewTabPageInteractiveTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/customize_themes_focus_test.js';
+  }
+};
+
+TEST_F('NewTabPageCustomizeThemesFocusTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js b/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js
index 6af84228..2afa1f39 100644
--- a/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/cups_printer_page_tests.js
@@ -66,13 +66,7 @@
   }
 
   function mockAddPrinterInputKeyboardPress(crInputId) {
-    // Starts in discovery dialog, select add manually button.
-    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-    assertTrue(!!discoveryDialog);
-    discoveryDialog.$.manuallyAddPrinterButton.click();
-    Polymer.dom.flush();
-
-    // Now we should be in the manually add dialog.
+    // Start in add manual dialog.
     const addDialog = dialog.$$('add-printer-manually-dialog');
     assertTrue(!!addDialog);
 
@@ -134,26 +128,6 @@
     page = null;
   });
 
-  /**
-   * Test that the discovery dialog is showing when a user initially asks
-   * to add a printer.
-   */
-  test('DiscoveryShowing', function() {
-    return test_util.flushTasks().then(function() {
-      // Discovery is showing.
-      assertTrue(dialog.showDiscoveryDialog_);
-      assertTrue(!!dialog.$$('add-printer-discovery-dialog'));
-
-      // All other components are hidden.
-      assertFalse(dialog.showManufacturerDialog_);
-      assertFalse(!!dialog.$$('add-printer-manufacturer-model-dialog'));
-      assertFalse(dialog.showConfiguringDialog_);
-      assertFalse(!!dialog.$$('add-printer-configuring-dialog'));
-      assertFalse(dialog.showManuallyAddDialog_);
-      assertFalse(!!dialog.$$('add-printer-manually-dialog'));
-    });
-  });
-
   test('ValidIPV4', function() {
     const dialog = document.createElement('add-printer-manually-dialog');
     expectTrue(canAddPrinter(dialog, 'Test printer', '127.0.0.1'));
@@ -215,15 +189,10 @@
    * Test that clicking on Add opens the model select page.
    */
   test('ValidAddOpensModelSelection', function() {
-    // Starts in discovery dialog, select add manually button.
-    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-    assertTrue(!!discoveryDialog);
-    discoveryDialog.$.manuallyAddPrinterButton.click();
-    Polymer.dom.flush();
-
-    // Now we should be in the manually add dialog.
+    // Starts in add manual dialog.
     const addDialog = dialog.$$('add-printer-manually-dialog');
     assertTrue(!!addDialog);
+    Polymer.dom.flush();
     fillAddManuallyDialog(addDialog);
 
     addDialog.$$('.action-button').click();
@@ -237,13 +206,10 @@
         })
         .then(function() {
           // Showing model selection.
-          assertFalse(!!dialog.$$('add-printer-configuring-dialog'));
           assertTrue(!!dialog.$$('add-printer-manufacturer-model-dialog'));
 
           assertTrue(dialog.showManufacturerDialog_);
-          assertFalse(dialog.showConfiguringDialog_);
           assertFalse(dialog.showManuallyAddDialog_);
-          assertFalse(dialog.showDiscoveryDialog_);
         });
   });
 
@@ -252,15 +218,10 @@
    * message is shown.
    */
   test('GetPrinterInfoFailsGeneralError', function() {
-    // Starts in discovery dialog, select add manually button.
-    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-    assertTrue(!!discoveryDialog);
-    discoveryDialog.$.manuallyAddPrinterButton.click();
-    Polymer.dom.flush();
-
-    // Now we should be in the manually add dialog.
+    // Starts in add manual dialog.
     const addDialog = dialog.$$('add-printer-manually-dialog');
     assertTrue(!!addDialog);
+    Polymer.dom.flush();
 
     fillAddManuallyDialog(addDialog);
 
@@ -287,15 +248,10 @@
    * address field is marked as invalid.
    */
   test('GetPrinterInfoFailsUnreachableError', function() {
-    // Starts in discovery dialog, select add manually button.
-    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-    assertTrue(!!discoveryDialog);
-    discoveryDialog.$.manuallyAddPrinterButton.click();
-    Polymer.dom.flush();
-
-    // Now we should be in the manually add dialog.
+    // Starts in add manual dialog.
     const addDialog = dialog.$$('add-printer-manually-dialog');
     assertTrue(!!addDialog);
+    Polymer.dom.flush();
 
     fillAddManuallyDialog(addDialog);
 
@@ -320,13 +276,10 @@
    * Test that getModels isn't called with a blank query.
    */
   test('NoBlankQueries', function() {
-    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-    assertTrue(!!discoveryDialog);
-    discoveryDialog.$.manuallyAddPrinterButton.click();
-    Polymer.dom.flush();
-
+    // Starts in add manual dialog.
     const addDialog = dialog.$$('add-printer-manually-dialog');
     assertTrue(!!addDialog);
+    Polymer.dom.flush();
     fillAddManuallyDialog(addDialog);
 
     // Verify that getCupsPrinterModelList is not called.
@@ -358,7 +311,7 @@
    */
   test('LogDialogCancelledIpp', function() {
     const makeAndModel = 'Printer Make And Model';
-    // Start on add manually.
+    // Start on add manual dialog.
     dialog.fire('open-manually-add-printer-dialog');
     Polymer.dom.flush();
 
@@ -396,6 +349,7 @@
     // Press the add button to advance dialog.
     const addDialog = dialog.$$('add-printer-manually-dialog');
     assertTrue(!!addDialog);
+    Polymer.dom.flush();
     clickAddButton(addDialog);
 
     // Click cancel on the manufacturer dialog when it shows up then verify
@@ -415,150 +369,14 @@
   });
 
   /**
-   * Test that dialog cancellation is logged from the manufacturer screen for
-   * USB printers.
-   */
-  test('LogDialogCancelledUSB', function() {
-    const vendorId = 0x1234;
-    const modelId = 0xDEAD;
-    const manufacturer = 'PrinterMFG';
-    const model = 'Printy Printerson';
-
-    const usbInfo = {
-      usbVendorId: vendorId,
-      usbProductId: modelId,
-      usbVendorName: manufacturer,
-      usbProductName: model,
-    };
-
-    const expectedPrinter = 'PICK_ME!';
-
-    const newPrinter = {
-      ppdManufacturer: '',
-      ppdModel: '',
-      printerAddress: 'EEAADDAA',
-      printerDescription: '',
-      printerId: expectedPrinter,
-      printerManufacturer: '',
-      printerModel: '',
-      printerMakeAndModel: '',
-      printerName: 'printer',
-      printerPPDPath: '',
-      printerPpdReference: {
-        userSuppliedPpdUrl: '',
-        effectiveMakeAndModel: '',
-        autoconf: false,
-      },
-      printerProtocol: 'usb',
-      printerQueue: 'moreinfohere',
-      printerStatus: '',
-      printerUsbInfo: usbInfo,
-    };
-
-    dialog.fire('open-discovery-printers-dialog');
-
-    // Make 'addDiscoveredPrinter' fail so we get sent to the make/model dialog.
-    cupsPrintersBrowserProxy.setAddDiscoveredPrinterFailure(newPrinter);
-
-    return cupsPrintersBrowserProxy.whenCalled('startDiscoveringPrinters')
-        .then(function() {
-          // Select the printer.
-          // TODO(skau): Figure out how to select in a dom-repeat.
-          const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-          assertTrue(!!discoveryDialog, 'Cannot find discovery dialog');
-          discoveryDialog.selectedPrinter = newPrinter;
-          // Run printer setup.
-          clickAddButton(discoveryDialog);
-          return cupsPrintersBrowserProxy.whenCalled('addDiscoveredPrinter');
-        })
-        .then(function(printerId) {
-          assertEquals(expectedPrinter, printerId);
-
-          return cupsPrintersBrowserProxy.whenCalled(
-              'getCupsPrinterManufacturersList');
-        })
-        .then(function() {
-          // Cancel setup with the cancel button.
-          clickCancelButton(dialog.$$('add-printer-manufacturer-model-dialog'));
-          return cupsPrintersBrowserProxy.whenCalled('cancelPrinterSetUp');
-        })
-        .then(function(printer) {
-          assertEquals(expectedPrinter, printer.printerId);
-          assertDeepEquals(usbInfo, printer.printerUsbInfo);
-        });
-  });
-
-  /**
-   * Test that the close button exists on the configure dialog.
-   */
-  test('ConfigureDialogCancelDisabled', function() {
-    const newPrinter = {
-      ppdManufacturer: '',
-      ppdModel: '',
-      printerAddress: 'EEAADDAA',
-      printerDescription: '',
-      printerId: 'printerId',
-      printerManufacturer: '',
-      printerModel: '',
-      printerMakeAndModel: '',
-      printerName: 'printer',
-      printerPPDPath: '',
-      printerPpdReference: {
-        userSuppliedPpdUrl: '',
-        effectiveMakeAndModel: '',
-        autoconf: false,
-      },
-      printerProtocol: 'usb',
-      printerQueue: 'moreinfohere',
-      printerStatus: '',
-      printerUsbInfo: '',
-    };
-
-    dialog.fire('open-discovery-printers-dialog');
-
-    return cupsPrintersBrowserProxy.whenCalled('startDiscoveringPrinters')
-        .then(function() {
-          // Select the printer.
-          const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-          assertTrue(!!discoveryDialog, 'Cannot find discovery dialog');
-          discoveryDialog.selectedPrinter = newPrinter;
-          // Run printer setup.
-          clickAddButton(discoveryDialog);
-          return cupsPrintersBrowserProxy.whenCalled('addDiscoveredPrinter');
-        })
-        .then(function(printerId) {
-          const configureDialog = dialog.$$('add-printer-configuring-dialog');
-          assertTrue(!!configureDialog);
-
-          const closeButton = configureDialog.$$('.cancel-button');
-          assertTrue(!!closeButton);
-          assertFalse(closeButton.disabled);
-
-          const waitForClose =
-              test_util.eventToPromise('close', configureDialog);
-
-          closeButton.click();
-          Polymer.dom.flush();
-
-          return waitForClose.then(() => {
-            dialog = page.$$('settings-cups-add-printer-dialog');
-            assertFalse(dialog.showConfiguringDialog_);
-          });
-        });
-  });
-
-  /**
    * Test that we are checking if a printer model has an EULA upon a model
    * change.
    */
   test('getEulaUrlGetsCalledOnModelChange', function() {
-    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-    assertTrue(!!discoveryDialog);
-    discoveryDialog.$.manuallyAddPrinterButton.click();
-    Polymer.dom.flush();
-
+    // Start in add manual dialog.
     const addDialog = dialog.$$('add-printer-manually-dialog');
     assertTrue(!!addDialog);
+    Polymer.dom.flush();
     fillAddManuallyDialog(addDialog);
 
     addDialog.$$('.action-button').click();
@@ -629,16 +447,11 @@
    * clicking it.
    */
   test('AddButtonDisabledAfterClicking', function() {
-    // Starting in the discovery dialog, select the add manually button.
-    const discoveryDialog = dialog.$$('add-printer-discovery-dialog');
-    assertTrue(!!discoveryDialog);
-    discoveryDialog.$.manuallyAddPrinterButton.click();
-    Polymer.dom.flush();
-
     // From the add manually dialog, click the add button to advance to the
     // manufacturer dialog.
     const addDialog = dialog.$$('add-printer-manually-dialog');
     assertTrue(!!addDialog);
+    Polymer.dom.flush();
     fillAddManuallyDialog(addDialog);
     clickAddButton(addDialog);
     Polymer.dom.flush();
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn
index 2688324..86befe4 100644
--- a/chrome/updater/mac/BUILD.gn
+++ b/chrome/updater/mac/BUILD.gn
@@ -7,6 +7,7 @@
 group("mac") {
   deps = [
     ":updater_bundle",
+    ":updater_install_script",
     ":updater_setup",
   ]
 }
@@ -87,3 +88,12 @@
     ":updater_setup_sources",
   ]
 }
+
+copy("updater_install_script") {
+  sources = [
+    "setup/.install.sh",
+  ]
+  outputs = [
+    "$root_build_dir/chrome/updater/.install.sh",
+  ]
+}
diff --git a/chrome/updater/mac/setup/.install.sh b/chrome/updater/mac/setup/.install.sh
new file mode 100755
index 0000000..7c145ba
--- /dev/null
+++ b/chrome/updater/mac/setup/.install.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script invokes updater_setup
+
+SCRIPT_PATH="$(dirname "$0")"
+
+"$SCRIPT_PATH/updater_setup"
diff --git a/chromeos/components/sync_wifi/network_identifier.cc b/chromeos/components/sync_wifi/network_identifier.cc
index ea8783f3..52ac855 100644
--- a/chromeos/components/sync_wifi/network_identifier.cc
+++ b/chromeos/components/sync_wifi/network_identifier.cc
@@ -18,7 +18,7 @@
 
 namespace {
 
-const char kDelimeter[] = "_";
+const char kDelimeter[] = "<||>";
 
 }  // namespace
 
diff --git a/chromeos/components/sync_wifi/network_identifier_unittest.cc b/chromeos/components/sync_wifi/network_identifier_unittest.cc
index a918bf4c..c6399c9c 100644
--- a/chromeos/components/sync_wifi/network_identifier_unittest.cc
+++ b/chromeos/components/sync_wifi/network_identifier_unittest.cc
@@ -39,7 +39,7 @@
 }
 
 TEST_F(NetworkIdentifierTest, FromString) {
-  std::string string_id("0123456789ABCDEF_psk");
+  std::string string_id("0123456789ABCDEF<||>psk");
   NetworkIdentifier id = NetworkIdentifier::DeserializeFromString(string_id);
   EXPECT_EQ(kHexSsid, id.hex_ssid());
   EXPECT_EQ(shill::kSecurityPsk, id.security_type());
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 911d999..0058999 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -50,10 +50,6 @@
   if (is_android || is_linux || is_mac || is_win) {
     data = [
       "test/data/",
-
-      # TODO(dpranke): Remove the next two lines after GN has rolled to 339778.
-      "$root_out_dir/components_tests_resources.pak",
-      "$root_out_dir/ui_test.pak",
     ]
   }
 
@@ -296,7 +292,7 @@
     data_deps = [
       ":components_tests_pak",
       "//third_party/mesa_headers",
-      "//ui/resources:ui_test_pak",
+      "//ui/resources:ui_test_pak_data",
     ]
   }  # iOS/!iOS
 
@@ -321,6 +317,7 @@
       "//components/gcm_driver/instance_id/android:instance_id_driver_test_support_java",
       "//components/invalidation/impl",
       "//components/invalidation/impl:java",
+      "//components/paint_preview/browser/android:java",
       "//components/paint_preview/player/android:unit_tests",
       "//components/policy/android:policy_java",
       "//components/signin/core/browser",
@@ -670,15 +667,13 @@
 
     data_deps = [
       ":components_tests_pak",
-      "//ui/resources:ui_test_pak",
+      "//ui/resources:ui_test_pak_data",
 
       # Needed for isolate script to execute.
       "//testing:run_perf_test",
     ]
 
     data = [
-      "$root_out_dir/components_tests_resources.pak",
-      "$root_out_dir/ui_test.pak",
       "//components/subresource_filter/core/common/perftests/data/",
       "//third_party/subresource-filter-ruleset/data/",
     ]
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
index 164a249..a77becc 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/files/file_path.h"
+#include "base/json/json_reader.h"
 #include "base/location.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
@@ -39,6 +40,22 @@
 
 namespace data_reduction_proxy {
 
+namespace {
+
+base::Optional<base::Value> GetSaveDataSavingsPercentEstimateFromFieldTrial() {
+  if (!base::FeatureList::IsEnabled(features::kReportSaveDataSavings))
+    return base::nullopt;
+  const auto origin_savings_estimate_json =
+      base::GetFieldTrialParamValueByFeature(features::kReportSaveDataSavings,
+                                             "origin_savings_estimate");
+  if (origin_savings_estimate_json.empty())
+    return base::nullopt;
+
+  return base::JSONReader::Read(origin_savings_estimate_json);
+}
+
+}  // namespace
+
 DataReductionProxyService::DataReductionProxyService(
     DataReductionProxySettings* settings,
     PrefService* prefs,
@@ -62,7 +79,9 @@
       data_use_measurement_(data_use_measurement),
       effective_connection_type_(net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN),
       client_(client),
-      channel_(channel) {
+      channel_(channel),
+      save_data_savings_estimate_dict_(
+          GetSaveDataSavingsPercentEstimateFromFieldTrial()) {
   DCHECK(settings);
   DCHECK(network_quality_tracker_);
   DCHECK(network_connection_tracker_);
@@ -405,8 +424,6 @@
   // process (renderer).
 
   if (bypass_duration < base::TimeDelta()) {
-    LOG(ERROR) << "Received bad MarkProxiesAsBad() -- invalid bypass_duration: "
-               << bypass_duration;
     std::move(callback).Run();
     return;
   }
@@ -420,8 +437,6 @@
   // received (FindConfiguredDataReductionProxy() searches recent proxies too).
   for (const auto& proxy : bad_proxies.GetAll()) {
     if (!config_->FindConfiguredDataReductionProxy(proxy)) {
-      LOG(ERROR) << "Received bad MarkProxiesAsBad() -- not a DRP server: "
-                 << proxy.ToURI();
       std::move(callback).Run();
       return;
     }
@@ -550,4 +565,17 @@
     config_client_->Initialize(url_loader_factory_);
 }
 
+double DataReductionProxyService::GetSaveDataSavingsPercentEstimate(
+    const std::string& origin) const {
+  if (origin.empty() || !save_data_savings_estimate_dict_ ||
+      !save_data_savings_estimate_dict_->is_dict()) {
+    return 0;
+  }
+  const auto savings_percent =
+      save_data_savings_estimate_dict_->FindDoubleKey(origin);
+  if (!savings_percent)
+    return 0;
+  return *savings_percent;
+}
+
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
index b59b72d..295c7b7b 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "base/values.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_metrics.h"
 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h"
 #include "components/data_reduction_proxy/core/browser/db_data_owner.h"
@@ -164,6 +165,10 @@
   void Clone(
       mojo::PendingReceiver<mojom::DataReductionProxy> receiver) override;
 
+  // Returns the percentage of data savings estimate provided by save-data for
+  // an origin.
+  double GetSaveDataSavingsPercentEstimate(const std::string& origin) const;
+
   // Accessor methods.
   DataReductionProxyCompressionStats* compression_stats() const {
     return compression_stats_.get();
@@ -309,6 +314,9 @@
   // is unavailable, then the destruction will happen on the UI thread.
   std::unique_ptr<NetworkPropertiesManager> network_properties_manager_;
 
+  // Dictionary of save-data savings estimates by origin.
+  const base::Optional<base::Value> save_data_savings_estimate_dict_;
+
   // The set of clients that will get updates about changes to the proxy config.
   mojo::RemoteSet<network::mojom::CustomProxyConfigClient>
       proxy_config_clients_;
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
index 4946a8c..476cce2a 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.cc
@@ -74,5 +74,10 @@
     "DataReductionProxyAggressiveConfigFetch",
     base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Reports estimated data savings due to save-data request header and JS API, as
+// savings provided by DataSaver.
+const base::Feature kReportSaveDataSavings{"ReportSaveDataSavings",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
index 81b4514..350b746 100644
--- a/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
+++ b/components/data_reduction_proxy/core/common/data_reduction_proxy_features.h
@@ -21,6 +21,7 @@
 extern const base::Feature kDataReductionProxyDisableProxyFailedWarmup;
 extern const base::Feature kDataReductionProxyServerExperiments;
 extern const base::Feature kDataReductionProxyAggressiveConfigFetch;
+extern const base::Feature kReportSaveDataSavings;
 
 }  // namespace features
 }  // namespace data_reduction_proxy
diff --git a/components/dom_distiller/OWNERS b/components/dom_distiller/OWNERS
index f500ab38..063c57c 100644
--- a/components/dom_distiller/OWNERS
+++ b/components/dom_distiller/OWNERS
@@ -1,4 +1,5 @@
 bengr@chromium.org
+gilmanmh@google.com
 mdjones@chromium.org
 nyquist@chromium.org
 wychen@chromium.org
diff --git a/components/leveldb_proto/internal/proto_database_impl_unittest.cc b/components/leveldb_proto/internal/proto_database_impl_unittest.cc
index ec4d02f..4aca604 100644
--- a/components/leveldb_proto/internal/proto_database_impl_unittest.cc
+++ b/components/leveldb_proto/internal/proto_database_impl_unittest.cc
@@ -161,6 +161,8 @@
         kDefaultClientName, shared_db_temp_dir_.GetPath()));
   }
 
+  void TearDown() override { shared_db_->Shutdown(); }
+
   void SetUpExperimentParams(std::map<std::string, std::string> params) {
     scoped_feature_list_.InitAndEnableFeatureWithParameters(
         kProtoDBSharedMigration, params);
diff --git a/components/leveldb_proto/internal/shared_proto_database.cc b/components/leveldb_proto/internal/shared_proto_database.cc
index e48c6085..56d35fa 100644
--- a/components/leveldb_proto/internal/shared_proto_database.cc
+++ b/components/leveldb_proto/internal/shared_proto_database.cc
@@ -31,10 +31,6 @@
 
 }  // namespace
 
-// static
-const base::TimeDelta SharedProtoDatabase::kDelayToClearObsoleteDatabase =
-    base::TimeDelta::FromSeconds(120);
-
 inline void RunInitStatusCallbackOnCallingSequence(
     SharedProtoDatabase::SharedClientInitCallback callback,
     scoped_refptr<base::SequencedTaskRunner> callback_task_runner,
@@ -435,6 +431,28 @@
   }
 
   ProcessInitRequests(status);
+
+  if (init_state_ == InitState::kSuccess) {
+    // Hold on to shared db until the remove operation is done or Shutdown()
+    // clears the task.
+    Callbacks::UpdateCallback keep_shared_db_alive =
+        base::BindOnce([](scoped_refptr<SharedProtoDatabase>, bool) {},
+                       base::WrapRefCounted<>(this));
+    delete_obsolete_task_.Reset(base::BindOnce(
+        &SharedProtoDatabase::DestroyObsoleteSharedProtoDatabaseClients, this,
+        std::move(keep_shared_db_alive)));
+    task_runner_->PostDelayedTask(FROM_HERE, delete_obsolete_task_.callback(),
+                                  delete_obsolete_delay_);
+  }
+}
+
+void SharedProtoDatabase::Shutdown() {
+  if (!task_runner_->RunsTasksInCurrentSequence()) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&SharedProtoDatabase::Shutdown, this));
+    return;
+  }
+  delete_obsolete_task_.Cancel();
 }
 
 void SharedProtoDatabase::OnUpdateCorruptionCountAtInit(bool success) {
@@ -530,6 +548,19 @@
       this));
 }
 
+void SharedProtoDatabase::DestroyObsoleteSharedProtoDatabaseClients(
+    Callbacks::UpdateCallback done) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(on_task_runner_);
+  // Create a ProtoLevelDBWrapper just like we create for each client, for
+  // deleting data from obsolete clients. It is fine to use the same wrapper to
+  // clear data from all clients. This object will be destroyed after clearing
+  // data for all these clients.
+  auto db_wrapper =
+      std::make_unique<ProtoLevelDBWrapper>(task_runner_, db_.get());
+  SharedProtoDatabaseClient::DestroyObsoleteSharedProtoDatabaseClients(
+      std::move(db_wrapper), std::move(done));
+}
+
 LevelDB* SharedProtoDatabase::GetLevelDBForTesting() const {
   return db_.get();
 }
diff --git a/components/leveldb_proto/internal/shared_proto_database.h b/components/leveldb_proto/internal/shared_proto_database.h
index 0f66355..11cbed6 100644
--- a/components/leveldb_proto/internal/shared_proto_database.h
+++ b/components/leveldb_proto/internal/shared_proto_database.h
@@ -10,6 +10,7 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
+#include "base/cancelable_callback.h"
 #include "base/component_export.h"
 #include "base/containers/queue.h"
 #include "base/memory/ref_counted.h"
@@ -53,6 +54,12 @@
       SharedDBMetadataProto::MigrationStatus migration_status,
       Callbacks::UpdateCallback callback);
 
+ protected:
+  SharedProtoDatabase(const std::string& client_db_id,
+                      const base::FilePath& db_dir);
+
+  virtual ~SharedProtoDatabase();
+
  private:
   friend class base::RefCountedThreadSafe<SharedProtoDatabase>;
   friend class ProtoDatabaseProvider;
@@ -61,6 +68,9 @@
   friend class SharedProtoDatabaseTest;
   friend class SharedProtoDatabaseClientTest;
   friend class TestSharedProtoDatabase;
+  FRIEND_TEST_ALL_PREFIXES(SharedProtoDatabaseTest,
+                           CancelDeleteObsoleteClients);
+  FRIEND_TEST_ALL_PREFIXES(SharedProtoDatabaseTest, DeleteObsoleteClients);
 
   enum InitState {
     // Initialization hasn't been attempted.
@@ -93,11 +103,7 @@
   // affecting startup or navigations.
   static const base::TimeDelta kDelayToClearObsoleteDatabase;
 
-  // Private since we only want to create a singleton of it.
-  SharedProtoDatabase(const std::string& client_db_id,
-                      const base::FilePath& db_dir);
-
-  virtual ~SharedProtoDatabase();
+  void Shutdown();
 
   void ProcessInitRequests(Enums::InitStatus status);
 
@@ -145,8 +151,16 @@
       Callbacks::InitStatusCallback callback,
       scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
 
+  // |done| will be called on |task_runner|.
+  virtual void DestroyObsoleteSharedProtoDatabaseClients(
+      Callbacks::UpdateCallback done);
+
   LevelDB* GetLevelDBForTesting() const;
 
+  void set_delete_obsolete_delay_for_testing(base::TimeDelta delay) {
+    delete_obsolete_delay_ = delay;
+  }
+
   scoped_refptr<base::SequencedTaskRunner> database_task_runner_for_testing()
       const {
     return task_runner_;
@@ -175,6 +189,9 @@
   base::queue<std::unique_ptr<InitRequest>> outstanding_init_requests_;
   bool create_if_missing_ = false;
 
+  base::TimeDelta delete_obsolete_delay_ = base::TimeDelta::FromSeconds(120);
+  base::CancelableOnceClosure delete_obsolete_task_;
+
   DISALLOW_COPY_AND_ASSIGN(SharedProtoDatabase);
 };
 
diff --git a/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc b/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc
index c5dc678..c61847d 100644
--- a/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc
+++ b/components/leveldb_proto/internal/shared_proto_database_client_unittest.cc
@@ -28,6 +28,11 @@
         new SharedProtoDatabase("client", temp_dir_.GetPath()));
   }
 
+  void TearDown() override {
+    if (db_)
+      db_->Shutdown();
+  }
+
  protected:
   scoped_refptr<SharedProtoDatabase> db() { return db_; }
   base::ScopedTempDir* temp_dir() { return &temp_dir_; }
diff --git a/components/leveldb_proto/internal/shared_proto_database_unittest.cc b/components/leveldb_proto/internal/shared_proto_database_unittest.cc
index d48c80f3..79ec500 100644
--- a/components/leveldb_proto/internal/shared_proto_database_unittest.cc
+++ b/components/leveldb_proto/internal/shared_proto_database_unittest.cc
@@ -11,8 +11,11 @@
 #include "build/build_config.h"
 #include "components/leveldb_proto/internal/proto_leveldb_wrapper.h"
 #include "components/leveldb_proto/testing/proto/test_db.pb.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::_;
+
 namespace leveldb_proto {
 
 namespace {
@@ -34,15 +37,31 @@
 
 }  // namespace
 
+class MockSharedDb : public SharedProtoDatabase {
+ public:
+  MockSharedDb(const std::string& client_db_id, const base::FilePath& db_dir)
+      : SharedProtoDatabase(client_db_id, db_dir) {}
+  MOCK_METHOD1(DestroyObsoleteSharedProtoDatabaseClients,
+               void(Callbacks::UpdateCallback));
+
+ private:
+  friend class base::RefCountedThreadSafe<MockSharedDb>;
+  friend class SharedProtoDatabaseTest;
+
+  ~MockSharedDb() override = default;
+};
+
 class SharedProtoDatabaseTest : public testing::Test {
  public:
   void SetUp() override {
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-    db_ = base::WrapRefCounted(
-        new SharedProtoDatabase("client", temp_dir_.GetPath()));
+    db_ = base::WrapRefCounted(new MockSharedDb("client", temp_dir_.GetPath()));
   }
 
-  void TearDown() override {}
+  void TearDown() override {
+    if (db_)
+      db_->Shutdown();
+  }
 
   void InitDB(bool create_if_missing,
               const std::string& client_name,
@@ -54,7 +73,10 @@
                        task_environment_.GetMainThreadTaskRunner()));
   }
 
-  void KillDB() { db_.reset(); }
+  void KillDB() {
+    db_->Shutdown();
+    db_.reset();
+  }
 
   bool IsDatabaseInitialized(SharedProtoDatabase* db) {
     return db->init_state_ == SharedProtoDatabase::InitState::kSuccess;
@@ -86,14 +108,14 @@
     return task_environment_.GetMainThreadTaskRunner();
   }
 
-  SharedProtoDatabase* db() { return db_.get(); }
+  MockSharedDb* db() { return db_.get(); }
   ProtoLevelDBWrapper* wrapper() { return db_->db_wrapper_.get(); }
 
  private:
   base::ScopedTempDir temp_dir_;
   base::test::TaskEnvironment task_environment_;
 
-  scoped_refptr<SharedProtoDatabase> db_;
+  scoped_refptr<MockSharedDb> db_;
 };
 
 TEST_F(SharedProtoDatabaseTest, CreateClient_SucceedsWithCreate) {
@@ -207,4 +229,52 @@
   KillDB();
 }
 
+TEST_F(SharedProtoDatabaseTest, CancelDeleteObsoleteClients) {
+  base::RunLoop run_init_loop;
+  EXPECT_CALL(*db(), DestroyObsoleteSharedProtoDatabaseClients(_)).Times(0);
+  InitDB(true /* create_if_missing */, "TestDatabaseUMA",
+         base::BindOnce(
+             [](base::OnceClosure signal, Enums::InitStatus status,
+                SharedDBMetadataProto::MigrationStatus migration_status) {
+               EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+                         migration_status);
+               ASSERT_EQ(status, Enums::InitStatus::kOK);
+               std::move(signal).Run();
+             },
+             run_init_loop.QuitClosure()));
+  run_init_loop.Run();
+
+  auto db_task_runner = db()->database_task_runner_for_testing();
+
+  KillDB();
+
+  base::RunLoop wait_task;
+  db_task_runner->PostTask(FROM_HERE, wait_task.QuitClosure());
+  wait_task.Run();
+}
+
+TEST_F(SharedProtoDatabaseTest, DeleteObsoleteClients) {
+  db()->set_delete_obsolete_delay_for_testing(base::TimeDelta());
+  EXPECT_CALL(*db(), DestroyObsoleteSharedProtoDatabaseClients(_)).Times(1);
+  base::RunLoop run_init_loop;
+  InitDB(true /* create_if_missing */, "TestDatabaseUMA",
+         base::BindOnce(
+             [](base::OnceClosure signal, Enums::InitStatus status,
+                SharedDBMetadataProto::MigrationStatus migration_status) {
+               EXPECT_EQ(SharedDBMetadataProto::MIGRATION_NOT_ATTEMPTED,
+                         migration_status);
+               ASSERT_EQ(status, Enums::InitStatus::kOK);
+               std::move(signal).Run();
+             },
+             run_init_loop.QuitClosure()));
+  run_init_loop.Run();
+  auto db_task_runner = db()->database_task_runner_for_testing();
+
+  base::RunLoop wait_task;
+  db_task_runner->PostTask(FROM_HERE, wait_task.QuitClosure());
+  wait_task.Run();
+
+  KillDB();
+}
+
 }  // namespace leveldb_proto
diff --git a/components/leveldb_proto/public/proto_database_provider.cc b/components/leveldb_proto/public/proto_database_provider.cc
index 48b2b73..5a3f716 100644
--- a/components/leveldb_proto/public/proto_database_provider.cc
+++ b/components/leveldb_proto/public/proto_database_provider.cc
@@ -28,7 +28,11 @@
     : profile_dir_(profile_dir),
       client_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
 
-ProtoDatabaseProvider::~ProtoDatabaseProvider() = default;
+ProtoDatabaseProvider::~ProtoDatabaseProvider() {
+  base::AutoLock lock(get_db_lock_);
+  if (db_)
+    db_->Shutdown();
+}
 
 void ProtoDatabaseProvider::GetSharedDBInstance(
     GetSharedDBInstanceCallback callback,
diff --git a/components/paint_preview/browser/BUILD.gn b/components/paint_preview/browser/BUILD.gn
index 0236311c..e3c93e68 100644
--- a/components/paint_preview/browser/BUILD.gn
+++ b/components/paint_preview/browser/BUILD.gn
@@ -41,6 +41,10 @@
     "//url",
   ]
 
+  if (is_android) {
+    deps += [ ":jni_headers" ]
+  }
+
   public_deps = [
     "//components/paint_preview/common",
     "//components/paint_preview/common/mojom",
@@ -50,6 +54,14 @@
   ]
 }
 
+if (is_android) {
+  generate_jni("jni_headers") {
+    sources = [
+      "android/java/src/org/chromium/components/paintpreview/browser/PaintPreviewBaseService.java",
+    ]
+  }
+}
+
 source_set("test_support") {
   testonly = true
   sources = [
diff --git a/components/paint_preview/browser/android/BUILD.gn b/components/paint_preview/browser/android/BUILD.gn
index 39c1e99..5ba9cdd 100644
--- a/components/paint_preview/browser/android/BUILD.gn
+++ b/components/paint_preview/browser/android/BUILD.gn
@@ -16,6 +16,7 @@
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 
   sources = [
+    "java/src/org/chromium/components/paintpreview/browser/PaintPreviewBaseService.java",
     "java/src/org/chromium/components/paintpreview/browser/PaintPreviewUtils.java",
   ]
 
diff --git a/components/paint_preview/browser/android/java/src/org/chromium/components/paintpreview/browser/PaintPreviewBaseService.java b/components/paint_preview/browser/android/java/src/org/chromium/components/paintpreview/browser/PaintPreviewBaseService.java
new file mode 100644
index 0000000..ec5d7cf
--- /dev/null
+++ b/components/paint_preview/browser/android/java/src/org/chromium/components/paintpreview/browser/PaintPreviewBaseService.java
@@ -0,0 +1,29 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.paintpreview.browser;
+
+import org.chromium.base.annotations.CalledByNative;
+
+/**
+ * The Java-side implementation of paint_preview_base_service.cc. This class is owned and managed by
+ * its C++ counterpart.
+ */
+public class PaintPreviewBaseService {
+    private long mNativePaintPreviewBaseService;
+
+    @CalledByNative
+    public PaintPreviewBaseService(long nativePaintPreviewBaseService) {
+        mNativePaintPreviewBaseService = nativePaintPreviewBaseService;
+    }
+
+    @CalledByNative
+    public void onDestroy() {
+        mNativePaintPreviewBaseService = 0;
+    }
+
+    public long getNativePaintPreviewBaseService() {
+        return mNativePaintPreviewBaseService;
+    }
+}
\ No newline at end of file
diff --git a/components/paint_preview/browser/paint_preview_base_service.cc b/components/paint_preview/browser/paint_preview_base_service.cc
index 4babde5..a6e395e 100644
--- a/components/paint_preview/browser/paint_preview_base_service.cc
+++ b/components/paint_preview/browser/paint_preview_base_service.cc
@@ -10,6 +10,8 @@
 #include "base/callback.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/task/post_task.h"
+#include "build/build_config.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/paint_preview/browser/compositor_utils.h"
 #include "components/paint_preview/browser/file_manager.h"
@@ -19,6 +21,12 @@
 #include "content/public/browser/web_contents.h"
 #include "ui/gfx/geometry/rect.h"
 
+#if defined(OS_ANDROID)
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#include "components/paint_preview/browser/jni_headers/PaintPreviewBaseService_jni.h"
+#endif  // defined(OS_ANDROID)
+
 namespace paint_preview {
 
 namespace {
@@ -35,9 +43,27 @@
     : policy_(std::move(policy)),
       file_manager_(
           path.AppendASCII(kPaintPreviewDir).AppendASCII(ascii_feature_name)),
-      is_off_the_record_(is_off_the_record) {}
+      is_off_the_record_(is_off_the_record) {
+#if defined(OS_ANDROID)
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jobject> java_ref =
+      Java_PaintPreviewBaseService_Constructor(
+          env, reinterpret_cast<intptr_t>(this));
+  java_ref_.Reset(java_ref);
+#endif  // defined(OS_ANDROID)
+}
 
-PaintPreviewBaseService::~PaintPreviewBaseService() = default;
+PaintPreviewBaseService::~PaintPreviewBaseService() {
+#if defined(OS_ANDROID)
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_PaintPreviewBaseService_onDestroy(env, java_ref_);
+#endif  // defined(OS_ANDROID)
+}
+
+base::Optional<PaintPreviewProto>
+PaintPreviewBaseService::GetCapturedPaintPreviewProto(const GURL& url) {
+  return base::nullopt;
+}
 
 void PaintPreviewBaseService::CapturePaintPreview(
     content::WebContents* web_contents,
diff --git a/components/paint_preview/browser/paint_preview_base_service.h b/components/paint_preview/browser/paint_preview_base_service.h
index 790b875..bf17fe8 100644
--- a/components/paint_preview/browser/paint_preview_base_service.h
+++ b/components/paint_preview/browser/paint_preview_base_service.h
@@ -9,8 +9,11 @@
 
 #include "base/callback.h"
 #include "base/files/file_path.h"
+#include "base/macros.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/unguessable_token.h"
+#include "build/build_config.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/paint_preview/browser/file_manager.h"
 #include "components/paint_preview/browser/paint_preview_policy.h"
@@ -18,6 +21,10 @@
 #include "components/paint_preview/common/proto/paint_preview.pb.h"
 #include "components/paint_preview/public/paint_preview_compositor_service.h"
 #include "content/public/browser/web_contents.h"
+#if defined(OS_ANDROID)
+#include "base/android/jni_android.h"
+#include "base/android/scoped_java_ref.h"
+#endif  // defined(OS_ANDROID)
 
 namespace paint_preview {
 
@@ -63,6 +70,12 @@
   // Returns whether the created service is off the record.
   bool IsOffTheRecord() const { return is_off_the_record_; }
 
+  // Returns the PaintPreviewProto that is associated with |url|. Implementers
+  // of this class should override this function as it returns base::nullopt by
+  // default.
+  virtual base::Optional<PaintPreviewProto> GetCapturedPaintPreviewProto(
+      const GURL& url);
+
   // The following methods both capture a Paint Preview; however, their behavior
   // and intended use is different. The first method is intended for capturing
   // full page contents. Generally, this is what you should be using for most
@@ -76,7 +89,6 @@
   // if a capture fails the service implementation is responsible for
   // implementing this management and tracking the directories in existence.
   // Data in a directory will contain:
-  // - paint_preview.pb (the metadata proto)
   // - a number of SKPs listed as <guid>.skp (one per frame)
   //
   // Captures the main frame of |web_contents| (an observer for capturing Paint
@@ -100,6 +112,12 @@
   std::unique_ptr<PaintPreviewCompositorService> StartCompositorService(
       base::OnceClosure disconnect_handler);
 
+#if defined(OS_ANDROID)
+  base::android::ScopedJavaGlobalRef<jobject> GetJavaObject() {
+    return java_ref_;
+  }
+#endif  // defined(OS_ANDROID)
+
  private:
   void OnCaptured(OnCapturedCallback callback,
                   base::UnguessableToken guid,
@@ -110,6 +128,11 @@
   FileManager file_manager_;
   bool is_off_the_record_;
 
+#if defined(OS_ANDROID)
+  // Points to the Java reference.
+  base::android::ScopedJavaGlobalRef<jobject> java_ref_;
+#endif  // defined(OS_ANDROID)
+
   base::WeakPtrFactory<PaintPreviewBaseService> weak_ptr_factory_{this};
 
   PaintPreviewBaseService(const PaintPreviewBaseService&) = delete;
diff --git a/components/paint_preview/player/BUILD.gn b/components/paint_preview/player/BUILD.gn
index b29390d..e731552 100644
--- a/components/paint_preview/player/BUILD.gn
+++ b/components/paint_preview/player/BUILD.gn
@@ -13,7 +13,11 @@
   deps = [
     "//base",
     "//components/paint_preview/browser",
+    "//components/paint_preview/common",
+    "//components/paint_preview/common/proto",
+    "//components/paint_preview/public",
     "//components/services/paint_preview_compositor",
+    "//mojo/public/cpp/bindings",
     "//ui/gfx/geometry",
     "//url",
   ]
diff --git a/components/paint_preview/player/android/BUILD.gn b/components/paint_preview/player/android/BUILD.gn
index 5f88e6d..abd9485c 100644
--- a/components/paint_preview/player/android/BUILD.gn
+++ b/components/paint_preview/player/android/BUILD.gn
@@ -34,6 +34,9 @@
     ":player_android",
     "//base",
     "//base/test:test_support",
+    "//components/paint_preview/browser",
+    "//components/paint_preview/player",
+    "//components/services/paint_preview_compositor/public/mojom",
     "//skia",
     "//testing/gmock",
     "//testing/gtest",
@@ -67,6 +70,7 @@
   deps = [
     "//base:base_java",
     "//base:jni_java",
+    "//components/paint_preview/browser/android:java",
     "//ui/android:ui_java",
   ]
 }
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java
index 9ff36fee..d8a1b145 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerCompositorDelegateImpl.java
@@ -12,6 +12,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
+import org.chromium.components.paintpreview.browser.PaintPreviewBaseService;
 
 import javax.annotation.Nonnull;
 
@@ -27,16 +28,22 @@
     }
 
     private CompositorListener mCompositorListener;
-    private long mNativePaintPreviewPlayerMediator;
+    private long mNativePlayerCompositorDelegate;
 
-    PlayerCompositorDelegateImpl(String url, @Nonnull CompositorListener compositorListener) {
+    PlayerCompositorDelegateImpl(PaintPreviewBaseService service, String url,
+            @Nonnull CompositorListener compositorListener) {
         mCompositorListener = compositorListener;
-        mNativePaintPreviewPlayerMediator =
-                PlayerCompositorDelegateImplJni.get().initialize(this, url);
+        if (service != null && service.getNativePaintPreviewBaseService() != 0) {
+            mNativePlayerCompositorDelegate = PlayerCompositorDelegateImplJni.get().initialize(
+                    this, service.getNativePaintPreviewBaseService(), url);
+        }
+        // TODO(crbug.com/1021590): Handle initialization errors when
+        // mNativePlayerCompositorDelegate == 0.
     }
 
     /**
      * Called by native when the Paint Preview compositor is ready.
+     *
      * @param rootFrameGuid The GUID for the root frame.
      * @param frameGuids Contains all frame GUIDs that are in this hierarchy.
      * @param frameContentSize Contains the content size for each frame. In native, this is called
@@ -68,31 +75,32 @@
     @Override
     public void requestBitmap(long frameGuid, Rect clipRect, float scaleFactor,
             Callback<Bitmap> bitmapCallback, Runnable errorCallback) {
-        if (mNativePaintPreviewPlayerMediator == 0) return;
+        if (mNativePlayerCompositorDelegate == 0) return;
 
-        PlayerCompositorDelegateImplJni.get().requestBitmap(mNativePaintPreviewPlayerMediator,
+        PlayerCompositorDelegateImplJni.get().requestBitmap(mNativePlayerCompositorDelegate,
                 frameGuid, bitmapCallback, errorCallback, scaleFactor, clipRect.left, clipRect.top,
                 clipRect.width(), clipRect.height());
     }
 
     @Override
     public void onClick(long frameGuid, Point point) {
-        if (mNativePaintPreviewPlayerMediator == 0) return;
+        if (mNativePlayerCompositorDelegate == 0) return;
 
         PlayerCompositorDelegateImplJni.get().onClick(
-                mNativePaintPreviewPlayerMediator, frameGuid, point.x, point.y);
+                mNativePlayerCompositorDelegate, frameGuid, point.x, point.y);
     }
 
     void destroy() {
-        if (mNativePaintPreviewPlayerMediator == 0) return;
+        if (mNativePlayerCompositorDelegate == 0) return;
 
-        PlayerCompositorDelegateImplJni.get().destroy(mNativePaintPreviewPlayerMediator);
-        mNativePaintPreviewPlayerMediator = 0;
+        PlayerCompositorDelegateImplJni.get().destroy(mNativePlayerCompositorDelegate);
+        mNativePlayerCompositorDelegate = 0;
     }
 
     @NativeMethods
     interface Natives {
-        long initialize(PlayerCompositorDelegateImpl caller, String url);
+        long initialize(PlayerCompositorDelegateImpl caller, long nativePaintPreviewBaseService,
+                String url);
         void destroy(long nativePlayerCompositorDelegateAndroid);
         void requestBitmap(long nativePlayerCompositorDelegateAndroid, long frameGuid,
                 Callback<Bitmap> bitmapCallback, Runnable errorCallback, float scaleFactor,
diff --git a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
index 5f1474f..7998eb14 100644
--- a/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
+++ b/components/paint_preview/player/android/java/src/org/chromium/components/paintpreview/player/PlayerManager.java
@@ -12,6 +12,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import org.chromium.components.paintpreview.browser.PaintPreviewBaseService;
 import org.chromium.components.paintpreview.player.frame.PlayerFrameCoordinator;
 
 import java.util.HashMap;
@@ -27,9 +28,9 @@
     private PlayerFrameCoordinator mRootFrameCoordinator;
     private FrameLayout mHostView;
 
-    public PlayerManager(Context context, String url) {
+    public PlayerManager(Context context, PaintPreviewBaseService service, String url) {
         mContext = context;
-        mDelegate = new PlayerCompositorDelegateImpl(url, this::onCompositorReady);
+        mDelegate = new PlayerCompositorDelegateImpl(service, url, this::onCompositorReady);
         mHostView = new FrameLayout(mContext);
     }
 
@@ -55,6 +56,7 @@
      * This method builds a hierarchy of {@link PaintPreviewFrame}s from primitive variables
      * that originate from native. Detailed explanation of the parameters can be found in
      * {@link PlayerCompositorDelegateImpl#onCompositorReady}.
+     *
      * @return The root {@link PaintPreviewFrame}
      */
     @VisibleForTesting
diff --git a/components/paint_preview/player/android/player_compositor_delegate_android.cc b/components/paint_preview/player/android/player_compositor_delegate_android.cc
index 4d6d75a..9cfedde 100644
--- a/components/paint_preview/player/android/player_compositor_delegate_android.cc
+++ b/components/paint_preview/player/android/player_compositor_delegate_android.cc
@@ -10,6 +10,7 @@
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/bind.h"
+#include "components/paint_preview/browser/paint_preview_base_service.h"
 #include "components/paint_preview/player/android/jni_headers/PlayerCompositorDelegateImpl_jni.h"
 #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
 #include "third_party/skia/include/core/SkBitmap.h"
@@ -26,23 +27,35 @@
 jlong JNI_PlayerCompositorDelegateImpl_Initialize(
     JNIEnv* env,
     const JavaParamRef<jobject>& j_object,
+    jlong paint_preview_service,
     const JavaParamRef<jstring>& j_string_url) {
-  PlayerCompositorDelegateAndroid* mediator =
-      new PlayerCompositorDelegateAndroid(env, j_object, j_string_url);
-  return reinterpret_cast<intptr_t>(mediator);
+  PlayerCompositorDelegateAndroid* delegate =
+      new PlayerCompositorDelegateAndroid(
+          env, j_object,
+          reinterpret_cast<PaintPreviewBaseService*>(paint_preview_service),
+          j_string_url);
+  return reinterpret_cast<intptr_t>(delegate);
 }
 
 PlayerCompositorDelegateAndroid::PlayerCompositorDelegateAndroid(
     JNIEnv* env,
     const JavaParamRef<jobject>& j_object,
+    PaintPreviewBaseService* paint_preview_service,
     const JavaParamRef<jstring>& j_string_url)
     : PlayerCompositorDelegate(
+          paint_preview_service,
           GURL(base::android::ConvertJavaStringToUTF16(env, j_string_url))) {
   java_ref_.Reset(env, j_object);
 }
 
 void PlayerCompositorDelegateAndroid::OnCompositorReady(
-    const mojom::PaintPreviewBeginCompositeResponse& composite_response) {
+    mojom::PaintPreviewCompositor::Status status,
+    mojom::PaintPreviewBeginCompositeResponsePtr composite_response) {
+  if (status != mojom::PaintPreviewCompositor::Status::kSuccess) {
+    // TODO(crbug.com/1021590): Handle initialization errors.
+    return;
+  }
+
   JNIEnv* env = base::android::AttachCurrentThread();
 
   // We use int64_t instead of uint64_t because (i) there is no equivalent
@@ -54,7 +67,7 @@
   std::vector<int64_t> subframe_ids;
   std::vector<int> subframe_rects;
 
-  CompositeResponseFramesToVectors(composite_response.frames, &all_guids,
+  CompositeResponseFramesToVectors(composite_response->frames, &all_guids,
                                    &scroll_extents, &subframe_count,
                                    &subframe_ids, &subframe_rects);
 
@@ -70,7 +83,7 @@
       base::android::ToJavaIntArray(env, subframe_rects);
 
   Java_PlayerCompositorDelegateImpl_onCompositorReady(
-      env, java_ref_, composite_response.root_frame_guid, j_all_guids,
+      env, java_ref_, composite_response->root_frame_guid, j_all_guids,
       j_scroll_extents, j_subframe_count, j_subframe_ids, j_subframe_rects);
 }
 
diff --git a/components/paint_preview/player/android/player_compositor_delegate_android.h b/components/paint_preview/player/android/player_compositor_delegate_android.h
index ecafc4d..937ed30 100644
--- a/components/paint_preview/player/android/player_compositor_delegate_android.h
+++ b/components/paint_preview/player/android/player_compositor_delegate_android.h
@@ -13,16 +13,19 @@
 class SkBitmap;
 
 namespace paint_preview {
+class PaintPreviewBaseService;
 
 class PlayerCompositorDelegateAndroid : public PlayerCompositorDelegate {
  public:
   PlayerCompositorDelegateAndroid(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& jobject,
+      PaintPreviewBaseService* paint_preview_service,
       const base::android::JavaParamRef<jstring>& j_string_url);
 
-  void OnCompositorReady(const mojom::PaintPreviewBeginCompositeResponse&
-                             composite_response) override;
+  void OnCompositorReady(
+      mojom::PaintPreviewCompositor::Status status,
+      mojom::PaintPreviewBeginCompositeResponsePtr composite_response) override;
 
   // Called from Java when there is a request for a new bitmap. When the bitmap
   // is ready, it will be passed to j_bitmap_callback. In case of any failure,
diff --git a/components/paint_preview/player/player_compositor_delegate.cc b/components/paint_preview/player/player_compositor_delegate.cc
index 8cd1e8ea..6b51979 100644
--- a/components/paint_preview/player/player_compositor_delegate.cc
+++ b/components/paint_preview/player/player_compositor_delegate.cc
@@ -4,36 +4,134 @@
 
 #include "components/paint_preview/player/player_compositor_delegate.h"
 
+#include <string>
+#include <utility>
 #include <vector>
 
 #include "base/callback.h"
+#include "base/containers/flat_map.h"
+#include "base/files/file_path.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/strings/string_piece.h"
+#include "components/paint_preview/browser/compositor_utils.h"
+#include "components/paint_preview/browser/paint_preview_base_service.h"
+#include "components/paint_preview/common/proto/paint_preview.pb.h"
+#include "components/paint_preview/public/paint_preview_compositor_client.h"
+#include "components/paint_preview/public/paint_preview_compositor_service.h"
 #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/geometry/rect.h"
 #include "url/gurl.h"
 
 namespace paint_preview {
+namespace {
 
-PlayerCompositorDelegate::PlayerCompositorDelegate(const GURL& url) {
-  // TODO(crbug.com/1019885): Use url to get proto and file map.
-  // TODO(crbug.com/1019885): Initialize the PaintPreviewCompositor class.
+base::flat_map<uint64_t, base::File> CreateFileMapFromProto(
+    const paint_preview::PaintPreviewProto& proto) {
+  std::vector<std::pair<uint64_t, base::File>> entries;
+  entries.reserve(1 + proto.subframes_size());
+  uint64_t root_frame_id = proto.root_frame().id();
+  base::BasicStringPiece<std::string> root_frame_file_path =
+      proto.root_frame().file_path();
+  entries.emplace_back(
+      root_frame_id, base::File(base::FilePath(root_frame_file_path),
+                                base::File::FLAG_OPEN | base::File::FLAG_READ));
+  for (int i = 0; i < proto.subframes_size(); ++i) {
+    uint64_t frame_id = proto.subframes(i).id();
+    base::BasicStringPiece<std::string> frame_file_path =
+        proto.subframes(i).file_path();
+    entries.emplace_back(
+        frame_id, base::File(base::FilePath(frame_file_path),
+                             base::File::FLAG_OPEN | base::File::FLAG_READ));
+  }
+  return base::flat_map<uint64_t, base::File>(std::move(entries));
+}
+
+base::Optional<base::ReadOnlySharedMemoryRegion> ToReadOnlySharedMemory(
+    const paint_preview::PaintPreviewProto& proto) {
+  auto region = base::WritableSharedMemoryRegion::Create(proto.ByteSizeLong());
+  if (!region.IsValid())
+    return base::nullopt;
+
+  auto mapping = region.Map();
+  if (!mapping.IsValid())
+    return base::nullopt;
+
+  proto.SerializeToArray(mapping.memory(), mapping.size());
+  return base::WritableSharedMemoryRegion::ConvertToReadOnly(std::move(region));
+}
+}  // namespace
+
+PlayerCompositorDelegate::PlayerCompositorDelegate(
+    PaintPreviewBaseService* paint_preview_service,
+    const GURL& url)
+    : paint_preview_service_(paint_preview_service) {
+  paint_preview_compositor_service_ =
+      paint_preview_service_->StartCompositorService(base::BindOnce(
+          &PlayerCompositorDelegate::OnCompositorServiceDisconnected,
+          weak_factory_.GetWeakPtr()));
+  paint_preview_compositor_client_ =
+      paint_preview_compositor_service_->CreateCompositor(
+          base::BindOnce(&PlayerCompositorDelegate::OnCompositorClientCreated,
+                         weak_factory_.GetWeakPtr(), url));
+  paint_preview_compositor_client_->SetDisconnectHandler(
+      base::BindOnce(&PlayerCompositorDelegate::OnCompositorClientDisconnected,
+                     weak_factory_.GetWeakPtr()));
+}
+
+void PlayerCompositorDelegate::OnCompositorServiceDisconnected() {
+  // TODO(crbug.com/1039699): Handle compositor service disconnect event.
+}
+
+void PlayerCompositorDelegate::OnCompositorClientCreated(const GURL& url) {
+  paint_preview_compositor_client_->SetRootFrameUrl(url);
+
+  base::Optional<PaintPreviewProto> proto =
+      paint_preview_service_->GetCapturedPaintPreviewProto(url);
+  if (!proto || !proto.value().IsInitialized()) {
+    // TODO(crbug.com/1021590): Handle initialization errors.
+    return;
+  }
+
+  // TODO(crbug.com/1034111): Investigate executing this in the background.
+  mojom::PaintPreviewBeginCompositeRequestPtr begin_composite_request =
+      mojom::PaintPreviewBeginCompositeRequest::New();
+  begin_composite_request->file_map = CreateFileMapFromProto(proto.value());
+  // TODO(crbug.com/1034111): Don't perform this on UI thread.
+  auto read_only_proto = ToReadOnlySharedMemory(proto.value());
+  if (!read_only_proto) {
+    // TODO(crbug.com/1021590): Handle initialization errors.
+    return;
+  }
+  begin_composite_request->proto = std::move(read_only_proto.value());
+  paint_preview_compositor_client_->BeginComposite(
+      std::move(begin_composite_request),
+      base::BindOnce(&PlayerCompositorDelegate::OnCompositorReady,
+                     weak_factory_.GetWeakPtr()));
   // TODO(crbug.com/1019883): Initialize the HitTester class.
 }
 
+void PlayerCompositorDelegate::OnCompositorClientDisconnected() {
+  // TODO(crbug.com/1039699): Handle compositor client disconnect event.
+}
+
 void PlayerCompositorDelegate::RequestBitmap(
     uint64_t frame_guid,
     const gfx::Rect& clip_rect,
     float scale_factor,
     base::OnceCallback<void(mojom::PaintPreviewCompositor::Status,
                             const SkBitmap&)> callback) {
-  if (!paint_preview_compositor_ || !paint_preview_compositor_.is_bound()) {
+  if (!paint_preview_compositor_client_) {
     std::move(callback).Run(
         mojom::PaintPreviewCompositor::Status::kCompositingFailure, SkBitmap());
     return;
   }
 
-  paint_preview_compositor_->BitmapForFrame(frame_guid, clip_rect, scale_factor,
-                                            std::move(callback));
+  paint_preview_compositor_client_->BitmapForFrame(
+      frame_guid, clip_rect, scale_factor, std::move(callback));
 }
 
 void PlayerCompositorDelegate::OnClick(uint64_t frame_guid, int x, int y) {
diff --git a/components/paint_preview/player/player_compositor_delegate.h b/components/paint_preview/player/player_compositor_delegate.h
index dc4cbf16..c92282d 100644
--- a/components/paint_preview/player/player_compositor_delegate.h
+++ b/components/paint_preview/player/player_compositor_delegate.h
@@ -6,23 +6,32 @@
 #define COMPONENTS_PAINT_PREVIEW_PLAYER_PLAYER_COMPOSITOR_DELEGATE_H_
 
 #include "base/callback_forward.h"
+#include "base/containers/flat_map.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "components/paint_preview/browser/paint_preview_base_service.h"
+#include "components/paint_preview/public/paint_preview_compositor_client.h"
+#include "components/paint_preview/public/paint_preview_compositor_service.h"
 #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
 
 namespace gfx {
 class Rect;
-}
+}  // namespace gfx
 
 class SkBitmap;
+class GURL;
 
 namespace paint_preview {
 
 class PlayerCompositorDelegate {
  public:
-  PlayerCompositorDelegate(const GURL& url);
+  PlayerCompositorDelegate(PaintPreviewBaseService* paint_preview_service,
+                           const GURL& url);
 
   virtual void OnCompositorReady(
-      const mojom::PaintPreviewBeginCompositeResponse& composite_response) = 0;
+      mojom::PaintPreviewCompositor::Status status,
+      mojom::PaintPreviewBeginCompositeResponsePtr composite_response) = 0;
 
   // Called when there is a request for a new bitmap. When the bitmap
   // is ready, it will be passed to callback.
@@ -40,8 +49,18 @@
   virtual ~PlayerCompositorDelegate();
 
  private:
-  // The current instance of PaintPreviewCompositor.
-  mojo::Remote<mojom::PaintPreviewCompositor> paint_preview_compositor_;
+  void OnCompositorServiceDisconnected();
+
+  void OnCompositorClientCreated(const GURL& url);
+  void OnCompositorClientDisconnected();
+
+  PaintPreviewBaseService* paint_preview_service_;
+  std::unique_ptr<PaintPreviewCompositorService>
+      paint_preview_compositor_service_;
+  std::unique_ptr<PaintPreviewCompositorClient>
+      paint_preview_compositor_client_;
+
+  base::WeakPtrFactory<PlayerCompositorDelegate> weak_factory_{this};
 
   PlayerCompositorDelegate(const PlayerCompositorDelegate&) = delete;
   PlayerCompositorDelegate& operator=(const PlayerCompositorDelegate&) = delete;
diff --git a/components/performance_manager/frame_node_source.h b/components/performance_manager/frame_node_source.h
index af1905a8..32b33c0e 100644
--- a/components/performance_manager/frame_node_source.h
+++ b/components/performance_manager/frame_node_source.h
@@ -7,6 +7,7 @@
 
 #include "base/callback.h"
 #include "base/observer_list_types.h"
+#include "content/public/browser/global_routing_id.h"
 
 namespace performance_manager {
 
@@ -22,20 +23,20 @@
   using OnbeforeFrameNodeRemovedCallback =
       base::OnceCallback<void(FrameNodeImpl*)>;
 
-  // Returns the frame node associated with |render_process_id| and |frame_id|.
-  // Returns null if no such node exists.
-  virtual FrameNodeImpl* GetFrameNode(int render_process_id, int frame_id) = 0;
+  // Returns the frame node associated with |render_process_host_id|. Returns
+  // null if no such node exists.
+  virtual FrameNodeImpl* GetFrameNode(
+      content::GlobalFrameRoutingId render_process_host_id) = 0;
 
   // Subscribes to receive removal notification for a frame node.
   virtual void SubscribeToFrameNode(
-      int render_process_id,
-      int frame_id,
+      content::GlobalFrameRoutingId render_process_host_id,
       OnbeforeFrameNodeRemovedCallback
           on_before_frame_node_removed_callback) = 0;
 
   // Unsubscribes to a frame node
-  virtual void UnsubscribeFromFrameNode(int render_process_id,
-                                        int frame_id) = 0;
+  virtual void UnsubscribeFromFrameNode(
+      content::GlobalFrameRoutingId render_process_host_id) = 0;
 };
 
 }  // namespace performance_manager
diff --git a/components/performance_manager/tab_helper_frame_node_source.cc b/components/performance_manager/tab_helper_frame_node_source.cc
index 049d581..631b71a4 100644
--- a/components/performance_manager/tab_helper_frame_node_source.cc
+++ b/components/performance_manager/tab_helper_frame_node_source.cc
@@ -19,12 +19,12 @@
   DCHECK(!performance_manager_tab_helper_observers_.IsObservingSources());
 }
 
-FrameNodeImpl* TabHelperFrameNodeSource::GetFrameNode(int render_process_id,
-                                                      int frame_id) {
+FrameNodeImpl* TabHelperFrameNodeSource::GetFrameNode(
+    content::GlobalFrameRoutingId render_process_host_id) {
   // Retrieve the client's RenderFrameHost and its associated
   // PerformanceManagerTabHelper.
   auto* render_frame_host =
-      content::RenderFrameHost::FromID(render_process_id, frame_id);
+      content::RenderFrameHost::FromID(render_process_host_id);
   if (!render_frame_host)
     return nullptr;
 
@@ -37,11 +37,10 @@
 }
 
 void TabHelperFrameNodeSource::SubscribeToFrameNode(
-    int render_process_id,
-    int frame_id,
+    content::GlobalFrameRoutingId render_process_host_id,
     OnbeforeFrameNodeRemovedCallback on_before_frame_node_removed_callback) {
   auto* render_frame_host =
-      content::RenderFrameHost::FromID(render_process_id, frame_id);
+      content::RenderFrameHost::FromID(render_process_host_id);
   DCHECK(render_frame_host);
 
   PerformanceManagerTabHelper* performance_manager_tab_helper =
@@ -70,10 +69,10 @@
   DCHECK(inserted);
 }
 
-void TabHelperFrameNodeSource::UnsubscribeFromFrameNode(int render_process_id,
-                                                        int frame_id) {
+void TabHelperFrameNodeSource::UnsubscribeFromFrameNode(
+    content::GlobalFrameRoutingId render_process_host_id) {
   auto* render_frame_host =
-      content::RenderFrameHost::FromID(render_process_id, frame_id);
+      content::RenderFrameHost::FromID(render_process_host_id);
   DCHECK(render_frame_host);
 
   PerformanceManagerTabHelper* performance_manager_tab_helper =
diff --git a/components/performance_manager/tab_helper_frame_node_source.h b/components/performance_manager/tab_helper_frame_node_source.h
index 63d739ca7..25737daa 100644
--- a/components/performance_manager/tab_helper_frame_node_source.h
+++ b/components/performance_manager/tab_helper_frame_node_source.h
@@ -24,12 +24,14 @@
   ~TabHelperFrameNodeSource() override;
 
   // FrameNodeSource:
-  FrameNodeImpl* GetFrameNode(int render_process_id, int frame_id) override;
-  void SubscribeToFrameNode(int render_process_id,
-                            int frame_id,
-                            OnbeforeFrameNodeRemovedCallback
-                                on_before_frame_node_removed_callback) override;
-  void UnsubscribeFromFrameNode(int render_process_id, int frame_id) override;
+  FrameNodeImpl* GetFrameNode(
+      content::GlobalFrameRoutingId render_process_host_id) override;
+  void SubscribeToFrameNode(
+      content::GlobalFrameRoutingId render_process_host_id,
+      OnbeforeFrameNodeRemovedCallback on_before_frame_node_removed_callback)
+      override;
+  void UnsubscribeFromFrameNode(
+      content::GlobalFrameRoutingId render_process_host_id) override;
 
   // PerformanceManagerTabHelper::Observer:
   void OnBeforeFrameNodeRemoved(
diff --git a/components/performance_manager/worker_watcher.cc b/components/performance_manager/worker_watcher.cc
index 2a93a66..7d843911 100644
--- a/components/performance_manager/worker_watcher.cc
+++ b/components/performance_manager/worker_watcher.cc
@@ -69,15 +69,14 @@
 void WorkerWatcher::TearDown() {
   // First clear client-child relations between frames and workers.
   for (auto& kv : frame_node_child_workers_) {
-    const FrameInfo& frame_info = kv.first;
+    const content::GlobalFrameRoutingId& render_frame_host_id = kv.first;
     base::flat_set<WorkerNodeImpl*>& child_workers = kv.second;
 
-    frame_node_source_->UnsubscribeFromFrameNode(frame_info.render_process_id,
-                                                 frame_info.frame_id);
+    frame_node_source_->UnsubscribeFromFrameNode(render_frame_host_id);
 
     // Disconnect all child workers from |frame_node|.
-    FrameNodeImpl* frame_node = frame_node_source_->GetFrameNode(
-        frame_info.render_process_id, frame_info.frame_id);
+    FrameNodeImpl* frame_node =
+        frame_node_source_->GetFrameNode(render_frame_host_id);
     DCHECK(frame_node);
     DCHECK(!child_workers.empty());
     PerformanceManagerImpl::CallOnGraphImpl(
@@ -125,11 +124,11 @@
   shared_worker_nodes_.erase(it);
 }
 
-void WorkerWatcher::OnClientAdded(const content::SharedWorkerInstance& instance,
-                                  int client_process_id,
-                                  int frame_id) {
+void WorkerWatcher::OnClientAdded(
+    const content::SharedWorkerInstance& instance,
+    content::GlobalFrameRoutingId render_frame_host_id) {
   FrameNodeImpl* frame_node =
-      frame_node_source_->GetFrameNode(client_process_id, frame_id);
+      frame_node_source_->GetFrameNode(render_frame_host_id);
   DCHECK(frame_node);
 
   // Connect the nodes in the PM graph.
@@ -139,22 +138,20 @@
       base::BindOnce(&AddWorkerToFrameNode, frame_node, worker_node));
 
   // Keep track of the shared workers that this frame is a client to.
-  if (AddChildWorker(client_process_id, frame_id, worker_node)) {
+  if (AddChildWorker(render_frame_host_id, worker_node)) {
     frame_node_source_->SubscribeToFrameNode(
-        client_process_id, frame_id,
+        render_frame_host_id,
         base::BindOnce(&WorkerWatcher::OnBeforeFrameNodeRemoved,
-                       base::Unretained(this), client_process_id, frame_id));
+                       base::Unretained(this), render_frame_host_id));
   }
 }
 
 void WorkerWatcher::OnClientRemoved(
     const content::SharedWorkerInstance& instance,
-    int client_process_id,
-    int frame_id) {
+    content::GlobalFrameRoutingId render_frame_host_id) {
   WorkerNodeImpl* worker_node = GetSharedWorkerNode(instance);
-
   FrameNodeImpl* frame_node =
-      frame_node_source_->GetFrameNode(client_process_id, frame_id);
+      frame_node_source_->GetFrameNode(render_frame_host_id);
 
   // It's possible that the frame was destroyed before receiving the
   // OnClientRemoved() for all of its child shared worker. Nothing to do in
@@ -184,15 +181,14 @@
 
   // Remove |worker_node| from the set of workers that this frame is a client
   // of.
-  if (RemoveChildWorker(client_process_id, frame_id, worker_node))
-    frame_node_source_->UnsubscribeFromFrameNode(client_process_id, frame_id);
+  if (RemoveChildWorker(render_frame_host_id, worker_node))
+    frame_node_source_->UnsubscribeFromFrameNode(render_frame_host_id);
 }
 
-void WorkerWatcher::OnBeforeFrameNodeRemoved(int render_process_id,
-                                             int frame_id,
-                                             FrameNodeImpl* frame_node) {
-  auto it =
-      frame_node_child_workers_.find(FrameInfo{render_process_id, frame_id});
+void WorkerWatcher::OnBeforeFrameNodeRemoved(
+    content::GlobalFrameRoutingId render_frame_host_id,
+    FrameNodeImpl* frame_node) {
+  auto it = frame_node_child_workers_.find(render_frame_host_id);
   DCHECK(it != frame_node_child_workers_.end());
 
   // Clean up all child workers of this frame node.
@@ -216,11 +212,11 @@
 #endif  // DCHECK_IS_ON()
 }
 
-bool WorkerWatcher::AddChildWorker(int render_process_id,
-                                   int frame_id,
-                                   WorkerNodeImpl* child_worker_node) {
+bool WorkerWatcher::AddChildWorker(
+    content::GlobalFrameRoutingId render_frame_host_id,
+    WorkerNodeImpl* child_worker_node) {
   auto insertion_result =
-      frame_node_child_workers_.insert({{render_process_id, frame_id}, {}});
+      frame_node_child_workers_.insert({render_frame_host_id, {}});
 
   auto& child_workers = insertion_result.first->second;
   bool inserted = child_workers.insert(child_worker_node).second;
@@ -229,11 +225,10 @@
   return insertion_result.second;
 }
 
-bool WorkerWatcher::RemoveChildWorker(int render_process_id,
-                                      int frame_id,
-                                      WorkerNodeImpl* child_worker_node) {
-  auto it =
-      frame_node_child_workers_.find(FrameInfo{render_process_id, frame_id});
+bool WorkerWatcher::RemoveChildWorker(
+    content::GlobalFrameRoutingId render_frame_host_id,
+    WorkerNodeImpl* child_worker_node) {
+  auto it = frame_node_child_workers_.find(render_frame_host_id);
   DCHECK(it != frame_node_child_workers_.end());
   auto& child_workers = it->second;
 
@@ -257,10 +252,4 @@
   return it->second.get();
 }
 
-bool operator<(const WorkerWatcher::FrameInfo& lhs,
-               const WorkerWatcher::FrameInfo& rhs) {
-  return std::tie(lhs.render_process_id, lhs.frame_id) <
-         std::tie(rhs.render_process_id, rhs.frame_id);
-}
-
 }  // namespace performance_manager
diff --git a/components/performance_manager/worker_watcher.h b/components/performance_manager/worker_watcher.h
index 98f733e..157de6e2 100644
--- a/components/performance_manager/worker_watcher.h
+++ b/components/performance_manager/worker_watcher.h
@@ -13,6 +13,7 @@
 #include "base/logging.h"
 #include "base/macros.h"
 #include "base/scoped_observer.h"
+#include "content/public/browser/global_routing_id.h"
 #include "content/public/browser/shared_worker_service.h"
 
 namespace content {
@@ -49,25 +50,23 @@
                        const base::UnguessableToken& dev_tools_token) override;
   void OnBeforeWorkerTerminated(
       const content::SharedWorkerInstance& instance) override;
-  void OnClientAdded(const content::SharedWorkerInstance& instance,
-                     int client_process_id,
-                     int frame_id) override;
-  void OnClientRemoved(const content::SharedWorkerInstance& instance,
-                       int client_process_id,
-                       int frame_id) override;
+  void OnClientAdded(
+      const content::SharedWorkerInstance& instance,
+      content::GlobalFrameRoutingId render_frame_host_id) override;
+  void OnClientRemoved(
+      const content::SharedWorkerInstance& instance,
+      content::GlobalFrameRoutingId render_frame_host_id) override;
 
  private:
   friend class WorkerWatcherTest;
 
-  void OnBeforeFrameNodeRemoved(int render_process_id,
-                                int frame_id,
-                                FrameNodeImpl* frame_node);
+  void OnBeforeFrameNodeRemoved(
+      content::GlobalFrameRoutingId render_frame_host_id,
+      FrameNodeImpl* frame_node);
 
-  bool AddChildWorker(int render_process_id,
-                      int frame_id,
+  bool AddChildWorker(content::GlobalFrameRoutingId render_frame_host_id,
                       WorkerNodeImpl* child_worker_node);
-  bool RemoveChildWorker(int render_process_id,
-                         int frame_id,
+  bool RemoveChildWorker(content::GlobalFrameRoutingId render_frame_host_id,
                          WorkerNodeImpl* child_worker_node);
 
   // Helper function to retrieve an existing shared worker node.
@@ -97,15 +96,7 @@
   // is used when a frame is torn down before the OnBeforeWorkerTerminated() is
   // received, to ensure the deletion of the worker nodes in the right order
   // (workers before frames).
-  struct FrameInfo {
-    int render_process_id;
-    int frame_id;
-  };
-
-  // Comparison operator to allow using FrameInfo as a key.
-  friend bool operator<(const FrameInfo& lhs, const FrameInfo& rhs);
-
-  base::flat_map<FrameInfo, base::flat_set<WorkerNodeImpl*>>
+  base::flat_map<content::GlobalFrameRoutingId, base::flat_set<WorkerNodeImpl*>>
       frame_node_child_workers_;
 
 #if DCHECK_IS_ON()
diff --git a/components/performance_manager/worker_watcher_unittest.cc b/components/performance_manager/worker_watcher_unittest.cc
index 840f15d..0426bd4 100644
--- a/components/performance_manager/worker_watcher_unittest.cc
+++ b/components/performance_manager/worker_watcher_unittest.cc
@@ -74,14 +74,14 @@
   void StopSharedWorker(const content::SharedWorkerInstance& instance);
 
   // Adds a new frame client to an existing worker.
-  void AddWorkerClient(const content::SharedWorkerInstance& instance,
-                       int client_process_id,
-                       int frame_id);
+  void AddWorkerClient(
+      const content::SharedWorkerInstance& instance,
+      content::GlobalFrameRoutingId client_render_frame_host_id);
 
   // Removes an existing frame client from a worker.
-  void RemoveWorkerClient(const content::SharedWorkerInstance& instance,
-                          int client_process_id,
-                          int frame_id);
+  void RemoveWorkerClient(
+      const content::SharedWorkerInstance& instance,
+      content::GlobalFrameRoutingId client_render_frame_host_id);
 
  private:
   base::ObserverList<Observer> observer_list_;
@@ -91,7 +91,7 @@
 
   // Contains the set of clients for each running workers.
   base::flat_map<content::SharedWorkerInstance,
-                 base::flat_set<std::pair<int, int>>>
+                 base::flat_set<content::GlobalFrameRoutingId>>
       shared_worker_client_frames_;
 
   DISALLOW_COPY_AND_ASSIGN(TestSharedWorkerService);
@@ -157,36 +157,33 @@
 
 void TestSharedWorkerService::AddWorkerClient(
     const content::SharedWorkerInstance& instance,
-    int client_process_id,
-    int frame_id) {
+    content::GlobalFrameRoutingId client_render_frame_host_id) {
   // Add the frame to the set of clients for this worker.
   auto it = shared_worker_client_frames_.find(instance);
   DCHECK(it != shared_worker_client_frames_.end());
 
-  base::flat_set<std::pair<int, int>>& client_frames = it->second;
-  bool inserted = client_frames.insert({client_process_id, frame_id}).second;
+  base::flat_set<content::GlobalFrameRoutingId>& client_frames = it->second;
+  bool inserted = client_frames.insert(client_render_frame_host_id).second;
   DCHECK(inserted);
 
   // Then notify observers.
   for (auto& observer : observer_list_)
-    observer.OnClientAdded(instance, client_process_id, frame_id);
+    observer.OnClientAdded(instance, client_render_frame_host_id);
 }
 
 void TestSharedWorkerService::RemoveWorkerClient(
     const content::SharedWorkerInstance& instance,
-    int client_process_id,
-    int frame_id) {
+    content::GlobalFrameRoutingId client_render_frame_host_id) {
   // Notify observers.
   for (auto& observer : observer_list_)
-    observer.OnClientRemoved(instance, client_process_id, frame_id);
+    observer.OnClientRemoved(instance, client_render_frame_host_id);
 
   // Then remove the frame from the set of clients of this worker.
   auto it = shared_worker_client_frames_.find(instance);
   DCHECK(it != shared_worker_client_frames_.end());
 
-  base::flat_set<std::pair<int, int>>& client_frames = it->second;
-  size_t removed =
-      client_frames.erase(std::make_pair(client_process_id, frame_id));
+  base::flat_set<content::GlobalFrameRoutingId>& client_frames = it->second;
+  size_t removed = client_frames.erase(client_render_frame_host_id);
   DCHECK_EQ(removed, 1u);
 }
 
@@ -254,18 +251,20 @@
   ~TestFrameNodeSource() override;
 
   // FrameNodeSource:
-  FrameNodeImpl* GetFrameNode(int render_process_id, int frame_id) override;
-  void SubscribeToFrameNode(int render_process_id,
-                            int frame_id,
+  FrameNodeImpl* GetFrameNode(
+      content::GlobalFrameRoutingId render_frame_host_id) override;
+  void SubscribeToFrameNode(content::GlobalFrameRoutingId render_frame_host_id,
                             OnbeforeFrameNodeRemovedCallback
                                 on_before_frame_node_removed_callback) override;
-  void UnsubscribeFromFrameNode(int render_process_id, int frame_id) override;
+  void UnsubscribeFromFrameNode(
+      content::GlobalFrameRoutingId render_frame_host_id) override;
 
-  // Creates a frame node and returns its generated frame id.
-  int CreateFrameNode(int render_process_id, ProcessNodeImpl* process_node);
+  // Creates a frame node and returns its generated render frame host id.
+  content::GlobalFrameRoutingId CreateFrameNode(int render_process_id,
+                                                ProcessNodeImpl* process_node);
 
   // Deletes an existing frame node and notify subscribers.
-  void DeleteFrameNode(int render_process_id, int frame_id);
+  void DeleteFrameNode(content::GlobalFrameRoutingId render_frame_host_id);
 
  private:
   // Helper function that invokes the OnBeforeFrameNodeRemovedCallback
@@ -275,9 +274,8 @@
   // The page node that hosts all frames.
   std::unique_ptr<PageNodeImpl> page_node_;
 
-  // Maps each frame's render process id and frame id with their associated
-  // frame node.
-  base::flat_map<std::pair<int, int>, std::unique_ptr<FrameNodeImpl>>
+  // Maps each frame's render frame host id with their associated frame node.
+  base::flat_map<content::GlobalFrameRoutingId, std::unique_ptr<FrameNodeImpl>>
       frame_node_map_;
 
   // Maps each observed frame node to their callback.
@@ -305,16 +303,15 @@
   frame_node_map_.clear();
 }
 
-FrameNodeImpl* TestFrameNodeSource::GetFrameNode(int render_process_id,
-                                                 int frame_id) {
-  auto it = frame_node_map_.find(std::make_pair(render_process_id, frame_id));
+FrameNodeImpl* TestFrameNodeSource::GetFrameNode(
+    content::GlobalFrameRoutingId render_frame_host_id) {
+  auto it = frame_node_map_.find(render_frame_host_id);
   return it != frame_node_map_.end() ? it->second.get() : nullptr;
 }
 void TestFrameNodeSource::SubscribeToFrameNode(
-    int render_process_id,
-    int frame_id,
+    content::GlobalFrameRoutingId render_frame_host_id,
     OnbeforeFrameNodeRemovedCallback on_before_frame_node_removed_callback) {
-  FrameNodeImpl* frame_node = GetFrameNode(render_process_id, frame_id);
+  FrameNodeImpl* frame_node = GetFrameNode(render_frame_host_id);
   DCHECK(frame_node);
 
   bool inserted =
@@ -325,35 +322,36 @@
   DCHECK(inserted);
 }
 
-void TestFrameNodeSource::UnsubscribeFromFrameNode(int render_process_id,
-                                                   int frame_id) {
-  FrameNodeImpl* frame_node = GetFrameNode(render_process_id, frame_id);
+void TestFrameNodeSource::UnsubscribeFromFrameNode(
+    content::GlobalFrameRoutingId render_frame_host_id) {
+  FrameNodeImpl* frame_node = GetFrameNode(render_frame_host_id);
   DCHECK(frame_node);
 
   size_t removed = frame_node_callbacks_.erase(frame_node);
   DCHECK_EQ(removed, 1u);
 }
 
-// Creates a frame node and returns its frame id.
-int TestFrameNodeSource::CreateFrameNode(int render_process_id,
-                                         ProcessNodeImpl* process_node) {
+content::GlobalFrameRoutingId TestFrameNodeSource::CreateFrameNode(
+    int render_process_id,
+    ProcessNodeImpl* process_node) {
   int frame_id = GenerateNextId();
+  content::GlobalFrameRoutingId render_frame_host_id(render_process_id,
+                                                     frame_id);
   auto frame_node = PerformanceManagerImpl::GetInstance()->CreateFrameNode(
       process_node, page_node_.get(), nullptr, 0, frame_id,
       base::UnguessableToken::Null(), 0, 0);
 
-  std::pair<int, int> frame_info(render_process_id, frame_id);
-  bool inserted = frame_node_map_
-                      .insert({std::make_pair(render_process_id, frame_id),
-                               std::move(frame_node)})
-                      .second;
+  bool inserted =
+      frame_node_map_.insert({render_frame_host_id, std::move(frame_node)})
+          .second;
   DCHECK(inserted);
 
-  return frame_id;
+  return render_frame_host_id;
 }
 
-void TestFrameNodeSource::DeleteFrameNode(int render_process_id, int frame_id) {
-  auto it = frame_node_map_.find(std::make_pair(render_process_id, frame_id));
+void TestFrameNodeSource::DeleteFrameNode(
+    content::GlobalFrameRoutingId render_frame_host_id) {
+  auto it = frame_node_map_.find(render_frame_host_id);
   DCHECK(it != frame_node_map_.end());
 
   FrameNodeImpl* frame_node = it->second.get();
@@ -472,9 +470,10 @@
   int render_process_id = process_node_source()->CreateProcessNode();
 
   // Create the frame node.
-  int frame_id = frame_node_source()->CreateFrameNode(
-      render_process_id,
-      process_node_source()->GetProcessNode(render_process_id));
+  content::GlobalFrameRoutingId render_frame_host_id =
+      frame_node_source()->CreateFrameNode(
+          render_process_id,
+          process_node_source()->GetProcessNode(render_process_id));
 
   // Create the worker.
   content::SharedWorkerInstance shared_worker_instance =
@@ -482,14 +481,14 @@
 
   // Connect the frame to the worker.
   shared_worker_service()->AddWorkerClient(shared_worker_instance,
-                                           render_process_id, frame_id);
+                                           render_frame_host_id);
 
   // Check expectations on the graph.
   CallOnGraphAndWait(base::BindLambdaForTesting(
       [process_node = process_node_source()->GetProcessNode(render_process_id),
        worker_node = GetSharedWorkerNode(shared_worker_instance),
        client_frame_node = frame_node_source()->GetFrameNode(
-           render_process_id, frame_id)](GraphImpl* graph) {
+           render_frame_host_id)](GraphImpl* graph) {
         EXPECT_TRUE(graph->NodeInGraph(worker_node));
         EXPECT_EQ(worker_node->worker_type(), WorkerNode::WorkerType::kShared);
         EXPECT_EQ(worker_node->process_node(), process_node);
@@ -498,16 +497,17 @@
 
   // Disconnect and clean up the worker.
   shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
-                                              render_process_id, frame_id);
+                                              render_frame_host_id);
   shared_worker_service()->StopSharedWorker(shared_worker_instance);
 }
 
 TEST_F(WorkerWatcherTest, CrossProcess) {
   // Create the frame node.
   int frame_process_id = process_node_source()->CreateProcessNode();
-  int frame_id = frame_node_source()->CreateFrameNode(
-      frame_process_id,
-      process_node_source()->GetProcessNode(frame_process_id));
+  content::GlobalFrameRoutingId render_frame_host_id =
+      frame_node_source()->CreateFrameNode(
+          frame_process_id,
+          process_node_source()->GetProcessNode(frame_process_id));
 
   // Create the worker in a different process.
   int worker_process_id = process_node_source()->CreateProcessNode();
@@ -516,7 +516,7 @@
 
   // Connect the frame to the worker.
   shared_worker_service()->AddWorkerClient(shared_worker_instance,
-                                           frame_process_id, frame_id);
+                                           render_frame_host_id);
 
   // Check expectations on the graph.
   CallOnGraphAndWait(base::BindLambdaForTesting(
@@ -526,7 +526,7 @@
        client_process_node =
            process_node_source()->GetProcessNode(frame_process_id),
        client_frame_node = frame_node_source()->GetFrameNode(
-           frame_process_id, frame_id)](GraphImpl* graph) {
+           render_frame_host_id)](GraphImpl* graph) {
         EXPECT_TRUE(graph->NodeInGraph(worker_node));
         EXPECT_EQ(worker_node->worker_type(), WorkerNode::WorkerType::kShared);
         EXPECT_EQ(worker_node->process_node(), worker_process_node);
@@ -535,7 +535,7 @@
 
   // Disconnect and clean up the worker.
   shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
-                                              frame_process_id, frame_id);
+                                              render_frame_host_id);
   shared_worker_service()->StopSharedWorker(shared_worker_instance);
 }
 
@@ -547,25 +547,27 @@
       shared_worker_service()->StartSharedWorker(render_process_id);
 
   // Create 2 client frame nodes and connect them to the worker.
-  int frame_id_1 = frame_node_source()->CreateFrameNode(
-      render_process_id,
-      process_node_source()->GetProcessNode(render_process_id));
+  content::GlobalFrameRoutingId render_frame_host_id_1 =
+      frame_node_source()->CreateFrameNode(
+          render_process_id,
+          process_node_source()->GetProcessNode(render_process_id));
   shared_worker_service()->AddWorkerClient(shared_worker_instance,
-                                           render_process_id, frame_id_1);
+                                           render_frame_host_id_1);
 
-  int frame_id_2 = frame_node_source()->CreateFrameNode(
-      render_process_id,
-      process_node_source()->GetProcessNode(render_process_id));
+  content::GlobalFrameRoutingId render_frame_host_id_2 =
+      frame_node_source()->CreateFrameNode(
+          render_process_id,
+          process_node_source()->GetProcessNode(render_process_id));
   shared_worker_service()->AddWorkerClient(shared_worker_instance,
-                                           render_process_id, frame_id_2);
+                                           render_frame_host_id_2);
 
   // Check expectations on the graph.
   CallOnGraphAndWait(base::BindLambdaForTesting(
       [worker_node = GetSharedWorkerNode(shared_worker_instance),
        client_frame_node_1 =
-           frame_node_source()->GetFrameNode(render_process_id, frame_id_1),
+           frame_node_source()->GetFrameNode(render_frame_host_id_1),
        client_frame_node_2 = frame_node_source()->GetFrameNode(
-           render_process_id, frame_id_2)](GraphImpl* graph) {
+           render_frame_host_id_2)](GraphImpl* graph) {
         EXPECT_TRUE(graph->NodeInGraph(worker_node));
         EXPECT_EQ(worker_node->worker_type(), WorkerNode::WorkerType::kShared);
 
@@ -578,9 +580,9 @@
 
   // Disconnect and clean up the worker.
   shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
-                                              render_process_id, frame_id_1);
+                                              render_frame_host_id_1);
   shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
-                                              render_process_id, frame_id_2);
+                                              render_frame_host_id_2);
   shared_worker_service()->StopSharedWorker(shared_worker_instance);
 }
 
@@ -588,27 +590,28 @@
   int render_process_id = process_node_source()->CreateProcessNode();
 
   // Create the frame node.
-  int frame_id = frame_node_source()->CreateFrameNode(
-      render_process_id,
-      process_node_source()->GetProcessNode(render_process_id));
+  content::GlobalFrameRoutingId render_frame_host_id =
+      frame_node_source()->CreateFrameNode(
+          render_process_id,
+          process_node_source()->GetProcessNode(render_process_id));
 
   // Create the 2 workers and connect them to the frame.
   content::SharedWorkerInstance shared_worker_instance_1 =
       shared_worker_service()->StartSharedWorker(render_process_id);
   shared_worker_service()->AddWorkerClient(shared_worker_instance_1,
-                                           render_process_id, frame_id);
+                                           render_frame_host_id);
 
   content::SharedWorkerInstance shared_worker_instance_2 =
       shared_worker_service()->StartSharedWorker(render_process_id);
   shared_worker_service()->AddWorkerClient(shared_worker_instance_2,
-                                           render_process_id, frame_id);
+                                           render_frame_host_id);
 
   // Check expectations on the graph.
   CallOnGraphAndWait(base::BindLambdaForTesting(
       [worker_node_1 = GetSharedWorkerNode(shared_worker_instance_1),
        worker_node_2 = GetSharedWorkerNode(shared_worker_instance_2),
        client_frame_node = frame_node_source()->GetFrameNode(
-           render_process_id, frame_id)](GraphImpl* graph) {
+           render_frame_host_id)](GraphImpl* graph) {
         // Check worker 1.
         EXPECT_TRUE(graph->NodeInGraph(worker_node_1));
         EXPECT_EQ(worker_node_1->worker_type(),
@@ -624,11 +627,11 @@
 
   // Disconnect and clean up the workers.
   shared_worker_service()->RemoveWorkerClient(shared_worker_instance_1,
-                                              render_process_id, frame_id);
+                                              render_frame_host_id);
   shared_worker_service()->StopSharedWorker(shared_worker_instance_1);
 
   shared_worker_service()->RemoveWorkerClient(shared_worker_instance_2,
-                                              render_process_id, frame_id);
+                                              render_frame_host_id);
   shared_worker_service()->StopSharedWorker(shared_worker_instance_2);
 }
 
@@ -636,9 +639,10 @@
   int render_process_id = process_node_source()->CreateProcessNode();
 
   // Create the frame node.
-  int frame_id = frame_node_source()->CreateFrameNode(
-      render_process_id,
-      process_node_source()->GetProcessNode(render_process_id));
+  content::GlobalFrameRoutingId render_frame_host_id =
+      frame_node_source()->CreateFrameNode(
+          render_process_id,
+          process_node_source()->GetProcessNode(render_process_id));
 
   // Create a shared worker.
   content::SharedWorkerInstance shared_worker_instance =
@@ -646,18 +650,18 @@
 
   // Connect the frame to the worker.
   shared_worker_service()->AddWorkerClient(shared_worker_instance,
-                                           render_process_id, frame_id);
+                                           render_frame_host_id);
 
   // Check that everything is wired up correctly.
   CallOnGraphAndWait(base::BindLambdaForTesting(
       [shared_worker_node = GetSharedWorkerNode(shared_worker_instance),
        client_frame_node = frame_node_source()->GetFrameNode(
-           render_process_id, frame_id)](GraphImpl* graph) {
+           render_frame_host_id)](GraphImpl* graph) {
         EXPECT_TRUE(graph->NodeInGraph(shared_worker_node));
         EXPECT_TRUE(IsWorkerClient(shared_worker_node, client_frame_node));
       }));
 
-  frame_node_source()->DeleteFrameNode(render_process_id, frame_id);
+  frame_node_source()->DeleteFrameNode(render_frame_host_id);
 
   // Check that the worker is no longer connected to the deleted frame.
   CallOnGraphAndWait(base::BindLambdaForTesting(
@@ -669,7 +673,7 @@
 
   // The watcher is still expecting a worker removed notification.
   shared_worker_service()->RemoveWorkerClient(shared_worker_instance,
-                                              render_process_id, frame_id);
+                                              render_frame_host_id);
   shared_worker_service()->StopSharedWorker(shared_worker_instance);
 }
 
diff --git a/components/startup_metric_utils/browser/startup_metric_utils.cc b/components/startup_metric_utils/browser/startup_metric_utils.cc
index 47be8cd..d470948 100644
--- a/components/startup_metric_utils/browser/startup_metric_utils.cc
+++ b/components/startup_metric_utils/browser/startup_metric_utils.cc
@@ -78,19 +78,22 @@
 #if defined(OS_WIN)
 
 // These values are taken from the Startup.BrowserMessageLoopStartHardFaultCount
-// histogram. If the cold start histogram starts looking strongly bimodal it may
-// be because the binary/resource sizes have grown significantly larger than
-// when these values were set. In this case the new values need to be chosen
-// from the original histogram.
+// histogram. The latest revision landed on <5 and >3500 for a good split
+// of warm/cold. In between being considered "lukewarm". Full analysis @
+// https://docs.google.com/document/d/1haXFN1cQ6XE-NfhKgww-rOP-Wi-gK6AczP3gT4M5_kI
+// These values should be reconsidered if either .WarmStartup or .ColdStartup
+// distributions of a suffixed histogram becomes unexplainably bimodal.
 //
 // Maximum number of hard faults tolerated for a startup to be classified as a
-// warm start. Set at roughly the 40th percentile of the HardFaultCount
-// histogram.
+// warm start.
 constexpr uint32_t kWarmStartHardFaultCountThreshold = 5;
-// Minimum number of hard faults expected for a startup to be classified as a
-// cold start. Set at roughly the 60th percentile of the HardFaultCount
-// histogram.
-constexpr uint32_t kColdStartHardFaultCountThreshold = 1200;
+// Minimum number of hard faults (of 4KB pages) expected for a startup to be
+// classified as a cold start. The right value for this seems to be between 10%
+// and 15% of chrome.dll's size (from anecdata of the two times we did this
+// analysis... it was 1200 in M47 back when chrome.dll was 35MB (32-bit and
+// split from chrome_child.dll) and was made 3500 in M81 when chrome.dll was
+// 126MB).
+constexpr uint32_t kColdStartHardFaultCountThreshold = 3500;
 
 // The struct used to return system process information via the NT internal
 // QuerySystemInformation call. This is partially documented at
@@ -210,7 +213,7 @@
         type(basename ".WarmStartup", value);                                 \
         break;                                                                \
       case LUKEWARM_STARTUP_TEMPERATURE:                                      \
-        type(basename ".LukewarmStartup", value);                             \
+        /* No suffix emitted for lukewarm startups. */                        \
         break;                                                                \
       case UNDETERMINED_STARTUP_TEMPERATURE:                                  \
         break;                                                                \
@@ -523,8 +526,7 @@
       now - render_process_host_init_time);
 }
 
-void RecordFirstWebContentsMainNavigationStart(base::TimeTicks ticks,
-                                               WebContentsWorkload workload) {
+void RecordFirstWebContentsMainNavigationStart(base::TimeTicks ticks) {
   static bool is_first_call = true;
   if (!is_first_call || ticks.is_null())
     return;
@@ -536,21 +538,6 @@
       UMA_HISTOGRAM_LONG_TIMES_100,
       "Startup.FirstWebContents.MainNavigationStart", g_process_creation_ticks,
       ticks);
-
-  // Log extra information about this startup's workload. Only added to this
-  // histogram as this extra suffix can help making it less noisy but isn't
-  // worth tripling the number of startup histograms either.
-  if (workload == WebContentsWorkload::SINGLE_TAB) {
-    UMA_HISTOGRAM_WITH_TEMPERATURE(
-        UMA_HISTOGRAM_LONG_TIMES_100,
-        "Startup.FirstWebContents.MainNavigationStart.SingleTab",
-        ticks - g_process_creation_ticks);
-  } else {
-    UMA_HISTOGRAM_WITH_TEMPERATURE(
-        UMA_HISTOGRAM_LONG_TIMES_100,
-        "Startup.FirstWebContents.MainNavigationStart.MultiTabs",
-        ticks - g_process_creation_ticks);
-  }
 }
 
 void RecordFirstWebContentsMainNavigationFinished(base::TimeTicks ticks) {
diff --git a/components/startup_metric_utils/browser/startup_metric_utils.h b/components/startup_metric_utils/browser/startup_metric_utils.h
index e158626..c59f7713 100644
--- a/components/startup_metric_utils/browser/startup_metric_utils.h
+++ b/components/startup_metric_utils/browser/startup_metric_utils.h
@@ -15,15 +15,6 @@
 
 namespace startup_metric_utils {
 
-// Identifies the workload of profiled WebContents, used to refine startup
-// metrics.
-enum class WebContentsWorkload {
-  // Only loading a single tab.
-  SINGLE_TAB,
-  // Loading multiple tabs (of which the profiled WebContents is foreground).
-  MULTI_TABS,
-};
-
 // Returns true when browser UI was not launched normally: some other UI was
 // shown first or browser was launched in background mode.
 bool WasMainWindowStartupInterrupted();
@@ -77,9 +68,8 @@
     base::TimeTicks render_process_host_init_time);
 
 // Call this with the time when the first web contents began navigating its main
-// frame. Adds a suffix to its metrics according to |workload|.
-void RecordFirstWebContentsMainNavigationStart(base::TimeTicks ticks,
-                                               WebContentsWorkload workload);
+// frame.
+void RecordFirstWebContentsMainNavigationStart(base::TimeTicks ticks);
 
 // Call this with the time when the first web contents successfully committed
 // its navigation for the main frame.
diff --git a/components/sync/driver/sync_driver_switches.cc b/components/sync/driver/sync_driver_switches.cc
index ddff8b9..999384a 100644
--- a/components/sync/driver/sync_driver_switches.cc
+++ b/components/sync/driver/sync_driver_switches.cc
@@ -55,7 +55,7 @@
 
 // Enable USS implementation of Passwords datatype.
 const base::Feature kSyncUSSPasswords{"SyncUSSPasswords",
-                                      base::FEATURE_DISABLED_BY_DEFAULT};
+                                      base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable USS implementation of Nigori datatype.
 const base::Feature kSyncUSSNigori{"SyncUSSNigori",
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 d76919f..5f69b5d 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
@@ -222,6 +222,9 @@
     std::unique_ptr<const SkSurface::AsyncReadResult> async_result) {
   std::unique_ptr<ReadPixelsContext> context(
       static_cast<ReadPixelsContext*>(c));
+  if (context->impl_on_gpu)
+    context->impl_on_gpu->ReadbackDone();
+
   if (!async_result) {
     // This will automatically send an empty result.
     return;
@@ -243,6 +246,9 @@
     std::unique_ptr<const SkSurface::AsyncReadResult> async_result) {
   std::unique_ptr<ReadPixelsContext> context(
       static_cast<ReadPixelsContext*>(c));
+  if (context->impl_on_gpu)
+    context->impl_on_gpu->ReadbackDone();
+
   if (!async_result) {
     // This will automatically send an empty result.
     return;
diff --git a/components/viz/service/gl/gpu_service_impl.cc b/components/viz/service/gl/gpu_service_impl.cc
index 62860ea..5a07336 100644
--- a/components/viz/service/gl/gpu_service_impl.cc
+++ b/components/viz/service/gl/gpu_service_impl.cc
@@ -284,11 +284,6 @@
   // Record initialization only after collecting the GPU info because that can
   // take a significant amount of time.
   gpu_info_.initialization_time = base::Time::Now() - start_time_;
-
-  // For the GPU watchdog - how long the init might take on a slow machine?
-  UMA_HISTOGRAM_CUSTOM_TIMES(
-      "GPU.GPUInitializationTime", gpu_info_.initialization_time,
-      base::TimeDelta::FromSeconds(1), base::TimeDelta::FromSeconds(60), 50);
 }
 
 void GpuServiceImpl::InitializeWithHost(
diff --git a/content/browser/appcache/appcache_update_job_unittest.cc b/content/browser/appcache/appcache_update_job_unittest.cc
index 22f09b6..3ee2543 100644
--- a/content/browser/appcache/appcache_update_job_unittest.cc
+++ b/content/browser/appcache/appcache_update_job_unittest.cc
@@ -23,6 +23,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "content/browser/appcache/appcache_cache_test_helper.h"
 #include "content/browser/appcache/appcache_group.h"
 #include "content/browser/appcache/appcache_host.h"
 #include "content/browser/appcache/appcache_response.h"
@@ -59,6 +60,7 @@
 const base::TimeDelta kMaxEvictableErrorDuration =
     base::TimeDelta::FromDays(14);
 const base::TimeDelta kOneHour = base::TimeDelta::FromHours(1);
+const base::TimeDelta kOneYear = base::TimeDelta::FromDays(365);
 
 const char kManifest1Contents[] =
     "CACHE MANIFEST\n"
@@ -68,6 +70,8 @@
     "NETWORK:\n"
     "*\n";
 
+const char kExplicit1Contents[] = "explicit1";
+
 // By default, kManifest2Contents is served from a path in /files/, so any
 // resource listing in it that references outside of that path will require a
 // scope override to be stored.
@@ -174,7 +178,7 @@
       (*body) = "CACHE MANIFEST\n";
     } else if (path == "/files/explicit1") {
       (*headers) = std::string(ok_headers, base::size(ok_headers));
-      (*body) = "explicit1";
+      (*body) = kExplicit1Contents;
     } else if (path == "/files/explicit2") {
       (*headers) = std::string(ok_headers, base::size(ok_headers));
       (*body) = "explicit2";
@@ -659,6 +663,12 @@
     response->headers = info.headers;
     response->headers->GetMimeType(&response->mime_type);
 
+    // Provide valid request and response times to
+    // UpdateUrlLoaderRequest.  It's still up to that class's
+    // |OnReceiveResponse| to capture these values in the net::HttpResponseInfo.
+    response->request_time = base::Time::Now();
+    response->response_time = base::Time::Now();
+
     params->client->OnReceiveResponse(std::move(response));
 
     mojo::ScopedDataPipeProducerHandle producer_handle;
@@ -3274,9 +3284,7 @@
         base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
                        base::Unretained(this), update));
 
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&AppCacheUpdateJobTest::UpdateFinishedUnwound,
-                                  base::Unretained(this)));
+    TriggerTestComplete();
   }
 
   void IfModifiedTestRefetch() {
@@ -3312,9 +3320,7 @@
         base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
                        base::Unretained(this), update));
 
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&AppCacheUpdateJobTest::UpdateFinishedUnwound,
-                                  base::Unretained(this)));
+    TriggerTestComplete();
   }
 
   void IfModifiedTestLastModified() {
@@ -3351,9 +3357,7 @@
         base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
                        base::Unretained(this), update));
 
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&AppCacheUpdateJobTest::UpdateFinishedUnwound,
-                                  base::Unretained(this)));
+    TriggerTestComplete();
   }
 
   // AppCaches built with manifest parser version 0 should update without
@@ -3439,20 +3443,13 @@
     group_ = base::MakeRefCounted<AppCacheGroup>(
         service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
         111);
-    http_headers_request_test_jobs_.emplace(
-        group_->manifest_url(),
-        std::make_unique<HttpHeadersRequestTestJob>(
-            "Sat, 29 Oct 1994 19:43:31 GMT", std::string()));
     AppCacheUpdateJob* update =
         new AppCacheUpdateJob(service_.get(), group_.get());
     group_->update_job_ = update;
 
-    // Give the newest cache a manifest entry that is in storage.
-    response_writer_ =
-        service_->storage()->CreateResponseWriter(group_->manifest_url());
-
-    AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(),
-                                        response_writer_->response_id());
+    // Create a cache without a manifest entry.  The manifest entry will be
+    // added later.
+    AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), -1);
     MockFrontend* frontend = MakeMockFrontend();
     AppCacheHost* host = MakeHost(frontend);
     host->AssociateCompleteCache(cache);
@@ -3476,26 +3473,41 @@
     frontend->AddExpectedEvent(
         blink::mojom::AppCacheEventID::APPCACHE_UPDATE_READY_EVENT);
 
+    AppCacheCacheTestHelper::CacheEntries cache_entries;
+
+    // Add cache entry for manifest.
     // Seed storage with expected manifest response info that will cause
     // an If-Modified-Since header to be put in the manifest fetch request.
     const char data[] =
         "HTTP/1.1 200 OK\0"
-        "Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0"
-        "\0";
+        "Last-Modified: Sat, 29 Oct 1994 19:43:31 GMT\0";
     scoped_refptr<net::HttpResponseHeaders> headers =
         base::MakeRefCounted<net::HttpResponseHeaders>(
             std::string(data, base::size(data)));
     std::unique_ptr<net::HttpResponseInfo> response_info =
         std::make_unique<net::HttpResponseInfo>();
     response_info->headers = std::move(headers);
-    scoped_refptr<HttpResponseInfoIOBuffer> io_buffer =
-        base::MakeRefCounted<HttpResponseInfoIOBuffer>(
-            std::move(response_info));
-    response_writer_->WriteInfo(
-        io_buffer.get(),
+    AppCacheCacheTestHelper::AddCacheEntry(
+        &cache_entries, group_->manifest_url(), AppCacheEntry::EXPLICIT,
+        /*expect_if_modified_since=*/"Sat, 29 Oct 1994 19:43:31 GMT",
+        /*expect_if_none_match=*/std::string(), /*headers_allowed=*/true,
+        std::move(response_info), kManifest1Contents);
+
+    // Add all header checks from |cache_entries|.
+    for (auto it = cache_entries.begin(); it != cache_entries.end(); ++it) {
+      http_headers_request_test_jobs_.emplace(
+          it->first,
+          std::make_unique<HttpHeadersRequestTestJob>(
+              it->second->expect_if_modified_since,
+              it->second->expect_if_none_match, it->second->headers_allowed));
+    }
+
+    cache_helper_ = std::make_unique<AppCacheCacheTestHelper>(
+        service_.get(), group_->manifest_url(), cache, std::move(cache_entries),
         base::BindOnce(
             &AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
             base::Unretained(this)));
+    cache_helper_->Write();
 
     // Start update after data write completes asynchronously.
   }
@@ -3643,6 +3655,89 @@
     // Start update after data write completes asynchronously.
   }
 
+  void RequestResponseTimesAreSetTest() {
+    MakeService();
+    group_ = base::MakeRefCounted<AppCacheGroup>(
+        service_->storage(), MockHttpServer::GetMockUrl("files/manifest1"),
+        111);
+    AppCacheUpdateJob* update =
+        new AppCacheUpdateJob(service_.get(), group_.get());
+    group_->update_job_ = update;
+
+    // Create a cache without a manifest entry.  The manifest entry will be
+    // added later.
+    AppCache* cache = MakeCacheForGroup(service_->storage()->NewCacheId(), -1);
+    MockFrontend* frontend = MakeMockFrontend();
+    AppCacheHost* host = MakeHost(frontend);
+    host->AssociateCompleteCache(cache);
+
+    // Set up checks for when update job finishes.
+    do_checks_after_update_finished_ = false;
+
+    AppCacheCacheTestHelper::CacheEntries cache_entries;
+
+    // Add cache entry for manifest.
+    // Seed storage with expected manifest response info that will cause
+    // an If-Modified-Since header to be put in the manifest fetch request.
+    {
+      const char data[] =
+          "HTTP/1.1 200 OK\0"
+          "Last-Modified: Sat, 29 Oct 2019 19:43:31 GMT\0";
+      scoped_refptr<net::HttpResponseHeaders> headers =
+          base::MakeRefCounted<net::HttpResponseHeaders>(
+              std::string(data, base::size(data)));
+      std::unique_ptr<net::HttpResponseInfo> response_info =
+          std::make_unique<net::HttpResponseInfo>();
+      response_info->headers = std::move(headers);
+      response_info->request_time = base::Time::Now() - kOneYear;
+      response_info->response_time = base::Time::Now() - kOneYear;
+      AppCacheCacheTestHelper::AddCacheEntry(
+          &cache_entries, group_->manifest_url(), AppCacheEntry::EXPLICIT,
+          /*expect_if_modified_since=*/std::string(),
+          /*expect_if_none_match=*/std::string(), /*headers_allowed=*/true,
+          std::move(response_info), kManifest1Contents);
+    }
+
+    cache_helper_ = std::make_unique<AppCacheCacheTestHelper>(
+        service_.get(), group_->manifest_url(), cache, std::move(cache_entries),
+        base::BindOnce(
+            &AppCacheUpdateJobTest::StartUpdateAfterSeedingStorageData,
+            base::Unretained(this)));
+    cache_helper_->Write();
+
+    post_update_finished_cb_ = base::BindOnce(
+        &AppCacheUpdateJobTest::RequestResponseTimesAreSetUpdateFinished,
+        base::Unretained(this));
+
+    // Start update after data write completes asynchronously.
+    // After update is finished, continues async in
+    // |RequestResponseTimesAreSetUpdateFinished|.
+  }
+
+  void RequestResponseTimesAreSetUpdateFinished() {
+    ASSERT_NE(group_->newest_complete_cache(), cache_helper_->write_cache());
+    ASSERT_NE(group_->newest_complete_cache(), nullptr);
+    cache_helper_->PrepareForRead(
+        group_->newest_complete_cache(),
+        base::BindOnce(
+            &AppCacheUpdateJobTest::RequestResponseTimesAreSetReadFinished,
+            base::Unretained(this)));
+    cache_helper_->Read();
+    // Continues async in |RequestResponseTimesAreSetReadFinished|.
+  }
+
+  void RequestResponseTimesAreSetReadFinished() {
+    auto it = cache_helper_->read_cache_entries().find(
+        MockHttpServer::GetMockUrl("files/explicit1"));
+    ASSERT_NE(it, cache_helper_->read_cache_entries().end());
+    CHECK_GT(it->second->response_info->request_time,
+             base::Time::Now() - kOneHour);
+    CHECK_GT(it->second->response_info->response_time,
+             base::Time::Now() - kOneHour);
+    TriggerTestComplete();
+    // Continues async in |TestComplete|.
+  }
+
   void IfNoneMatchRefetchTest() {
     MakeService();
     group_ = base::MakeRefCounted<AppCacheGroup>(
@@ -3675,9 +3770,7 @@
         base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
                        base::Unretained(this), update));
 
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&AppCacheUpdateJobTest::UpdateFinishedUnwound,
-                                  base::Unretained(this)));
+    TriggerTestComplete();
   }
 
   void MultipleHeadersRefetchTest() {
@@ -3715,9 +3808,7 @@
         base::BindOnce(&AppCacheUpdateJobTest::VerifyHeadersAndDeleteUpdate,
                        base::Unretained(this), update));
 
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&AppCacheUpdateJobTest::UpdateFinishedUnwound,
-                                  base::Unretained(this)));
+    TriggerTestComplete();
   }
 
   void CrossOriginHttpsSuccessTest() {
@@ -3786,20 +3877,30 @@
   }
 
   void UpdateFinished() {
+    if (post_update_finished_cb_) {
+      std::move(post_update_finished_cb_).Run();
+      return;
+    }
+
+    TriggerTestComplete();
+  }
+
+  void TriggerTestComplete() {
     // We unwind the stack prior to finishing up to let stack-based objects
     // get deleted.
     base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(&AppCacheUpdateJobTest::UpdateFinishedUnwound,
+        FROM_HERE, base::BindOnce(&AppCacheUpdateJobTest::TestComplete,
                                   base::Unretained(this)));
   }
 
-  void UpdateFinishedUnwound() {
+  void TestComplete() {
     EXPECT_EQ(AppCacheGroup::IDLE, group_->update_status());
     EXPECT_TRUE(group_->update_job() == nullptr);
     if (do_checks_after_update_finished_)
       VerifyExpectations();
 
     // Clean up everything that was created on the IO thread.
+    cache_helper_.reset();
     protect_newest_cache_ = nullptr;
     group_ = nullptr;
     hosts_.clear();
@@ -3832,8 +3933,10 @@
     group_->set_last_full_update_check_time(cache->update_time());
 
     // Add manifest entry to cache.
-    cache->AddEntry(manifest_entry_url, AppCacheEntry(AppCacheEntry::MANIFEST,
-                                                      manifest_response_id));
+    if (manifest_response_id >= 0) {
+      cache->AddEntry(manifest_entry_url, AppCacheEntry(AppCacheEntry::MANIFEST,
+                                                        manifest_response_id));
+    }
 
     // Specific tests that expect a newer time should set
     // expect_full_update_time_newer_than_ which causes this
@@ -4562,8 +4665,10 @@
   scoped_refptr<AppCacheGroup> group_;
   scoped_refptr<AppCache> protect_newest_cache_;
   base::OnceClosure test_completed_cb_;
+  base::OnceClosure post_update_finished_cb_;
 
   std::unique_ptr<AppCacheResponseWriter> response_writer_;
+  std::unique_ptr<AppCacheCacheTestHelper> cache_helper_;
 
   // Hosts used by an async test that need to live until update job finishes.
   // Otherwise, test can put host on the stack instead of here.
@@ -4977,6 +5082,10 @@
       &AppCacheUpdateJobTest::IfNoneMatchUpgradeParserVersion1Test);
 }
 
+TEST_F(AppCacheUpdateJobTest, RequestResponseTimesAreSet) {
+  RunTestOnUIThread(&AppCacheUpdateJobTest::RequestResponseTimesAreSetTest);
+}
+
 TEST_F(AppCacheUpdateJobTest, IfNoneMatchRefetch) {
   RunTestOnUIThread(&AppCacheUpdateJobTest::IfNoneMatchRefetchTest);
 }
diff --git a/content/browser/browser_interface_binders.cc b/content/browser/browser_interface_binders.cc
index 7f530ca..8b5d3ffb 100644
--- a/content/browser/browser_interface_binders.cc
+++ b/content/browser/browser_interface_binders.cc
@@ -208,8 +208,8 @@
 void BindSharedWorkerConnector(
     RenderFrameHostImpl* host,
     mojo::PendingReceiver<blink::mojom::SharedWorkerConnector> receiver) {
-  SharedWorkerConnectorImpl::Create(host->GetProcess()->GetID(),
-                                    host->GetRoutingID(), std::move(receiver));
+  SharedWorkerConnectorImpl::Create(host->GetGlobalFrameRoutingId(),
+                                    std::move(receiver));
 }
 
 #if defined(OS_ANDROID)
diff --git a/content/browser/child_process_task_port_provider_mac_unittest.cc b/content/browser/child_process_task_port_provider_mac_unittest.cc
index 78585c5f..acb656e 100644
--- a/content/browser/child_process_task_port_provider_mac_unittest.cc
+++ b/content/browser/child_process_task_port_provider_mac_unittest.cc
@@ -34,7 +34,7 @@
 #if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)
   MOCK_METHOD1(SetIPCLoggingEnabled, void(bool));
 #endif
-#if BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
   MOCK_METHOD1(SetCoverageFile, void(base::File));
 #endif
   MOCK_METHOD1(GetBackgroundTracingAgentProvider,
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 35af015..ce96260 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -797,6 +797,11 @@
 };
 
 // static
+RenderFrameHost* RenderFrameHost::FromID(GlobalFrameRoutingId id) {
+  return RenderFrameHostImpl::FromID(id);
+}
+
+// static
 RenderFrameHost* RenderFrameHost::FromID(int render_process_id,
                                          int render_frame_id) {
   return RenderFrameHostImpl::FromID(
@@ -809,13 +814,6 @@
 }
 
 // static
-RenderFrameHostImpl* RenderFrameHostImpl::FromID(int render_process_id,
-                                                 int render_frame_id) {
-  return RenderFrameHostImpl::FromID(
-      GlobalFrameRoutingId(render_process_id, render_frame_id));
-}
-
-// static
 RenderFrameHostImpl* RenderFrameHostImpl::FromID(GlobalFrameRoutingId id) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   RoutingIDFrameMap* frames = g_routing_id_frame_map.Pointer();
@@ -824,6 +822,12 @@
 }
 
 // static
+RenderFrameHostImpl* RenderFrameHostImpl::FromID(int render_process_id,
+                                                 int render_frame_id) {
+  return RenderFrameHostImpl::FromID(
+      GlobalFrameRoutingId(render_process_id, render_frame_id));
+}
+// static
 RenderFrameHost* RenderFrameHost::FromAXTreeID(ui::AXTreeID ax_tree_id) {
   return RenderFrameHostImpl::FromAXTreeID(ax_tree_id);
 }
diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc
index 0cc2f9c..1b34a9b 100644
--- a/content/browser/gpu/gpu_data_manager_impl_private.cc
+++ b/content/browser/gpu/gpu_data_manager_impl_private.cc
@@ -608,6 +608,10 @@
       gpu_info_.dx12_vulkan_version_info;
 #endif
   gpu_info_ = gpu_info;
+  UMA_HISTOGRAM_CUSTOM_TIMES("GPU.GPUInitializationTime.V2",
+                             gpu_info_.initialization_time,
+                             base::TimeDelta::FromMilliseconds(200),
+                             base::TimeDelta::FromSeconds(5), 50);
 #if defined(OS_WIN)
   if (!dx_diagnostics.IsEmpty()) {
     gpu_info_.dx_diagnostics = dx_diagnostics;
diff --git a/content/browser/hid/hid_browsertest.cc b/content/browser/hid/hid_browsertest.cc
index 00c63838..226b168 100644
--- a/content/browser/hid/hid_browsertest.cc
+++ b/content/browser/hid/hid_browsertest.cc
@@ -94,13 +94,17 @@
 
   auto device = device::mojom::HidDeviceInfo::New();
   device->guid = "test-guid";
+  std::vector<device::mojom::HidDeviceInfoPtr> devices;
+  devices.push_back(std::move(device));
   EXPECT_CALL(delegate(), RunChooserInternal)
-      .WillOnce(Return(ByMove(std::move(device))));
+      .WillOnce(Return(ByMove(std::move(devices))));
 
   EXPECT_EQ(true, EvalJs(shell(),
                          R"((async () => {
-               let device = await navigator.hid.requestDevice({filters:[]});
-               return device instanceof HIDDevice;
+               let devices = await navigator.hid.requestDevice({filters:[]});
+               return devices instanceof Array
+                      && devices.length == 1
+                      && devices[0] instanceof HIDDevice;
              })())"));
 }
 
@@ -111,15 +115,11 @@
       .WillOnce(Return(false));
   EXPECT_CALL(delegate(), RunChooserInternal).Times(Exactly(0));
 
-  EXPECT_EQ(false, EvalJs(shell(),
-                          R"((async () => {
-                            try {
-                              await navigator.hid.requestDevice({filters:[]});
-                              return true;
-                            } catch (e) {
-                              return false;
-                            }
-                          })())"));
+  EXPECT_EQ(0, EvalJs(shell(),
+                      R"((async () => {
+               let devices = await navigator.hid.requestDevice({filters:[]});
+               return devices.length;
+             })())"));
 }
 
 }  // namespace content
diff --git a/content/browser/hid/hid_service.cc b/content/browser/hid/hid_service.cc
index faecfdc0..5da923d5 100644
--- a/content/browser/hid/hid_service.cc
+++ b/content/browser/hid/hid_service.cc
@@ -70,7 +70,7 @@
     RequestDeviceCallback callback) {
   HidDelegate* delegate = GetContentClient()->browser()->GetHidDelegate();
   if (!delegate->CanRequestDevicePermission(web_contents(), origin())) {
-    std::move(callback).Run(nullptr);
+    std::move(callback).Run(std::vector<device::mojom::HidDeviceInfoPtr>());
     return;
   }
 
@@ -126,14 +126,10 @@
   std::move(callback).Run(std::move(result));
 }
 
-void HidService::FinishRequestDevice(RequestDeviceCallback callback,
-                                     device::mojom::HidDeviceInfoPtr device) {
-  if (!device) {
-    std::move(callback).Run(nullptr);
-    return;
-  }
-
-  std::move(callback).Run(std::move(device));
+void HidService::FinishRequestDevice(
+    RequestDeviceCallback callback,
+    std::vector<device::mojom::HidDeviceInfoPtr> devices) {
+  std::move(callback).Run(std::move(devices));
 }
 
 void HidService::FinishConnect(
diff --git a/content/browser/hid/hid_service.h b/content/browser/hid/hid_service.h
index 939f669..ed75856 100644
--- a/content/browser/hid/hid_service.h
+++ b/content/browser/hid/hid_service.h
@@ -48,8 +48,9 @@
 
   void FinishGetDevices(GetDevicesCallback callback,
                         std::vector<device::mojom::HidDeviceInfoPtr> devices);
-  void FinishRequestDevice(RequestDeviceCallback callback,
-                           device::mojom::HidDeviceInfoPtr device);
+  void FinishRequestDevice(
+      RequestDeviceCallback callback,
+      std::vector<device::mojom::HidDeviceInfoPtr> devices);
   void FinishConnect(
       ConnectCallback callback,
       mojo::PendingRemote<device::mojom::HidConnection> connection);
diff --git a/content/browser/hid/hid_service_unittest.cc b/content/browser/hid/hid_service_unittest.cc
index 150b427c..bb564f59 100644
--- a/content/browser/hid/hid_service_unittest.cc
+++ b/content/browser/hid/hid_service_unittest.cc
@@ -143,24 +143,27 @@
 
   auto device_info = device::mojom::HidDeviceInfo::New();
   device_info->guid = kTestGuid;
-  hid_manager()->AddDevice(device_info.Clone());
+  std::vector<device::mojom::HidDeviceInfoPtr> device_infos;
+  device_infos.push_back(device_info.Clone());
+  hid_manager()->AddDevice(std::move(device_info));
 
   EXPECT_CALL(hid_delegate(), CanRequestDevicePermission)
       .WillOnce(testing::Return(true));
   EXPECT_CALL(hid_delegate(), RunChooserInternal)
-      .WillOnce(testing::Return(testing::ByMove(std::move(device_info))));
+      .WillOnce(testing::Return(testing::ByMove(std::move(device_infos))));
 
   base::RunLoop run_loop;
-  device::mojom::HidDeviceInfoPtr chosen_device;
+  std::vector<device::mojom::HidDeviceInfoPtr> chosen_devices;
   service->RequestDevice(
       std::vector<blink::mojom::HidDeviceFilterPtr>(),
       base::BindLambdaForTesting(
-          [&run_loop, &chosen_device](device::mojom::HidDeviceInfoPtr d) {
-            chosen_device = std::move(d);
+          [&run_loop,
+           &chosen_devices](std::vector<device::mojom::HidDeviceInfoPtr> d) {
+            chosen_devices = std::move(d);
             run_loop.Quit();
           }));
   run_loop.Run();
-  EXPECT_TRUE(chosen_device);
+  EXPECT_EQ(1u, chosen_devices.size());
 }
 
 TEST_F(HidServiceTest, OpenAndCloseHidConnection) {
diff --git a/content/browser/hid/hid_test_utils.h b/content/browser/hid/hid_test_utils.h
index 73945c8..2589e62 100644
--- a/content/browser/hid/hid_test_utils.h
+++ b/content/browser/hid/hid_test_utils.h
@@ -29,7 +29,8 @@
       std::vector<blink::mojom::HidDeviceFilterPtr> filters,
       HidChooser::Callback callback) override;
 
-  MOCK_METHOD0(RunChooserInternal, device::mojom::HidDeviceInfoPtr());
+  MOCK_METHOD0(RunChooserInternal,
+               std::vector<device::mojom::HidDeviceInfoPtr>());
   MOCK_METHOD2(CanRequestDevicePermission,
                bool(content::WebContents* web_contents,
                     const url::Origin& requesting_origin));
diff --git a/content/browser/indexed_db/docs/README.md b/content/browser/indexed_db/docs/README.md
index c6487a922..ac5ecc6 100644
--- a/content/browser/indexed_db/docs/README.md
+++ b/content/browser/indexed_db/docs/README.md
@@ -75,27 +75,27 @@
 database in an origin. This is used to uniquely identify a blob in an IndexedDB
 database.
 
-#### Blob Number
-A blob number is a `int64_t` `database_id` and a unique auto-incrementing
+#### 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
-`BlobNumberGeneratorCurrentNumber` which is used to save the next blob number number
+`BlobNumberGeneratorCurrentNumber` which is used to save the next blob key number
 to use.
 
-A blob number uniquely identifies a blob in an backing store.
+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 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 number is added to the
+(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 number.
+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
@@ -116,7 +116,7 @@
 #### 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 number & blob file information (like size, type,
+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`:
@@ -128,17 +128,17 @@
 given object store data key.
 
 #### Recovery Journal
-The recovery journal is a list of blob numbers that are pending deletion. These
-blob numbers represent blobs that are in an **unlinked**, **inactive**, and
+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 number) is deleted, and the
+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 numbers that are to-be-deleted by a
+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).
diff --git a/content/browser/media/media_power_experiment_manager.cc b/content/browser/media/media_power_experiment_manager.cc
index 092a4aee..549aca2 100644
--- a/content/browser/media/media_power_experiment_manager.cc
+++ b/content/browser/media/media_power_experiment_manager.cc
@@ -40,6 +40,8 @@
       current_experiment_player_ && *current_experiment_player_ == player_id) {
     current_experiment_player_.reset();
     current_experiment_cb_ = ExperimentCB();
+    // Note that there will be no incoming player; there's exactly one and we're
+    // removing it.
   }
   players_.erase(player_id);
   CheckExperimentState();
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc
index 430c723e..cf7858f 100644
--- a/content/browser/media/media_web_contents_observer.cc
+++ b/content/browser/media/media_web_contents_observer.cc
@@ -360,6 +360,8 @@
     ActiveMediaPlayerMap* player_map) {
   (*player_map)[id.render_frame_host].insert(id.delegate_id);
   if (power_experiment_manager_) {
+    // Bind the callback to a WeakPtr for the frame, so that we won't try to
+    // notify the frame after it's been destroyed.
     power_experiment_manager_->PlayerStarted(
         id,
         base::BindRepeating(&MediaWebContentsObserver::OnExperimentStateChanged,
@@ -426,7 +428,9 @@
 
 void MediaWebContentsObserver::OnExperimentStateChanged(MediaPlayerId id,
                                                         bool is_starting) {
-  // TODO(liberato): Notify the player.
+  id.render_frame_host->Send(
+      new MediaPlayerDelegateMsg_NotifyPowerExperimentState(
+          id.render_frame_host->GetRoutingID(), id.delegate_id, is_starting));
 }
 
 void MediaWebContentsObserver::RemoveAllPlayers(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 57d1bcbb..bae3acad 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -291,7 +291,7 @@
 #include "services/service_manager/zygote/common/zygote_handle.h"  // nogncheck
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
 #include "content/common/coverage_utils.h"
 #endif
 
@@ -3471,7 +3471,7 @@
   child_process_->SetIPCLoggingEnabled(IPC::Logging::GetInstance()->Enabled());
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
   child_process_->SetCoverageFile(OpenCoverageFile());
 #endif
 }
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index dba17ef..de66a89e 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -399,8 +399,11 @@
 }
 
 void RenderViewHostImpl::SetMainFrameRoutingId(int routing_id) {
+  bool was_active = is_active();
   main_frame_routing_id_ = routing_id;
   GetWidget()->UpdatePriority();
+  if (was_active && !is_active())
+    GetWidget()->DidDestroyRenderWidget();
 }
 
 void RenderViewHostImpl::EnterBackForwardCache() {
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 8aa9da1..53a1ae45 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -522,6 +522,12 @@
     process_->UpdateClientPriority(this);
 }
 
+void RenderWidgetHostImpl::DidDestroyRenderWidget() {
+  if (owner_delegate_)
+    DCHECK(!owner_delegate_->IsMainFrameActive());
+  frame_token_message_queue_->Reset();
+}
+
 void RenderWidgetHostImpl::Init() {
   DCHECK(process_->IsInitializedAndNotDead());
 
@@ -859,8 +865,8 @@
     bool scroll_focused_node_into_view) {
   // If the RenderViewHost is inactive, then there is no RenderWidget that can
   // receive visual properties yet, even though we are setting them on the
-  // browser side. Wait until the RenderWidget is not undead before sending the
-  // visual properties.
+  // browser side. Wait until there is a local main frame with a RenderWidget
+  // to receive these before sending the visual properties.
   //
   // When the RenderViewHost becomes active, a SynchronizeVisualProperties()
   // call does not explicitly get made. That is because RenderWidgets for frames
@@ -2925,6 +2931,13 @@
 }
 
 void RenderWidgetHostImpl::DidProcessFrame(uint32_t frame_token) {
+  // In this case the RenderWidgetHostImpl is still here because it's the top
+  // level RenderWidgetHostImpl, but the renderer's RenderWidget no longer
+  // exists and this would clobber state that was reset in
+  // DidDestroyRenderWidget() with data from the destroyed RenderWidget.
+  if (owner_delegate_ && !owner_delegate_->IsMainFrameActive())
+    return;
+
   frame_token_message_queue_->DidProcessFrame(frame_token);
 }
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 03c3f3d..80cdc18 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -314,6 +314,11 @@
   void SetFrameDepth(unsigned int depth);
   void SetIntersectsViewport(bool intersects);
   void UpdatePriority();
+  // If there is no local main frame, then the RenderWidget in the renderer
+  // has been destroyed, and a new one will be created when a main frame
+  // exists again. This notifies that the renderer-side RenderWidget has been
+  // destroyed.
+  void DidDestroyRenderWidget();
 
   // Tells the renderer to die and optionally delete |this|.
   void ShutdownAndDestroyWidget(bool also_delete);
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc
index ed112ac..77453de2 100644
--- a/content/browser/service_worker/service_worker_container_host.cc
+++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -897,14 +897,22 @@
     return GetContentClient()->browser()->AllowServiceWorkerOnUI(
         scope, site_for_cookies().RepresentativeUrl(), top_frame_origin(),
         script_url, context_->wrapper()->browser_context(),
-        base::BindRepeating(&WebContentsImpl::FromRenderFrameHostID,
-                            process_id_, frame_id_));
+        base::BindRepeating(
+            [](int process_id, int frame_id) {
+              return WebContentsImpl::FromRenderFrameHostID(process_id,
+                                                            frame_id);
+            },
+            process_id_, frame_id_));
   } else {
     return GetContentClient()->browser()->AllowServiceWorkerOnIO(
         scope, site_for_cookies().RepresentativeUrl(), top_frame_origin(),
         script_url, context_->wrapper()->resource_context(),
-        base::BindRepeating(&WebContentsImpl::FromRenderFrameHostID,
-                            process_id_, frame_id_));
+        base::BindRepeating(
+            [](int process_id, int frame_id) {
+              return WebContentsImpl::FromRenderFrameHostID(process_id,
+                                                            frame_id);
+            },
+            process_id_, frame_id_));
   }
 }
 
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index f68eb4b0..80ace8e 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -818,12 +818,12 @@
 }
 
 // static
-WebContents* WebContentsImpl::FromRenderFrameHostID(int render_process_host_id,
-                                                    int render_frame_host_id) {
+WebContents* WebContentsImpl::FromRenderFrameHostID(
+    GlobalFrameRoutingId render_frame_host_id) {
   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
          !BrowserThread::IsThreadInitialized(BrowserThread::UI));
   RenderFrameHost* render_frame_host =
-      RenderFrameHost::FromID(render_process_host_id, render_frame_host_id);
+      RenderFrameHost::FromID(render_frame_host_id);
   if (!render_frame_host)
     return nullptr;
 
@@ -831,6 +831,13 @@
 }
 
 // static
+WebContents* WebContentsImpl::FromRenderFrameHostID(int render_process_host_id,
+                                                    int render_frame_host_id) {
+  return FromRenderFrameHostID(
+      GlobalFrameRoutingId(render_process_host_id, render_frame_host_id));
+}
+
+// static
 WebContentsImpl* WebContentsImpl::FromOuterFrameTreeNode(
     const FrameTreeNode* frame_tree_node) {
   return WebContentsImpl::FromFrameTreeNode(frame_tree_node)
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index 698984fb0..6361e700 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -167,6 +167,8 @@
 
   static WebContentsImpl* FromFrameTreeNode(
       const FrameTreeNode* frame_tree_node);
+  static WebContents* FromRenderFrameHostID(
+      GlobalFrameRoutingId render_frame_host_id);
   static WebContents* FromRenderFrameHostID(int render_process_host_id,
                                             int render_frame_host_id);
   static WebContents* FromFrameTreeNodeId(int frame_tree_node_id);
diff --git a/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc b/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc
index a0ed484..4f19f7e 100644
--- a/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc
+++ b/content/browser/webrtc/webrtc_capture_from_element_browsertest.cc
@@ -104,13 +104,6 @@
 
 IN_PROC_BROWSER_TEST_F(WebRtcCaptureFromElementBrowserTest,
                        VerifyCanvasCaptureWebGLFrames) {
-#if defined(OS_ANDROID)
-  // TODO(ericrk): fails on Pixel 2 Skia Renderer Vulkan after shared image
-  // changes. See https://crbug.com/1039232
-  if (base::SysInfo::HardwareModelName() == "Pixel 2") {
-    return;
-  }
-#endif
   MakeTypicalCall("testCanvasCapture(drawWebGL);", kCanvasCaptureTestHtmlFile);
 }
 
diff --git a/content/browser/worker_host/shared_worker_connector_impl.cc b/content/browser/worker_host/shared_worker_connector_impl.cc
index d597032b..68ff3a6 100644
--- a/content/browser/worker_host/shared_worker_connector_impl.cc
+++ b/content/browser/worker_host/shared_worker_connector_impl.cc
@@ -21,17 +21,16 @@
 
 // static
 void SharedWorkerConnectorImpl::Create(
-    int client_process_id,
-    int frame_id,
+    GlobalFrameRoutingId client_render_frame_host_id,
     mojo::PendingReceiver<blink::mojom::SharedWorkerConnector> receiver) {
   mojo::MakeSelfOwnedReceiver(base::WrapUnique(new SharedWorkerConnectorImpl(
-                                  client_process_id, frame_id)),
+                                  client_render_frame_host_id)),
                               std::move(receiver));
 }
 
-SharedWorkerConnectorImpl::SharedWorkerConnectorImpl(int client_process_id,
-                                                     int frame_id)
-    : client_process_id_(client_process_id), frame_id_(frame_id) {}
+SharedWorkerConnectorImpl::SharedWorkerConnectorImpl(
+    GlobalFrameRoutingId client_render_frame_host_id)
+    : client_render_frame_host_id_(client_render_frame_host_id) {}
 
 void SharedWorkerConnectorImpl::Connect(
     blink::mojom::SharedWorkerInfoPtr info,
@@ -41,7 +40,8 @@
     blink::mojom::SharedWorkerCreationContextType creation_context_type,
     mojo::ScopedMessagePipeHandle message_port,
     mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token) {
-  RenderProcessHost* host = RenderProcessHost::FromID(client_process_id_);
+  RenderProcessHost* host =
+      RenderProcessHost::FromID(client_render_frame_host_id_.child_id);
   // The render process was already terminated.
   if (!host) {
     mojo::Remote<blink::mojom::SharedWorkerClient> remote_client(
@@ -62,7 +62,7 @@
   SharedWorkerServiceImpl* service =
       static_cast<StoragePartitionImpl*>(host->GetStoragePartition())
           ->GetSharedWorkerService();
-  service->ConnectToWorker(client_process_id_, frame_id_, std::move(info),
+  service->ConnectToWorker(client_render_frame_host_id_, std::move(info),
                            std::move(outside_fetch_client_settings_object),
                            std::move(client), creation_context_type,
                            blink::MessagePortChannel(std::move(message_port)),
diff --git a/content/browser/worker_host/shared_worker_connector_impl.h b/content/browser/worker_host/shared_worker_connector_impl.h
index 86cb9d5..c6580946 100644
--- a/content/browser/worker_host/shared_worker_connector_impl.h
+++ b/content/browser/worker_host/shared_worker_connector_impl.h
@@ -6,6 +6,7 @@
 #define CONTENT_BROWSER_WORKER_HOST_SHARED_WORKER_CONNECTOR_IMPL_H_
 
 #include "content/common/content_export.h"
+#include "content/public/browser/global_routing_id.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "third_party/blink/public/mojom/worker/shared_worker_connector.mojom.h"
@@ -18,12 +19,12 @@
     : public blink::mojom::SharedWorkerConnector {
  public:
   static void Create(
-      int client_process_id,
-      int frame_id,
+      GlobalFrameRoutingId client_render_frame_host_id,
       mojo::PendingReceiver<blink::mojom::SharedWorkerConnector> receiver);
 
  private:
-  SharedWorkerConnectorImpl(int client_process_id, int frame_id);
+  explicit SharedWorkerConnectorImpl(
+      GlobalFrameRoutingId client_render_frame_host_id);
 
   // blink::mojom::SharedWorkerConnector methods:
   void Connect(
@@ -35,8 +36,7 @@
       mojo::ScopedMessagePipeHandle message_port,
       mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token) override;
 
-  const int client_process_id_;
-  const int frame_id_;
+  const GlobalFrameRoutingId client_render_frame_host_id_;
 };
 
 }  // namespace content
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index 2819b9c5..21f040f 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -117,8 +117,7 @@
     // Notify the service that each client still connected will be removed and
     // that the worker will terminate.
     for (const auto& client : clients_) {
-      service_->NotifyClientRemoved(instance_, client.client_process_id,
-                                    client.frame_id);
+      service_->NotifyClientRemoved(instance_, client.render_frame_host_id);
     }
     service_->NotifyWorkerTerminating(instance_);
   } else {
@@ -237,8 +236,7 @@
   service_->NotifyWorkerStarted(instance_, worker_process_host_->GetID(),
                                 devtools_worker_token);
   for (const auto& client : clients_) {
-    service_->NotifyClientAdded(instance_, client.client_process_id,
-                                client.frame_id);
+    service_->NotifyClientAdded(instance_, client.render_frame_host_id);
   }
 }
 
@@ -340,12 +338,10 @@
 SharedWorkerHost::ClientInfo::ClientInfo(
     mojo::Remote<blink::mojom::SharedWorkerClient> client,
     int connection_request_id,
-    int client_process_id,
-    int frame_id)
+    GlobalFrameRoutingId render_frame_host_id)
     : client(std::move(client)),
       connection_request_id(connection_request_id),
-      client_process_id(client_process_id),
-      frame_id(frame_id) {}
+      render_frame_host_id(render_frame_host_id) {}
 
 SharedWorkerHost::ClientInfo::~ClientInfo() {}
 
@@ -401,10 +397,8 @@
 SharedWorkerHost::GetRenderFrameIDsForWorker() {
   std::vector<GlobalFrameRoutingId> result;
   result.reserve(clients_.size());
-  for (const ClientInfo& info : clients_) {
-    result.push_back(
-        GlobalFrameRoutingId(info.client_process_id, info.frame_id));
-  }
+  for (const ClientInfo& info : clients_)
+    result.push_back(info.render_frame_host_id);
   return result;
 }
 
@@ -414,8 +408,7 @@
 
 void SharedWorkerHost::AddClient(
     mojo::PendingRemote<blink::mojom::SharedWorkerClient> client,
-    int client_process_id,
-    int frame_id,
+    GlobalFrameRoutingId client_render_frame_host_id,
     const blink::MessagePortChannel& port) {
   mojo::Remote<blink::mojom::SharedWorkerClient> remote_client(
       std::move(client));
@@ -425,7 +418,7 @@
   remote_client->OnCreated(instance_.creation_context_type());
 
   clients_.emplace_back(std::move(remote_client), next_connection_request_id_++,
-                        client_process_id, frame_id);
+                        client_render_frame_host_id);
   ClientInfo& info = clients_.back();
 
   // Observe when the client goes away.
@@ -438,7 +431,7 @@
   // Start() function will handle sending a notification for each existing
   // client.
   if (started_)
-    service_->NotifyClientAdded(instance_, client_process_id, frame_id);
+    service_->NotifyClientAdded(instance_, client_render_frame_host_id);
 }
 
 void SharedWorkerHost::SetAppCacheHandle(
@@ -459,8 +452,7 @@
   // It isn't necessary to send a notification to the removed clients since they
   // are about to be destroyed anyway.
   clients_.remove_if([](const ClientInfo& client_info) {
-    return !RenderFrameHostImpl::FromID(client_info.client_process_id,
-                                        client_info.frame_id);
+    return !RenderFrameHostImpl::FromID(client_info.render_frame_host_id);
   });
 }
 
@@ -488,8 +480,7 @@
       // Notify the service that a client was removed while the worker was
       // running.
       if (started_) {
-        service_->NotifyClientRemoved(instance_, it->client_process_id,
-                                      it->frame_id);
+        service_->NotifyClientRemoved(instance_, it->render_frame_host_id);
       }
       clients_.erase(it);
       break;
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index 9c280cd..0492d3de 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -107,8 +107,7 @@
   void Destruct();
 
   void AddClient(mojo::PendingRemote<blink::mojom::SharedWorkerClient> client,
-                 int client_process_id,
-                 int frame_id,
+                 GlobalFrameRoutingId client_render_frame_host_id,
                  const blink::MessagePortChannel& port);
 
   void SetAppCacheHandle(
@@ -142,13 +141,11 @@
   struct ClientInfo {
     ClientInfo(mojo::Remote<blink::mojom::SharedWorkerClient> client,
                int connection_request_id,
-               int client_process_id,
-               int frame_id);
+               GlobalFrameRoutingId render_frame_host_id);
     ~ClientInfo();
     mojo::Remote<blink::mojom::SharedWorkerClient> client;
     const int connection_request_id;
-    const int client_process_id;
-    const int frame_id;
+    const GlobalFrameRoutingId render_frame_host_id;
   };
 
   using ClientList = std::list<ClientInfo>;
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index bdbb602..316c837 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -128,11 +128,14 @@
   MessagePortChannel AddClient(
       SharedWorkerHost* host,
       mojo::PendingRemote<blink::mojom::SharedWorkerClient> client) {
+    GlobalFrameRoutingId dummy_render_frame_host_id(
+        mock_render_process_host_.GetID(), 22);
+
     mojo::MessagePipe message_pipe;
     MessagePortChannel local_port(std::move(message_pipe.handle0));
     MessagePortChannel remote_port(std::move(message_pipe.handle1));
-    host->AddClient(std::move(client), mock_render_process_host_.GetID(),
-                    22 /* dummy frame_id */, std::move(remote_port));
+    host->AddClient(std::move(client), dummy_render_frame_host_id,
+                    std::move(remote_port));
     return local_port;
   }
 
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index 9e828e6..421df8c 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -97,8 +97,7 @@
 }
 
 void SharedWorkerServiceImpl::ConnectToWorker(
-    int client_process_id,
-    int frame_id,
+    GlobalFrameRoutingId client_render_frame_host_id,
     blink::mojom::SharedWorkerInfoPtr info,
     blink::mojom::FetchClientSettingsObjectPtr
         outside_fetch_client_settings_object,
@@ -109,7 +108,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   RenderFrameHostImpl* render_frame_host =
-      RenderFrameHostImpl::FromID(client_process_id, frame_id);
+      RenderFrameHostImpl::FromID(client_render_frame_host_id);
   if (!render_frame_host) {
     // TODO(crbug.com/31666): Support the case where the requester is a worker
     // (i.e., nested worker).
@@ -135,9 +134,10 @@
           info->url,
           render_frame_host->ComputeSiteForCookies().RepresentativeUrl(),
           main_frame->GetLastCommittedOrigin(), info->name, constructor_origin,
-          WebContentsImpl::FromRenderFrameHostID(client_process_id, frame_id)
+          WebContentsImpl::FromRenderFrameHostID(client_render_frame_host_id)
               ->GetBrowserContext(),
-          client_process_id, frame_id)) {
+          client_render_frame_host_id.child_id,
+          client_render_frame_host_id.frame_routing_id)) {
     ScriptLoadFailed(std::move(client));
     return;
   }
@@ -152,7 +152,7 @@
       return;
     }
 
-    host->AddClient(std::move(client), client_process_id, frame_id,
+    host->AddClient(std::move(client), client_render_frame_host_id,
                     message_port);
     return;
   }
@@ -178,9 +178,9 @@
       info->content_security_policy_type, info->creation_address_space,
       creation_context_type);
   host = CreateWorker(instance, std::move(outside_fetch_client_settings_object),
-                      client_process_id, frame_id, storage_domain, message_port,
+                      client_render_frame_host_id, storage_domain, message_port,
                       std::move(blob_url_loader_factory));
-  host->AddClient(std::move(client), client_process_id, frame_id, message_port);
+  host->AddClient(std::move(client), client_render_frame_host_id, message_port);
 }
 
 void SharedWorkerServiceImpl::DestroyHost(SharedWorkerHost* host) {
@@ -206,10 +206,9 @@
 
 void SharedWorkerServiceImpl::NotifyClientAdded(
     const SharedWorkerInstance& instance,
-    int client_process_id,
-    int frame_id) {
+    GlobalFrameRoutingId client_render_frame_host_id) {
   auto insertion_result = shared_worker_client_counts_.insert(
-      {{instance, GlobalFrameRoutingId(client_process_id, frame_id)}, 0});
+      {{instance, client_render_frame_host_id}, 0});
 
   int& count = insertion_result.first->second;
   ++count;
@@ -218,16 +217,15 @@
   // shared worker.
   if (insertion_result.second) {
     for (Observer& observer : observers_)
-      observer.OnClientAdded(instance, client_process_id, frame_id);
+      observer.OnClientAdded(instance, client_render_frame_host_id);
   }
 }
 
 void SharedWorkerServiceImpl::NotifyClientRemoved(
     const SharedWorkerInstance& instance,
-    int client_process_id,
-    int frame_id) {
-  auto it = shared_worker_client_counts_.find(std::make_pair(
-      instance, GlobalFrameRoutingId(client_process_id, frame_id)));
+    GlobalFrameRoutingId client_render_frame_host_id) {
+  auto it = shared_worker_client_counts_.find(
+      std::make_pair(instance, client_render_frame_host_id));
   DCHECK(it != shared_worker_client_counts_.end());
 
   int& count = it->second;
@@ -239,7 +237,7 @@
   if (count == 0) {
     shared_worker_client_counts_.erase(it);
     for (Observer& observer : observers_)
-      observer.OnClientRemoved(instance, client_process_id, frame_id);
+      observer.OnClientRemoved(instance, client_render_frame_host_id);
   }
 }
 
@@ -247,8 +245,7 @@
     const SharedWorkerInstance& instance,
     blink::mojom::FetchClientSettingsObjectPtr
         outside_fetch_client_settings_object,
-    int creator_process_id,
-    int creator_frame_id,
+    GlobalFrameRoutingId creator_render_frame_host_id,
     const std::string& storage_domain,
     const blink::MessagePortChannel& message_port,
     scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
@@ -256,7 +253,8 @@
   DCHECK(!blob_url_loader_factory || instance.url().SchemeIsBlob());
 
   // Allocate the worker in the same process as the creator.
-  auto* worker_process_host = RenderProcessHost::FromID(creator_process_id);
+  auto* worker_process_host =
+      RenderProcessHost::FromID(creator_render_frame_host_id.child_id);
   DCHECK(!IsShuttingDown(worker_process_host));
 
   // Create the host. We need to do this even before starting the worker,
@@ -288,7 +286,7 @@
   const auto credentials_mode = network::mojom::CredentialsMode::kSameOrigin;
 
   RenderFrameHostImpl* creator_render_frame_host =
-      RenderFrameHostImpl::FromID(creator_process_id, creator_frame_id);
+      RenderFrameHostImpl::FromID(creator_render_frame_host_id);
   url::Origin origin(
       creator_render_frame_host->frame_tree_node()->current_origin());
 
diff --git a/content/browser/worker_host/shared_worker_service_impl.h b/content/browser/worker_host/shared_worker_service_impl.h
index 82169dd..0c5c8fa 100644
--- a/content/browser/worker_host/shared_worker_service_impl.h
+++ b/content/browser/worker_host/shared_worker_service_impl.h
@@ -63,8 +63,7 @@
 
   // Creates the worker if necessary or connects to an already existing worker.
   void ConnectToWorker(
-      int client_process_id,
-      int frame_id,
+      GlobalFrameRoutingId client_render_frame_host_id,
       blink::mojom::SharedWorkerInfoPtr info,
       blink::mojom::FetchClientSettingsObjectPtr
           outside_fetch_client_settings_object,
@@ -81,11 +80,9 @@
                            const base::UnguessableToken& dev_tools_token);
   void NotifyWorkerTerminating(const SharedWorkerInstance& instance);
   void NotifyClientAdded(const SharedWorkerInstance& instance,
-                         int client_process_id,
-                         int frame_id);
+                         GlobalFrameRoutingId render_frame_host_id);
   void NotifyClientRemoved(const SharedWorkerInstance& instance,
-                           int client_process_id,
-                           int frame_id);
+                           GlobalFrameRoutingId render_frame_host_id);
 
   StoragePartitionImpl* storage_partition() { return storage_partition_; }
 
@@ -100,8 +97,7 @@
       const SharedWorkerInstance& instance,
       blink::mojom::FetchClientSettingsObjectPtr
           outside_fetch_client_settings_object,
-      int creator_process_id,
-      int creator_frame_id,
+      GlobalFrameRoutingId creator_render_frame_host_id,
       const std::string& storage_domain,
       const blink::MessagePortChannel& message_port,
       scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory);
diff --git a/content/browser/worker_host/shared_worker_service_impl_unittest.cc b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
index bdd87c9..7354ff7 100644
--- a/content/browser/worker_host/shared_worker_service_impl_unittest.cc
+++ b/content/browser/worker_host/shared_worker_service_impl_unittest.cc
@@ -84,10 +84,9 @@
 class SharedWorkerServiceImplTest : public RenderViewHostImplTestHarness {
  public:
   mojo::Remote<blink::mojom::SharedWorkerConnector> MakeSharedWorkerConnector(
-      RenderProcessHost* process_host,
-      int frame_id) {
+      GlobalFrameRoutingId render_frame_host_id) {
     mojo::Remote<blink::mojom::SharedWorkerConnector> connector;
-    SharedWorkerConnectorImpl::Create(process_host->GetID(), frame_id,
+    SharedWorkerConnectorImpl::Create(render_frame_host_id,
                                       connector.BindNewPipeAndPassReceiver());
     return connector;
   }
@@ -215,9 +214,9 @@
   MockSharedWorkerClient client;
   MessagePortChannel local_port;
   const GURL kUrl("http://example.com/w.js");
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host, render_frame_host->GetRoutingID()),
-                        kUrl, "name", &client, &local_port);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host->GetGlobalFrameRoutingId()),
+      kUrl, "name", &client, &local_port);
 
   mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver =
       WaitForFactoryReceiver(process_id);
@@ -312,9 +311,9 @@
   MockSharedWorkerClient client;
   MessagePortChannel local_port;
   const GURL kUrl("http://example.com/w.js");
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host, render_frame_host->GetRoutingID()),
-                        kUrl, "name", &client, &local_port);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host->GetGlobalFrameRoutingId()),
+      kUrl, "name", &client, &local_port);
 
   // Now asynchronously destroy |web_contents| so that the startup sequence at
   // least reaches SharedWorkerServiceImpl::StartWorker().
@@ -345,9 +344,9 @@
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
   const GURL kUrl("http://example.com/w.js");
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kUrl, "name", &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kUrl, "name", &client0, &local_port0);
 
   base::RunLoop().RunUntilIdle();
 
@@ -424,9 +423,9 @@
 
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kUrl, "name", &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kUrl, "name", &client1, &local_port1);
 
   base::RunLoop().RunUntilIdle();
 
@@ -504,9 +503,9 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kUrl, kName, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client0, &local_port0);
   base::RunLoop().RunUntilIdle();
 
   mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver =
@@ -529,9 +528,9 @@
 
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kUrl, kName, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client1, &local_port1);
   base::RunLoop().RunUntilIdle();
 
   EXPECT_TRUE(CheckNotReceivedFactoryReceiver(process_id1));
@@ -579,9 +578,9 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kUrl0, kName, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kUrl0, kName, &client0, &local_port0);
   base::RunLoop().RunUntilIdle();
 
   mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver0 =
@@ -604,9 +603,9 @@
 
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kUrl1, kName, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kUrl1, kName, &client1, &local_port1);
   base::RunLoop().RunUntilIdle();
 
   mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver1 =
@@ -666,9 +665,9 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kUrl, kName0, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kUrl, kName0, &client0, &local_port0);
   base::RunLoop().RunUntilIdle();
 
   mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver0 =
@@ -691,9 +690,9 @@
 
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kUrl, kName1, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kUrl, kName1, &client1, &local_port1);
   base::RunLoop().RunUntilIdle();
 
   mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver1 =
@@ -752,15 +751,15 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kUrl, kName, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client0, &local_port0);
 
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kUrl, kName, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client1, &local_port1);
 
   base::RunLoop().RunUntilIdle();
 
@@ -828,15 +827,15 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kUrl0, kName, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kUrl0, kName, &client0, &local_port0);
 
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kUrl1, kName, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kUrl1, kName, &client1, &local_port1);
 
   base::RunLoop().RunUntilIdle();
 
@@ -919,15 +918,15 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kUrl, kName0, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kUrl, kName0, &client0, &local_port0);
 
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kUrl, kName1, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kUrl, kName1, &client1, &local_port1);
 
   base::RunLoop().RunUntilIdle();
 
@@ -1017,9 +1016,9 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kUrl, kName, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client0, &local_port0);
 
   base::RunLoop().RunUntilIdle();
 
@@ -1049,9 +1048,9 @@
   // Start a new client, attemping to connect to the same worker.
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kUrl, kName, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client1, &local_port1);
 
   base::RunLoop().RunUntilIdle();
 
@@ -1079,9 +1078,9 @@
   // Start another client to confirm that it can connect to the same worker.
   MockSharedWorkerClient client2;
   MessagePortChannel local_port2;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host2, render_frame_host2->GetRoutingID()),
-                        kUrl, kName, &client2, &local_port2);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host2->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client2, &local_port2);
 
   base::RunLoop().RunUntilIdle();
 
@@ -1133,9 +1132,9 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kUrl, kName, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client0, &local_port0);
 
   mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver0 =
       WaitForFactoryReceiver(process_id0);
@@ -1147,9 +1146,9 @@
   // Start a new client, attempting to connect to the same worker.
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kUrl, kName, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client1, &local_port1);
 
   base::RunLoop().RunUntilIdle();
 
@@ -1179,9 +1178,9 @@
   // Start another client to confirm that it can connect to the same worker.
   MockSharedWorkerClient client2;
   MessagePortChannel local_port2;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host2, render_frame_host2->GetRoutingID()),
-                        kUrl, kName, &client2, &local_port2);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host2->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client2, &local_port2);
 
   base::RunLoop().RunUntilIdle();
 
@@ -1226,15 +1225,15 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host0, render_frame_host0->GetRoutingID()),
-                        kURL, kName, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host0->GetGlobalFrameRoutingId()),
+      kURL, kName, &client0, &local_port0);
 
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host1, render_frame_host1->GetRoutingID()),
-                        kURL, kName, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host1->GetGlobalFrameRoutingId()),
+      kURL, kName, &client1, &local_port1);
   base::RunLoop().RunUntilIdle();
 
   // Expect a factory receiver. It can come from either process.
@@ -1283,21 +1282,21 @@
   void OnBeforeWorkerTerminated(const SharedWorkerInstance& instance) override {
     EXPECT_EQ(1u, running_workers_.erase(instance));
   }
-  void OnClientAdded(const SharedWorkerInstance& instance,
-                     int client_process_id,
-                     int frame_id) override {
+  void OnClientAdded(
+      const SharedWorkerInstance& instance,
+      GlobalFrameRoutingId client_render_frame_host_id) override {
     auto it = running_workers_.find(instance);
     EXPECT_TRUE(it != running_workers_.end());
-    std::set<ClientInfo>& clients = it->second;
-    EXPECT_TRUE(clients.insert({client_process_id, frame_id}).second);
+    std::set<GlobalFrameRoutingId>& clients = it->second;
+    EXPECT_TRUE(clients.insert(client_render_frame_host_id).second);
   }
-  void OnClientRemoved(const SharedWorkerInstance& instance,
-                       int client_process_id,
-                       int frame_id) override {
+  void OnClientRemoved(
+      const SharedWorkerInstance& instance,
+      GlobalFrameRoutingId client_render_frame_host_id) override {
     auto it = running_workers_.find(instance);
     EXPECT_TRUE(it != running_workers_.end());
-    std::set<ClientInfo>& clients = it->second;
-    EXPECT_EQ(1u, clients.erase({client_process_id, frame_id}));
+    std::set<GlobalFrameRoutingId>& clients = it->second;
+    EXPECT_EQ(1u, clients.erase(client_render_frame_host_id));
   }
 
   size_t GetWorkerCount() { return running_workers_.size(); }
@@ -1310,9 +1309,8 @@
   }
 
  private:
-  using ClientInfo = std::pair<int, int>;
-
-  base::flat_map<SharedWorkerInstance, std::set<ClientInfo>> running_workers_;
+  base::flat_map<SharedWorkerInstance, std::set<GlobalFrameRoutingId>>
+      running_workers_;
 
   DISALLOW_COPY_AND_ASSIGN(TestSharedWorkerServiceObserver);
 };
@@ -1339,9 +1337,9 @@
   MockSharedWorkerClient client;
   MessagePortChannel local_port;
   const GURL kUrl("http://example.com/w.js");
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host, render_frame_host->GetRoutingID()),
-                        kUrl, "name", &client, &local_port);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host->GetGlobalFrameRoutingId()),
+      kUrl, "name", &client, &local_port);
 
   mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver =
       WaitForFactoryReceiver(process_id);
@@ -1400,9 +1398,9 @@
 
   MockSharedWorkerClient client0;
   MessagePortChannel local_port0;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host, render_frame_host->GetRoutingID()),
-                        kUrl, kName, &client0, &local_port0);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client0, &local_port0);
   base::RunLoop().RunUntilIdle();
 
   mojo::PendingReceiver<blink::mojom::SharedWorkerFactory> factory_receiver =
@@ -1428,9 +1426,9 @@
   // Now the same frame connects to the same worker.
   MockSharedWorkerClient client1;
   MessagePortChannel local_port1;
-  ConnectToSharedWorker(MakeSharedWorkerConnector(
-                            renderer_host, render_frame_host->GetRoutingID()),
-                        kUrl, kName, &client1, &local_port1);
+  ConnectToSharedWorker(
+      MakeSharedWorkerConnector(render_frame_host->GetGlobalFrameRoutingId()),
+      kUrl, kName, &client1, &local_port1);
   base::RunLoop().RunUntilIdle();
 
   EXPECT_TRUE(CheckNotReceivedFactoryReceiver(process_id));
diff --git a/content/child/child_thread_impl.cc b/content/child/child_thread_impl.cc
index 3a3e67f..aee66825 100644
--- a/content/child/child_thread_impl.cc
+++ b/content/child/child_thread_impl.cc
@@ -84,7 +84,7 @@
 #include "base/mac/mach_port_rendezvous.h"
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
 #include <stdio.h>
 #if defined(OS_WIN)
 #include <io.h>
@@ -373,7 +373,7 @@
                                   weak_main_thread_, std::move(receiver)));
   }
 
-#if BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
   void SetCoverageFile(base::File file) override {
     // TODO(crbug.com/985574) Remove Android check when possible.
 #if defined(OS_POSIX) && !defined(OS_ANDROID)
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 6a9c28a..7b3a449 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -429,7 +429,7 @@
     deps += [ "//third_party/fuchsia-sdk/sdk:fdio" ]
   }
 
-  if (use_clang_coverage) {
+  if (use_clang_coverage_inside_sandbox) {
     sources += [
       "coverage_utils.cc",
       "coverage_utils.h",
@@ -493,8 +493,8 @@
   if (is_linux || is_chromeos) {
     enabled_features += [ "supports_thread_priorities" ]
   }
-  if (use_clang_coverage) {
-    enabled_features += [ "clang_coverage" ]
+  if (use_clang_coverage_inside_sandbox) {
+    enabled_features += [ "clang_coverage_inside_sandbox" ]
   }
 
   import_dirs = [ "//mojo/services" ]
diff --git a/content/common/child_process.mojom b/content/common/child_process.mojom
index 34ab578..0c256c13 100644
--- a/content/common/child_process.mojom
+++ b/content/common/child_process.mojom
@@ -92,6 +92,6 @@
   BindReceiver(mojo_base.mojom.GenericPendingReceiver receiver);
 
   // Sets the coverage file for the child process.
-  [EnableIf=clang_coverage]
+  [EnableIf=clang_coverage_inside_sandbox]
   SetCoverageFile(mojo_base.mojom.File file);
 };
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc
index 5e397c7e..a72710b 100644
--- a/content/common/child_process_host_impl.cc
+++ b/content/common/child_process_host_impl.cc
@@ -41,7 +41,7 @@
 #include "content/common/mac_helpers.h"
 #endif  // OS_LINUX
 
-#if BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
 #include "content/common/coverage_utils.h"
 #endif
 
@@ -206,7 +206,7 @@
   child_process_->SetIPCLoggingEnabled(enabled);
 #endif
 
-#if BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
   child_process_->SetCoverageFile(OpenCoverageFile());
 #endif
 
diff --git a/content/common/media/media_player_delegate_messages.h b/content/common/media/media_player_delegate_messages.h
index c9fde77..368571a 100644
--- a/content/common/media/media_player_delegate_messages.h
+++ b/content/common/media/media_player_delegate_messages.h
@@ -68,6 +68,10 @@
                     int /* delegate_id, distinguishes instances */,
                     double /* is_persistent */)
 
+IPC_MESSAGE_ROUTED2(MediaPlayerDelegateMsg_NotifyPowerExperimentState,
+                    int /* delegate_id, distinguishes instances */,
+                    bool /* is experiment starting (true) or stopping? */)
+
 // ----------------------------------------------------------------------------
 // Messages from the renderer notifying the browser of playback state changes.
 // ----------------------------------------------------------------------------
diff --git a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
index 25cc907..2f920be 100644
--- a/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/selection/SelectionPopupControllerImpl.java
@@ -553,7 +553,7 @@
     @Override
     public void finishActionMode() {
         mHidden = false;
-        if (mView != null) mView.removeCallbacks(mRepeatingHideRunnable);
+        mHandler.removeCallbacks(mRepeatingHideRunnable);
 
         if (isActionModeValid()) {
             mActionMode.finish();
diff --git a/content/public/browser/hid_chooser.h b/content/public/browser/hid_chooser.h
index 24f8685..d40cf59 100644
--- a/content/public/browser/hid_chooser.h
+++ b/content/public/browser/hid_chooser.h
@@ -16,9 +16,10 @@
 // object should cancel the prompt.
 class CONTENT_EXPORT HidChooser {
  public:
-  // Callback type used to report the user action. Passed |nullptr| if no device
-  // was selected.
-  using Callback = base::OnceCallback<void(device::mojom::HidDeviceInfoPtr)>;
+  // Callback type used to report the user action. An empty vector is passed if
+  // no device was selected.
+  using Callback =
+      base::OnceCallback<void(std::vector<device::mojom::HidDeviceInfoPtr>)>;
 
   HidChooser() = default;
   virtual ~HidChooser() = default;
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 6ae023e..c9ce3ed 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -14,6 +14,7 @@
 #include "base/optional.h"
 #include "build/build_config.h"
 #include "content/common/content_export.h"
+#include "content/public/browser/global_routing_id.h"
 #include "content/public/common/browser_controls_state.h"
 #include "content/public/common/isolated_world_ids.h"
 #include "content/public/common/page_visibility_state.h"
@@ -81,6 +82,7 @@
 
   // Returns the RenderFrameHost given its ID and the ID of its render process.
   // Returns nullptr if the IDs do not correspond to a live RenderFrameHost.
+  static RenderFrameHost* FromID(GlobalFrameRoutingId id);
   static RenderFrameHost* FromID(int render_process_id, int render_frame_id);
 
   // Globally allows for injecting JavaScript into the main world. This feature
diff --git a/content/public/browser/shared_worker_service.h b/content/public/browser/shared_worker_service.h
index b7e064312..0a777ed 100644
--- a/content/public/browser/shared_worker_service.h
+++ b/content/public/browser/shared_worker_service.h
@@ -9,6 +9,7 @@
 
 #include "base/observer_list_types.h"
 #include "content/common/content_export.h"
+#include "content/public/browser/global_routing_id.h"
 
 class GURL;
 
@@ -46,12 +47,12 @@
     // Called when a frame starts/stop being a client of a shared worker. It is
     // guaranteed that OnWorkerStarted() is called before receiving these
     // notifications.
-    virtual void OnClientAdded(const SharedWorkerInstance& instance,
-                               int client_process_id,
-                               int frame_id) = 0;
-    virtual void OnClientRemoved(const SharedWorkerInstance& instance,
-                                 int client_process_id,
-                                 int frame_id) = 0;
+    virtual void OnClientAdded(
+        const SharedWorkerInstance& instance,
+        content::GlobalFrameRoutingId render_frame_host_id) = 0;
+    virtual void OnClientRemoved(
+        const SharedWorkerInstance& instance,
+        content::GlobalFrameRoutingId render_frame_host_id) = 0;
   };
 
   // Adds/removes an observer.
diff --git a/content/renderer/input/widget_input_handler_manager.cc b/content/renderer/input/widget_input_handler_manager.cc
index eafde289..5a603f4 100644
--- a/content/renderer/input/widget_input_handler_manager.cc
+++ b/content/renderer/input/widget_input_handler_manager.cc
@@ -163,7 +163,6 @@
   base::OnceClosure init_closure = base::BindOnce(
       &WidgetInputHandlerManager::InitOnInputHandlingThread, this,
       render_widget_->layer_tree_host()->GetInputHandler(),
-      render_widget_->compositor_deps()->IsScrollAnimatorEnabled(),
       sync_compositing);
   InputThreadTaskRunner()->PostTask(FROM_HERE, std::move(init_closure));
 }
@@ -515,7 +514,6 @@
 
 void WidgetInputHandlerManager::InitOnInputHandlingThread(
     const base::WeakPtr<cc::InputHandler>& input_handler,
-    bool smooth_scroll_enabled,
     bool sync_compositing) {
   DCHECK(InputThreadTaskRunner()->BelongsToCurrentThread());
 
@@ -531,8 +529,6 @@
   input_handler_proxy_ = std::make_unique<ui::InputHandlerProxy>(
       input_handler.get(), this, force_input_handling_on_main);
 
-  input_handler_proxy_->set_smooth_scroll_enabled(smooth_scroll_enabled);
-
 #if defined(OS_ANDROID)
   if (sync_compositing) {
     DCHECK(synchronous_compositor_registry_);
diff --git a/content/renderer/input/widget_input_handler_manager.h b/content/renderer/input/widget_input_handler_manager.h
index 5e5f20d3..4a403a0 100644
--- a/content/renderer/input/widget_input_handler_manager.h
+++ b/content/renderer/input/widget_input_handler_manager.h
@@ -151,7 +151,6 @@
   void InitInputHandler();
   void InitOnInputHandlingThread(
       const base::WeakPtr<cc::InputHandler>& input_handler,
-      bool smooth_scroll_enabled,
       bool sync_compositing);
   void BindAssociatedChannel(
       mojo::PendingAssociatedReceiver<mojom::WidgetInputHandler> receiver);
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.cc b/content/renderer/media/renderer_webmediaplayer_delegate.cc
index cbeb280..39904ad 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.cc
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.cc
@@ -237,6 +237,8 @@
                         OnMediaDelegateVolumeMultiplierUpdate)
     IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_BecamePersistentVideo,
                         OnMediaDelegateBecamePersistentVideo)
+    IPC_MESSAGE_HANDLER(MediaPlayerDelegateMsg_NotifyPowerExperimentState,
+                        OnMediaDelegatePowerExperimentState)
     IPC_MESSAGE_UNHANDLED(return false)
   IPC_END_MESSAGE_MAP()
   return true;
@@ -347,6 +349,14 @@
     observer->OnBecamePersistentVideo(value);
 }
 
+void RendererWebMediaPlayerDelegate::OnMediaDelegatePowerExperimentState(
+    int player_id,
+    bool state) {
+  Observer* observer = id_map_.Lookup(player_id);
+  if (observer)
+    observer->OnPowerExperimentState(state);
+}
+
 void RendererWebMediaPlayerDelegate::ScheduleUpdateTask() {
   if (!pending_update_task_) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
diff --git a/content/renderer/media/renderer_webmediaplayer_delegate.h b/content/renderer/media/renderer_webmediaplayer_delegate.h
index 8c4f71b2..7e5d1631 100644
--- a/content/renderer/media/renderer_webmediaplayer_delegate.h
+++ b/content/renderer/media/renderer_webmediaplayer_delegate.h
@@ -98,6 +98,7 @@
   void OnMediaDelegateSuspendAllMediaPlayers();
   void OnMediaDelegateVolumeMultiplierUpdate(int player_id, double multiplier);
   void OnMediaDelegateBecamePersistentVideo(int player_id, bool value);
+  void OnMediaDelegatePowerExperimentState(int player_id, bool state);
 
   // Schedules UpdateTask() to run soon.
   void ScheduleUpdateTask();
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index c20b747..1659f93 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -1077,11 +1077,12 @@
       web_frame->FirstChild()->NextSibling()->GetSecurityOrigin().IsOpaque());
 }
 
-// When we enable --use-zoom-for-dsf, visiting the first web page after opening
-// a new tab looks fine, but visiting the second web page renders smaller DOM
-// elements. We can solve this by updating DSF after swapping in the main frame.
+// Test that when navigating cross-origin, which creates a new main frame
+// RenderWidget, that the device scale is set correctly for that RenderWidget
+// the WebView and frames.
 // See crbug.com/737777#c37.
-TEST_F(RenderViewImplEnableZoomForDSFTest, UpdateDSFAfterSwapIn) {
+TEST_F(RenderViewImplEnableZoomForDSFTest,
+       DeviceScaleCorrectAfterCrossOriginNav) {
   const float device_scale = 3.0f;
   SetDeviceScaleFactor(device_scale);
   EXPECT_EQ(device_scale, view()->GetMainRenderFrame()->GetDeviceScaleFactor());
@@ -1119,8 +1120,9 @@
       routing_id, std::move(stub_interface_provider),
       std::move(stub_browser_interface_broker), kProxyRoutingId,
       MSG_ROUTING_NONE, MSG_ROUTING_NONE, MSG_ROUTING_NONE,
-      base::UnguessableToken::Create(), replication_state, nullptr,
-      &widget_params, FrameOwnerProperties(), /*has_committed_real_load=*/true);
+      base::UnguessableToken::Create(), replication_state,
+      compositor_deps_.get(), &widget_params, FrameOwnerProperties(),
+      /*has_committed_real_load=*/true);
   TestRenderFrame* provisional_frame =
       static_cast<TestRenderFrame*>(RenderFrameImpl::FromRoutingID(routing_id));
   EXPECT_TRUE(provisional_frame);
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 1fe51f1..af8dfe8 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1844,7 +1844,7 @@
   // There is a WebFrameWidget previously attached by AttachWebFrameWidget().
   DCHECK(render_widget_->GetWebWidget());
 
-  if (destroying_) {
+  if (true || destroying_) {
     // We are inside RenderViewImpl::Destroy() and the main frame is being
     // detached as part of shutdown. So we can destroy the RenderWidget.
 
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index c8ed193e..b1c61a3e 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -454,6 +454,7 @@
       widget_receiver_(this, std::move(widget_receiver)) {
   DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
   DCHECK(RenderThread::IsMainThread());
+  DCHECK(compositor_deps_);
 
   // In tests there may not be a RenderThreadImpl.
   if (RenderThreadImpl::current()) {
@@ -3086,6 +3087,8 @@
   settings.commit_fractional_scroll_deltas =
       blink::WebRuntimeFeatures::IsFractionalScrollOffsetsEnabled();
 
+  settings.enable_smooth_scroll = compositor_deps->IsScrollAnimatorEnabled();
+
   // The means the renderer compositor has 2 possible modes:
   // - Threaded compositing with a scheduler.
   // - Single threaded compositing without a scheduler (for web tests only).
diff --git a/content/test/data/gpu/pixel_paintWorklet_basics.html b/content/test/data/gpu/pixel_paintWorklet_basics.html
new file mode 100644
index 0000000..ffd70e0
--- /dev/null
+++ b/content/test/data/gpu/pixel_paintWorklet_basics.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+
+<!-- READ BEFORE UPDATING:
+If this test is updated make sure to increment the "revision" value of the
+associated test in content/test/gpu/gpu_tests/pixel_test_pages.py. This will ensure
+that the baseline images are regenerated on the next run.
+-->
+
+<html>
+<head>
+<title>PaintWorklet with basic draw and transform operations</title>
+<style type="text/css">
+.nomargin {
+  margin: 0px auto;
+  width:300px;
+  height:300px;
+  background-image:paint(basic);
+}
+</style>
+
+<script id="code" type="text/worklet">
+registerPaint('basic', class {
+    paint(ctx, geom) {
+        ctx.fillStyle = 'green';
+        ctx.transform(1, 0.5, 0, 1, 20, 20);
+        ctx.fillRect(0, 0, 200, 200);
+    }
+});
+</script>
+
+<script>
+var g_swapsBeforeAck = 15;
+
+function main()
+{
+  var code = document.getElementById('code').textContent;
+  var blob = new Blob([code], {type: 'text/javascript'});
+  CSS.paintWorklet.addModule(URL.createObjectURL(blob)).then(function() {
+  });
+  waitForFinish();
+}
+
+function waitForFinish()
+{
+  // This tests need paint worklet implemented.
+  if (g_swapsBeforeAck == 0 || typeof CSS.paintWorklet === 'undefined') {
+    domAutomationController.send("SUCCESS");
+  } else {
+    g_swapsBeforeAck--;
+    document.getElementById('container').style.zIndex = g_swapsBeforeAck + 1;
+    window.requestAnimationFrame(waitForFinish);
+  }
+}
+</script>
+</head>
+<body onload="main()">
+<div style="position:relative; width:300px; height:300px; background-color:white">
+</div>
+<div id="container" style="position:absolute; top:0px; left:0px">
+  <div id="target" class="nomargin"></div>
+</div>
+</body>
+</html>
diff --git a/content/test/gpu/gpu_tests/pixel_integration_test.py b/content/test/gpu/gpu_tests/pixel_integration_test.py
index a0231f9..56438d6e 100644
--- a/content/test/gpu/gpu_tests/pixel_integration_test.py
+++ b/content/test/gpu/gpu_tests/pixel_integration_test.py
@@ -58,6 +58,7 @@
     pages = namespace.DefaultPages(cls.test_base_name)
     pages += namespace.GpuRasterizationPages(cls.test_base_name)
     pages += namespace.ExperimentalCanvasFeaturesPages(cls.test_base_name)
+    pages += namespace.PaintWorkletPages(cls.test_base_name)
     # pages += namespace.NoGpuProcessPages(cls.test_base_name)
     # The following pages should run only on platforms where SwiftShader is
     # enabled. They are skipped on other platforms through test expectations.
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index 703d163..01271d4 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -788,6 +788,22 @@
         browser_args=browser_args)
     ]
 
+  # Pages that should be run with off-thread paint worklet flags.
+  @staticmethod
+  def PaintWorkletPages(base_name):
+    browser_args = [
+      '--enable-blink-features=OffMainThreadCSSPaint',
+      '--enable-gpu-rasterization',
+      '--enable-oop-rasterization']
+
+    return [
+      PixelTestPage(
+        'pixel_paintWorklet_basics.html',
+        base_name + '_PaintWorkletBasics',
+        test_rect=[0, 0, 300, 300],
+        browser_args=browser_args),
+    ]
+
   # Pages that should be run with experimental canvas features.
   @staticmethod
   def ExperimentalCanvasFeaturesPages(base_name):
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index cd8ac015..46f37fe8 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -71,10 +71,6 @@
 # Produces black images on Nexus 5Xs in Android Webview (qualcomm-adreno-(tm)-418) crbug.com/984352
 # Fails on Pixel 2 (qualcomm-adreno-(tm)-540) crbug.com/966069
 crbug.com/805739 [ android ] Pixel_CanvasLowLatency2D [ Skip ]
-crbug.com/1039232 [ android android-chromium qualcomm-adreno-(tm)-540 use-vulkan skia-renderer ] Pixel_CanvasLowLatencyWebGL [ Failure ]
-crbug.com/1039232 [ android android-chromium qualcomm-adreno-(tm)-540 use-vulkan skia-renderer ] Pixel_OffscreenCanvasWebGLPaintAfterResize [ Failure ]
-crbug.com/1039232 [ android android-chromium qualcomm-adreno-(tm)-540 use-vulkan skia-renderer ] Pixel_OffscreenCanvasWebGLDefaultWorker [ Failure ]
-crbug.com/1039232 [ android android-chromium qualcomm-adreno-(tm)-540 use-vulkan skia-renderer ] Pixel_RepeatedWebGLTo2D [ Failure ]
 
 # Skip test that kills GPU process since Android Webview only supports
 # in-process GPU.
@@ -199,6 +195,9 @@
 # Produces blank images on Intel HD 630 w/ Mesa 19.0.2
 crbug.com/976861 [ linux intel-0x5912 ] Pixel_OffscreenCanvasTransferToImageBitmap [ Skip ]
 
+# Flakes regularly on Mac.
+crbug.com/1040202 [ mac ] Pixel_CSSFilterEffects_NoOverlays [ Failure ]
+
 # Skip swap chain tests on non-Windows
 [ android ] Pixel_CanvasLowLatency2DSwapChain [ Skip ]
 [ android ] Pixel_CanvasLowLatencyWebGLSwapChain [ Skip ]
diff --git a/device/vr/openxr/openxr_interaction_profiles.h b/device/vr/openxr/openxr_interaction_profiles.h
index 8220865..eab6eff1 100644
--- a/device/vr/openxr/openxr_interaction_profiles.h
+++ b/device/vr/openxr/openxr_interaction_profiles.h
@@ -24,7 +24,8 @@
   kKHRSimple = 1,
   kOculusTouch = 2,
   kValveIndex = 3,
-  kCount = 4,
+  kHTCVive = 4,
+  kCount = 5,
 };
 
 enum class OpenXrButtonType {
@@ -87,6 +88,7 @@
 // Khronos simple controller.
 // Oculus touch controller.
 // Valve index controller.
+// HTC vive controller
 // Declare OpenXR input profile bindings for other runtimes when they become
 // available.
 constexpr const char* kMicrosoftMotionInputProfiles[] = {
@@ -100,6 +102,9 @@
 constexpr const char* kValveIndexInputProfiles[] = {
     "valve-index", "generic-trigger-squeeze-touchpad-thumbstick"};
 
+constexpr const char* kHTCViveInputProfiles[] = {
+    "htc-vive", "generic-trigger-squeeze-touchpad"};
+
 constexpr OpenXrButtonPathMap kMicrosoftMotionControllerButtonPathMaps[] = {
     {OpenXrButtonType::kTrigger,
      {
@@ -207,6 +212,21 @@
      2},
 };  // namespace device
 
+constexpr OpenXrButtonPathMap kHTCViveControllerButtonPathMaps[] = {
+    {OpenXrButtonType::kTrigger,
+     {
+         {OpenXrButtonActionType::kPress, "/input/trigger/click"},
+         {OpenXrButtonActionType::kValue, "/input/trigger/value"},
+     },
+     2},
+    {OpenXrButtonType::kSqueeze,
+     {{OpenXrButtonActionType::kPress, "/input/squeeze/click"}},
+     1},
+    {OpenXrButtonType::kTrackpad,
+     {{OpenXrButtonActionType::kPress, "/input/trackpad/click"},
+      {OpenXrButtonActionType::kTouch, "/input/trackpad/touch"}},
+     2}};
+
 constexpr OpenXrAxisPathMap kMicrosoftMotionControllerAxisPathMaps[] = {
     {OpenXrAxisType::kTrackpad, "/input/trackpad"},
     {OpenXrAxisType::kThumbstick, "/input/thumbstick"},
@@ -221,6 +241,10 @@
     {OpenXrAxisType::kThumbstick, "/input/thumbstick"},
 };
 
+constexpr OpenXrAxisPathMap kHTCViveControllerAxisPathMaps[] = {
+    {OpenXrAxisType::kTrackpad, "/input/trackpad"},
+};
+
 constexpr OpenXrControllerInteractionProfile
     kMicrosoftMotionInteractionProfile = {
         OpenXrInteractionProfileType::kMicrosoftMotion,
@@ -274,10 +298,24 @@
     kValveIndexControllerAxisPathMaps,
     base::size(kValveIndexControllerAxisPathMaps)};
 
+constexpr OpenXrControllerInteractionProfile kHTCViveInteractionProfile = {
+    OpenXrInteractionProfileType::kHTCVive,
+    "/interaction_profiles/htc/vive_controller",
+    GamepadMapping::kXrStandard,
+    kHTCViveInputProfiles,
+    base::size(kHTCViveInputProfiles),
+    kHTCViveControllerButtonPathMaps,
+    base::size(kHTCViveControllerButtonPathMaps),
+    kHTCViveControllerButtonPathMaps,
+    base::size(kHTCViveControllerButtonPathMaps),
+    kHTCViveControllerAxisPathMaps,
+    base::size(kHTCViveControllerAxisPathMaps)};
+
 constexpr OpenXrControllerInteractionProfile
     kOpenXrControllerInteractionProfiles[] = {
         kMicrosoftMotionInteractionProfile, kKHRSimpleInteractionProfile,
-        kOculusTouchInteractionProfile, kValveIndexInteractionProfile};
+        kOculusTouchInteractionProfile, kValveIndexInteractionProfile,
+        kHTCViveInteractionProfile};
 
 }  // namespace device
 
diff --git a/device/vr/openxr/test/fake_openxr_impl_api.cc b/device/vr/openxr/test/fake_openxr_impl_api.cc
index 68fd418..b785169 100644
--- a/device/vr/openxr/test/fake_openxr_impl_api.cc
+++ b/device/vr/openxr/test/fake_openxr_impl_api.cc
@@ -780,21 +780,7 @@
       g_test_helper.ValidatePath(suggested_bindings->interactionProfile));
   std::string interaction_profile =
       g_test_helper.PathToString(suggested_bindings->interactionProfile);
-  RETURN_IF(
-      interaction_profile.compare(
-          interaction_profile::kMicrosoftMotionControllerInteractionProfile) !=
-              0 &&
-          interaction_profile.compare(
-              interaction_profile::kKHRSimpleControllerInteractionProfile) !=
-              0 &&
-          interaction_profile.compare(
-              interaction_profile::kOculusTouchControllerInteractionProfile) !=
-              0 &&
-          interaction_profile.compare(
-              interaction_profile::kValveIndexControllerinteractionProfile) !=
-              0,
-      XR_ERROR_VALIDATION_FAILURE,
-      "xrSetInteractionProfileSuggestedBindings invalid interaction_profile");
+
   RETURN_IF(suggested_bindings->suggestedBindings == nullptr,
             XR_ERROR_VALIDATION_FAILURE,
             "XrInteractionProfileSuggestedBinding has nullptr "
diff --git a/device/vr/openxr/test/openxr_test_helper.h b/device/vr/openxr/test/openxr_test_helper.h
index e3c5a9a..50c4042 100644
--- a/device/vr/openxr/test/openxr_test_helper.h
+++ b/device/vr/openxr/test/openxr_test_helper.h
@@ -36,8 +36,6 @@
 constexpr char kOculusTouchControllerInteractionProfile[] =
     "/interaction_profiles/oculus/touch_controller";
 
-constexpr char kValveIndexControllerinteractionProfile[] =
-    "/interaction_profiles/valve/index_controller";
 }  // namespace interaction_profile
 
 class OpenXrTestHelper : public device::ServiceTestHook {
diff --git a/docs/android_dynamic_feature_modules.md b/docs/android_dynamic_feature_modules.md
index 3e4bf490..f0f335e 100644
--- a/docs/android_dynamic_feature_modules.md
+++ b/docs/android_dynamic_feature_modules.md
@@ -29,6 +29,13 @@
 `YOUR_FEATURE_NAME`.
 ***
 
+### Reference DFM
+
+In addition to this guide, the
+[Test Dummy](https://cs.chromium.org/chromium/src/chrome/android/modules/test_dummy/test_dummy_module.gni)
+module serves as an actively-maintained reference DFM. Test Dummy is used in
+automated bundle testing, and covers both Java and native code and resource
+usage.
 
 ### Create DFM target
 
@@ -300,7 +307,7 @@
 module (`-m foo`) "`bar in module`" or "`module not installed`" is printed to
 logcat. Yay!
 
-### Adding third-party native code
+### Adding pre-built native libraries
 
 You can add a third-party native library (or any standalone library that doesn't
 depend on Chrome code) by adding it as a loadable module to the module descriptor in
@@ -316,201 +323,21 @@
 
 ### Adding Chrome native code
 
-Chrome native code may be placed in a DFM.
+Chrome native code may be placed in a DFM. The easiest way to access native
+feature code is by calling it from Java via JNI. When a module is first
+accessed, its native library (or potentially libraries, if using a component
+build), are automatically opened by the DFM framework, and a feature-specific
+JNI method (supplied by the feature's implementation) is invoked. Hence, a
+module's Java code may freely use JNI to call module native code.
 
-A linker-assisted partitioning system automates the placement of code into
-either the main Chrome library or feature-specific .so libraries. Feature code
-may continue to make use of core Chrome code (eg. base::) without modification,
-but Chrome must call feature code through a virtual interface.
+Using the module framework and JNI to access the native code eliminates concerns
+with DFM library file names (which vary across build variants),
+`android_dlopen_ext()` (needed to open feature libraries), and use of dlsym().
 
-Partitioning is explained in [Android Native
-Libraries](android_native_libraries.md#partitioned-libraries).
-
-#### Creating an interface to feature code
-
-One way of creating an interface to a feature library is through an interface
-definition. Feature Foo could define the following in
-`//chrome/android/features/foo/public/foo_interface.h`:
-
-```c++
-class FooInterface {
- public:
-  virtual ~FooInterface() = default;
-
-  virtual void ProcessInput(const std::string& input) = 0;
-}
-```
-
-Alongside the interface definition, also in
-`//chrome/android/features/foo/public/foo_interface.h`, it's helpful to define a
-factory function type that can be used to create a Foo instance:
-
-```c++
-typedef FooInterface* CreateFooFunction(bool arg1, bool arg2);
-```
-
-<!--- TODO(cjgrant): Add a full, pastable Foo implementation. -->
-The feature library implements class Foo, hiding its implementation within the
-library. The library may then expose a single entrypoint, a Foo factory
-function. Here, C naming is (optionally) used so that the entrypoint symbol
-isn't mangled. In `//chrome/android/features/foo/internal/foo.cc`:
-
-```c++
-extern "C" {
-// This symbol is retrieved from the Foo feature module library via dlsym(),
-// where it's bare address is type-cast to its actual type and executed.
-// The forward declaration here ensures that CreateFoo()'s signature is correct.
-CreateFooFunction CreateFoo;
-
-__attribute__((visibility("default"))) FooInterface* CreateFoo(
-    bool arg1, bool arg2) {
-  return new Foo(arg1, arg2);
-}
-}  // extern "C"
-```
-
-Ideally, the interface to the feature will avoid feature-specific types. If a
-feature defines complex data types, and uses them in its own interface, then its
-likely the main library will utilize the code backing these types. That code,
-and anything it references, will in turn be pulled back into the main library.
-
-Therefore, designing the feature inferface to use C types, C++ standard types,
-or classes that aren't expected to move out of Chrome's main library is ideal.
-If feature-specific classes are needed, they simply need to avoid referencing
-feature library internals.
-
-*** note
-**Note:** To help enforce separation between the feature interface and
-implementation, the interface class is best placed in its own GN target, on
-which the feature and main library code both depend.
-***
-
-#### Marking feature entrypoints
-
-Foo's feature module descriptor needs to pull in the appropriate native GN code
-dependencies, and also indicate the name of the file that lists the entrypoint
-symbols. In `//chrome/android/features/foo/foo_module.gni`:
-
-```gn
-foo_module_desc = {
-  ...
-  native_deps = [ "//chrome/android/features/foo/internal:foo" ]
-  native_entrypoints = "//chrome/android/features/foo/internal/module_entrypoints.lst"
-}
-```
-
-The module entrypoint file is a text file listing symbols. In this example,
-`//chrome/android/features/foo/internal/module_entrypoints.lst` has only a
-single factory function exposed:
-
-```shell
-# This file lists entrypoints exported from the Foo native feature library.
-
-CreateFoo
-```
-
-These symbols will be pulled into a version script for the linker, indicating
-that they should be exported in the dynamic symbol table of the feature library.
-
-*** note
-**Note:** If C++ symbol names are chosen as entrypoints, the full mangled names
-must be listed.
-***
-
-Additionally, it's necessary to map entrypoints to a particular partition. To
-follow compiler/linker convention, this is done at the compiler stage. A cflag
-is applied to source file(s) that may supply entrypoints (it's okay to apply the
-flag to all feature source - the attribute is utilized only on modules that
-export symbols). In `//chrome/android/features/foo/internal/BUILD.gn`:
-
-```gn
-static_library("foo") {
-  sources = [
-    ...
-  ]
-
-  # Mark symbols in this target as belonging to the Foo library partition. Only
-  # exported symbols (entrypoints) are affected, and only if this build supports
-  # native modules.
-  if (use_native_modules) {
-    cflags = [ "-fsymbol-partition=libfoo.so" ]
-  }
-}
-```
-
-Feature code is free to use any existing Chrome code (eg. logging, base::,
-skia::, cc::, etc), as well as other feature targets. From a GN build config
-perspective, the dependencies are defined as they normally would. The
-partitioning operation works independently of GN's dependency tree.
-
-```gn
-static_library("foo") {
-  ...
-
-  # It's fine to depend on base:: and other Chrome code.
-  deps = [
-    "//base",
-    "//cc/animation",
-    ...
-  ]
-
-  # Also fine to depend on other feature sub-targets.
-  deps += [
-    ":some_other_foo_target"
-  ]
-
-  # And fine to depend on the interface.
-  deps += [
-    ":foo_interface"
-  ]
-}
-```
-
-#### Opening the feature library
-
-Now, code in the main library can open the feature library and create an
-instance of feature Foo. Note that in this example, no care is taken to scope
-the lifetime of the opened library. Depending on the feature, it may be
-preferable to open and close the library as a feature is used.
-`//chrome/android/features/foo/factory/foo_factory.cc` may contain this:
-
-```c++
-std::unique_ptr<FooInterface> FooFactory(bool arg1, bool arg2) {
-  // Open the feature library, using the partition library helper to map it into
-  // the correct memory location. Specifying partition name *foo* will open
-  // libfoo.so.
-  void* foo_library_handle =
-        base::android::BundleUtils::DlOpenModuleLibraryPartition("foo");
-  }
-  DCHECK(foo_library_handle != nullptr) << "Could not open foo library:"
-      << dlerror();
-
-  // Pull the Foo factory function out of the library. The function name isn't
-  // mangled because it was extern "C".
-  CreateFooFunction* create_foo = reinterpret_cast<CreateFooFunction*>(
-      dlsym(foo_library_handle, "CreateFoo"));
-  DCHECK(create_foo != nullptr);
-
-  // Make and return a Foo!
-  return base::WrapUnique(create_foo(arg1, arg2));
-}
-
-```
-
-*** note
-**Note:** Component builds do not support partitioned libraries (code splitting
-happens across component boundaries instead). As such, an alternate, simplified
-feature factory implementation must be supplied (either by linking in a
-different factory source file, or using #defines in the factory) that simply
-instantiates a Foo object directly.
-***
-
-Finally, the main library is free to utilize Foo:
-
-```c++
-  auto foo = FooFactory::Create(arg1, arg2);
-  foo->ProcessInput(const std::string& input);
-```
+This mechanism can be extended if necessary by DFM implementers to facilitate
+subsequent native-native calls, by having a JNI-called initialization method
+create instance of a object or factory, and register it through a call to the
+base module's native code (DFM native code can call base module code directly).
 
 #### JNI
 
@@ -522,12 +349,183 @@
 * Generated wrapper `ClassNameJni` classes are packaged into the DFM's dex file
 * The class containing the actual native definitions, `GEN_JNI.java`, is always
   stored in the base module
-* If the DFM is only included in bundles that use
-  [implicit JNI registration](android_native_libraries.md#JNI-Native-Methods-Resolution)
-  (i.e. Monochrome and newer), then no extra consideration is necessary
+* If the DFM is only included in bundles that use [implicit JNI
+  registration](android_native_libraries.md#JNI-Native-Methods-Resolution) (i.e.
+  Monochrome and newer), then no extra consideration is necessary
 * Otherwise, the DFM will need to provide a `generate_jni_registration` target
   that will generate all of the native registration functions
 
+#### Calling DFM native code via JNI
+
+A linker-assisted partitioning system automates the placement of code into
+either the main Chrome library or feature-specific .so libraries. Feature code
+may continue to make use of core Chrome code (eg. base::) without modification,
+but Chrome must call feature code through a virtual interface (any "direct"
+calls to the feature code from the main library will cause the feature code to
+be pulled back into the main library).
+
+Partitioning is explained in [Android Native
+Libraries](android_native_libraries.md#partitioned-libraries).
+
+First, build a module native interface. Supply a JNI method named
+`JNI_OnLoad_foo` for the module framework to call, in
+`//chrome/android/modules/foo/internal/entrypoints.cc`. This method is invoked
+on all Chrome build variants, including Monochrome (unlike base module JNI).
+
+```c++
+#include "base/android/jni_generator/jni_generator_helper.h"
+#include "base/android/jni_utils.h"
+#include "chrome/android/modules/foo/internal/jni_registration.h"
+
+extern "C" {
+// This JNI registration method is found and called by module framework code.
+JNI_GENERATOR_EXPORT bool JNI_OnLoad_foo(JNIEnv* env) {
+  if (!base::android::IsSelectiveJniRegistrationEnabled(env) &&
+      !foo::RegisterNonMainDexNatives(env)) {
+    return false;
+  }
+  if (!foo::RegisterMainDexNatives(env)) {
+    return false;
+  }
+  return true;
+}
+}  // extern "C"
+```
+
+Next, include the module entrypoint and related pieces in the build config at
+`//chrome/android/modules/foo/internal/BUILD.gn`:
+
+```gn
+import("//build/config/android/rules.gni")
+import("//chrome/android/modules/buildflags.gni")
+...
+
+# Put the JNI entrypoint in a component, so that the component build has a
+# library to include in the foo module. This makes things feel consistent with
+# a release build.
+component("foo") {
+  sources = [
+    "entrypoints.cc",
+  ]
+  deps = [
+    ":jni_registration",
+    "//chrome/android/features/foo/internal:native",
+    "//base",
+  ]
+
+  # Instruct the compiler to flag exported entrypoint function as belonging in
+  # foo's library. The linker will use this information when creating the
+  # native libraries. The partition name must be <feature>_partition.
+  if (use_native_partitions) {
+    cflags = [ "-fsymbol-partition=foo_partition" ]
+  }
+}
+
+# Generate JNI registration for the methods called by the Java side. Note the
+# no_transitive_deps argument, which ensures that JNI is generated for only the
+# specified Java target, and not all its transitive deps (which could include
+# the base module).
+generate_jni_registration("jni_registration") {
+  targets = [ "//chrome/android/features/foo/internal:java" ]
+  header_output = "$target_gen_dir/jni_registration.h"
+  namespace = "foo"
+  no_transitive_deps = true
+}
+
+# This group is a convenience alias representing the module's native code,
+# allowing it to be named "native" for clarity in module descriptors.
+group("native") {
+  deps = [
+    ":foo",
+  ]
+}
+```
+
+Now, over to the implementation of the module. These are the parts that
+shouldn't know or care whether they're living in a module or not.
+
+Add a stub implementation in
+`//chrome/android/features/foo/internal/foo_impl.cc`:
+
+```c++
+#include "base/logging.h"
+#include "chrome/android/features/foo/internal/jni_headers/FooImpl_jni.h"
+
+static int JNI_FooImpl_Execute(JNIEnv* env) {
+  LOG(INFO) << "Running foo feature code!";
+  return 123;
+}
+```
+
+And, the associated build config in
+`//chrome/android/features/foo/internal/BUILD.gn`:
+
+```gn
+import("//build/config/android/rules.gni")
+
+...
+
+source_set("native") {
+  sources = [
+    "foo_impl.cc",
+  ]
+
+  deps = [
+    ":jni_headers",
+    "//base",
+  ]
+}
+
+generate_jni("jni_headers") {
+  sources = [
+    "java/src/org/chromium/chrome/features/foo/FooImpl.java",
+  ]
+}
+```
+
+With a declaration of the native method on the Java side:
+
+```java
+public class FooImpl implements Foo {
+    ...
+
+    @NativeMethods
+    interface Natives {
+        int execute();
+    }
+}
+```
+
+Finally, augment the module descriptor in
+`//chrome/android/modules/foo/foo_module.gni` with the native dependencies:
+
+```gn
+foo_module_desc = {
+  ...
+  native_deps = [
+    "//chrome/android/features/foo/internal:native",
+    "//chrome/android/modules/foo/internal:native",
+  ]
+}
+```
+
+#### Calling feature module native code from base the module
+
+If planning to use direct native-native calls into DFM code, then the module
+should have a purely virtual interface available. The main module can obtain a
+pointer to a DFM-created object or factory (implemented by the feature), and
+call its virtual methods.
+
+Ideally, the interface to the feature will avoid feature-specific types. If a
+feature defines complex data types, and uses them in its own interface, then its
+likely the main library will utilize the code backing these types. That code,
+and anything it references, will in turn be pulled back into the main library,
+negating the intent to house code in the DFM.
+
+Therefore, designing the feature interface to use C types, C++ standard types,
+or classes that aren't expected to move out of Chrome's main library is ideal.
+If feature-specific classes are needed, they simply need to avoid referencing
+feature library internals.
 
 ### Adding Android resources
 
diff --git a/extensions/browser/extension_service_worker_message_filter.cc b/extensions/browser/extension_service_worker_message_filter.cc
index cb246ae3..f2a17ed 100644
--- a/extensions/browser/extension_service_worker_message_filter.cc
+++ b/extensions/browser/extension_service_worker_message_filter.cc
@@ -146,7 +146,7 @@
 
 void ExtensionServiceWorkerMessageFilter::OnDidStartServiceWorkerContext(
     const ExtensionId& extension_id,
-    int activation_sequence,
+    ActivationSequence activation_sequence,
     const GURL& service_worker_scope,
     int64_t service_worker_version_id,
     int thread_id) {
@@ -168,7 +168,7 @@
 
 void ExtensionServiceWorkerMessageFilter::OnDidStopServiceWorkerContext(
     const ExtensionId& extension_id,
-    int activation_sequence,
+    ActivationSequence activation_sequence,
     const GURL& service_worker_scope,
     int64_t service_worker_version_id,
     int thread_id) {
diff --git a/extensions/browser/extension_service_worker_message_filter.h b/extensions/browser/extension_service_worker_message_filter.h
index 07a72ab..59a9694 100644
--- a/extensions/browser/extension_service_worker_message_filter.h
+++ b/extensions/browser/extension_service_worker_message_filter.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/browser_thread.h"
+#include "extensions/common/activation_sequence.h"
 #include "extensions/common/extension_id.h"
 
 class GURL;
@@ -56,12 +57,12 @@
                                            int64_t service_worker_version_id,
                                            int thread_id);
   void OnDidStartServiceWorkerContext(const ExtensionId& extension_id,
-                                      int activation_sequence,
+                                      ActivationSequence activation_sequence,
                                       const GURL& service_worker_scope,
                                       int64_t service_worker_version_id,
                                       int thread_id);
   void OnDidStopServiceWorkerContext(const ExtensionId& extension_id,
-                                     int activation_sequence,
+                                     ActivationSequence activation_sequence,
                                      const GURL& service_worker_scope,
                                      int64_t service_worker_version_id,
                                      int thread_id);
diff --git a/extensions/browser/renderer_startup_helper.cc b/extensions/browser/renderer_startup_helper.cc
index 3f56948..2cf07a6 100644
--- a/extensions/browser/renderer_startup_helper.cc
+++ b/extensions/browser/renderer_startup_helper.cc
@@ -24,6 +24,7 @@
 #include "extensions/browser/extensions_browser_client.h"
 #include "extensions/browser/guest_view/web_view/web_view_guest.h"
 #include "extensions/browser/service_worker_task_queue.h"
+#include "extensions/common/activation_sequence.h"
 #include "extensions/common/cors_util.h"
 #include "extensions/common/extension_messages.h"
 #include "extensions/common/extension_set.h"
@@ -60,8 +61,9 @@
 
 // Returns the current ActivationSequence of |extension| if the extension is
 // Service Worker-based, otherwise returns base::nullopt.
-base::Optional<int> GetWorkerActivationSequence(BrowserContext* browser_context,
-                                                const Extension& extension) {
+base::Optional<ActivationSequence> GetWorkerActivationSequence(
+    BrowserContext* browser_context,
+    const Extension& extension) {
   if (BackgroundInfo::IsServiceWorkerBased(&extension)) {
     return ServiceWorkerTaskQueue::Get(browser_context)
         ->GetCurrentSequence(extension.id());
diff --git a/extensions/browser/service_worker_task_queue.cc b/extensions/browser/service_worker_task_queue.cc
index 18cac2d..50424ec8 100644
--- a/extensions/browser/service_worker_task_queue.cc
+++ b/extensions/browser/service_worker_task_queue.cc
@@ -243,7 +243,7 @@
 void ServiceWorkerTaskQueue::DidStartServiceWorkerContext(
     int render_process_id,
     const ExtensionId& extension_id,
-    int activation_sequence,
+    ActivationSequence activation_sequence,
     const GURL& service_worker_scope,
     int64_t service_worker_version_id,
     int thread_id) {
@@ -280,7 +280,7 @@
 void ServiceWorkerTaskQueue::DidStopServiceWorkerContext(
     int render_process_id,
     const ExtensionId& extension_id,
-    int activation_sequence,
+    ActivationSequence activation_sequence,
     const GURL& service_worker_scope,
     int64_t service_worker_version_id,
     int thread_id) {
@@ -355,7 +355,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   const ExtensionId extension_id = extension->id();
-  ActivationSequence current_sequence = ++next_activation_sequence_;
+  ActivationSequence current_sequence(++next_activation_sequence_);
   activation_sequences_[extension_id] = current_sequence;
   SequencedContextId context_id(
       LazyContextId(browser_context_, extension_id, extension->url()),
@@ -577,8 +577,7 @@
   return current_sequence == sequence;
 }
 
-base::Optional<ServiceWorkerTaskQueue::ActivationSequence>
-ServiceWorkerTaskQueue::GetCurrentSequence(
+base::Optional<ActivationSequence> ServiceWorkerTaskQueue::GetCurrentSequence(
     const ExtensionId& extension_id) const {
   auto iter = activation_sequences_.find(extension_id);
   if (iter == activation_sequences_.end())
diff --git a/extensions/browser/service_worker_task_queue.h b/extensions/browser/service_worker_task_queue.h
index 76b2285..c7a0260 100644
--- a/extensions/browser/service_worker_task_queue.h
+++ b/extensions/browser/service_worker_task_queue.h
@@ -16,6 +16,7 @@
 #include "extensions/browser/lazy_context_id.h"
 #include "extensions/browser/lazy_context_task_queue.h"
 #include "extensions/browser/service_worker/worker_id.h"
+#include "extensions/common/activation_sequence.h"
 #include "extensions/common/extension_id.h"
 #include "url/gurl.h"
 
@@ -72,12 +73,6 @@
 class ServiceWorkerTaskQueue : public KeyedService,
                                public LazyContextTaskQueue {
  public:
-  // Unique identifier for an extension's activation->deactivation span.
-  // TODO(lazyboy): Move this under extensions/common/ for consistency, so that
-  // renderer process can use this instead of using "int" directly. We'd also
-  // want StrongAlias for this.
-  using ActivationSequence = int;
-
   explicit ServiceWorkerTaskQueue(content::BrowserContext* browser_context);
   ~ServiceWorkerTaskQueue() override;
 
@@ -108,14 +103,14 @@
   // has completed executing.
   void DidStartServiceWorkerContext(int render_process_id,
                                     const ExtensionId& extension_id,
-                                    int activation_sequence,
+                                    ActivationSequence activation_sequence,
                                     const GURL& service_worker_scope,
                                     int64_t service_worker_version_id,
                                     int thread_id);
   // Called once an extension Service Worker was destroyed.
   void DidStopServiceWorkerContext(int render_process_id,
                                    const ExtensionId& extension_id,
-                                   int activation_sequence,
+                                   ActivationSequence activation_sequence,
                                    const GURL& service_worker_scope,
                                    int64_t service_worker_version_id,
                                    int thread_id);
@@ -202,7 +197,7 @@
 
   WorkerState* GetWorkerState(const SequencedContextId& context_id);
 
-  ActivationSequence next_activation_sequence_ = 0;
+  int next_activation_sequence_ = 0;
 
   // The state of worker of each activated extension.
   std::map<SequencedContextId, WorkerState> worker_state_map_;
diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn
index 581eeb1..ee0bfa0 100644
--- a/extensions/common/BUILD.gn
+++ b/extensions/common/BUILD.gn
@@ -65,6 +65,7 @@
   # in. TODO(brettw): This reverse dependency should be fixed.
   jumbo_static_library("common") {
     sources = [
+      "activation_sequence.h",
       "alias.h",
       "api/bluetooth/bluetooth_manifest_data.cc",
       "api/bluetooth/bluetooth_manifest_data.h",
diff --git a/extensions/common/activation_sequence.h b/extensions/common/activation_sequence.h
new file mode 100644
index 0000000..233c88ba
--- /dev/null
+++ b/extensions/common/activation_sequence.h
@@ -0,0 +1,22 @@
+// Copyright 2020 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 EXTENSIONS_COMMON_ACTIVATION_SEQUENCE_H_
+#define EXTENSIONS_COMMON_ACTIVATION_SEQUENCE_H_
+
+#include "base/util/type_safety/strong_alias.h"
+
+namespace extensions {
+
+// Unique identifier for an extension's activation->deactivation span.
+//
+// Applies to Service Worker based extensions. This is used to detect if a
+// PendingTask for an extension expired at the time of executing the task, due
+// to extension activation after deactivation.
+using ActivationSequence =
+    ::util::StrongAlias<class ActivationSequenceTag, int>;
+
+}  // namespace extensions
+
+#endif  // EXTENSIONS_COMMON_ACTIVATION_SEQUENCE_H_
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index 692156f..3d32153 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -219,7 +219,6 @@
         "chrome://cast/*",
         "chrome://cellular-setup/*",
         "chrome://discards/*",
-        "chrome://extensions-frame/*",  // TODO(dbeam): still needed?
         "chrome://extensions/*",
         "chrome://home/*",
         "chrome://chrome-signin/*",
@@ -262,7 +261,6 @@
     "contexts": ["webui"],
     "matches": [
       "chrome://extensions/*",
-      "chrome://extensions-frame/*",  // TODO(dbeam): still needed?
       "chrome://settings/*"
     ]
   }],
@@ -581,10 +579,7 @@
   }, {
     "channel": "stable",
     "contexts": ["webui"],
-    "matches": [
-      "chrome://extensions/*",
-      "chrome://extensions-frame/*"  // TODO(dbeam): still needed?
-    ]
+    "matches": ["chrome://extensions/*"]
   }],
   "types": {
     "internal": true,
diff --git a/extensions/common/extension_messages.cc b/extensions/common/extension_messages.cc
index 16dff60..d62a27bf 100644
--- a/extensions/common/extension_messages.cc
+++ b/extensions/common/extension_messages.cc
@@ -16,6 +16,7 @@
 #include "extensions/common/permissions/permissions_data.h"
 #include "extensions/common/permissions/permissions_info.h"
 
+using extensions::ActivationSequence;
 using extensions::APIPermission;
 using extensions::APIPermissionInfo;
 using extensions::APIPermissionSet;
@@ -62,7 +63,7 @@
 ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params(
     const Extension* extension,
     bool include_tab_permissions,
-    base::Optional<int> worker_activation_sequence)
+    base::Optional<ActivationSequence> worker_activation_sequence)
     : manifest(static_cast<base::DictionaryValue&&>(
           extension->manifest()->value()->Clone())),
       location(extension->location()),
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index ea431be..680db404 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -20,6 +20,7 @@
 #include "base/values.h"
 #include "content/public/common/common_param_traits.h"
 #include "content/public/common/socket_permission_request.h"
+#include "extensions/common/activation_sequence.h"
 #include "extensions/common/api/messaging/message.h"
 #include "extensions/common/api/messaging/messaging_endpoint.h"
 #include "extensions/common/api/messaging/port_context.h"
@@ -357,7 +358,8 @@
   ~ExtensionMsg_Loaded_Params();
   ExtensionMsg_Loaded_Params(const extensions::Extension* extension,
                              bool include_tab_permissions,
-                             base::Optional<int> worker_activation_sequence);
+                             base::Optional<extensions::ActivationSequence>
+                                 worker_activation_sequence);
 
   ExtensionMsg_Loaded_Params(ExtensionMsg_Loaded_Params&& other);
   ExtensionMsg_Loaded_Params& operator=(ExtensionMsg_Loaded_Params&& other);
@@ -398,7 +400,7 @@
 
   // If this extension is Service Worker based, then this contains the
   // activation sequence of the extension.
-  base::Optional<int> worker_activation_sequence;
+  base::Optional<extensions::ActivationSequence> worker_activation_sequence;
 
   // Send creation flags so extension is initialized identically.
   int creation_flags;
@@ -1071,7 +1073,7 @@
 // See https://crbug.com/879015#c4 for details.
 IPC_MESSAGE_CONTROL5(ExtensionHostMsg_DidStartServiceWorkerContext,
                      std::string /* extension_id */,
-                     int /* activation_sequence */,
+                     extensions::ActivationSequence /* activation_sequence */,
                      GURL /* service_worker_scope */,
                      int64_t /* service_worker_version_id */,
                      int /* worker_thread_id */)
@@ -1080,7 +1082,7 @@
 // destroyed.
 IPC_MESSAGE_CONTROL5(ExtensionHostMsg_DidStopServiceWorkerContext,
                      std::string /* extension_id */,
-                     int /* activation_sequence */,
+                     extensions::ActivationSequence /* activation_sequence */,
                      GURL /* service_worker_scope */,
                      int64_t /* service_worker_version_id */,
                      int /* worker_thread_id */)
diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc
index d63225b0..60086ef 100644
--- a/extensions/renderer/dispatcher.cc
+++ b/extensions/renderer/dispatcher.cc
@@ -457,7 +457,7 @@
     std::unique_ptr<IPCMessageSender> ipc_sender =
         IPCMessageSender::CreateWorkerThreadIPCMessageSender(
             worker_dispatcher, service_worker_version_id);
-    int worker_activation_sequence =
+    ActivationSequence worker_activation_sequence =
         *RendererExtensionRegistry::Get()->GetWorkerActivationSequence(
             extension->id());
     worker_dispatcher->AddWorkerData(
diff --git a/extensions/renderer/renderer_extension_registry.cc b/extensions/renderer/renderer_extension_registry.cc
index 93936af5..1439240 100644
--- a/extensions/renderer/renderer_extension_registry.cc
+++ b/extensions/renderer/renderer_extension_registry.cc
@@ -105,7 +105,7 @@
 
 void RendererExtensionRegistry::SetWorkerActivationSequence(
     const scoped_refptr<const Extension>& extension,
-    int worker_activation_sequence) {
+    ActivationSequence worker_activation_sequence) {
   DCHECK(content::RenderThread::Get());
   DCHECK(Contains(extension->id()));
   DCHECK(BackgroundInfo::IsServiceWorkerBased(extension.get()));
@@ -114,7 +114,8 @@
   worker_activation_sequences_[extension->id()] = worker_activation_sequence;
 }
 
-base::Optional<int> RendererExtensionRegistry::GetWorkerActivationSequence(
+base::Optional<ActivationSequence>
+RendererExtensionRegistry::GetWorkerActivationSequence(
     const ExtensionId& extension_id) const {
   base::AutoLock lock(lock_);
   auto iter = worker_activation_sequences_.find(extension_id);
diff --git a/extensions/renderer/renderer_extension_registry.h b/extensions/renderer/renderer_extension_registry.h
index a62d26c..25cfc815 100644
--- a/extensions/renderer/renderer_extension_registry.h
+++ b/extensions/renderer/renderer_extension_registry.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/synchronization/lock.h"
+#include "extensions/common/activation_sequence.h"
 #include "extensions/common/extension_id.h"
 #include "extensions/common/extension_set.h"
 
@@ -56,17 +57,17 @@
   // Sets ActivationSequence for a Service Worker based |extension|.
   void SetWorkerActivationSequence(
       const scoped_refptr<const Extension>& extension,
-      int worker_activation_sequence);
+      ActivationSequence worker_activation_sequence);
   // Returns the current activation sequence for worker based extension with
   // |extension_id|. Returns base::nullopt otherwise.
-  base::Optional<int> GetWorkerActivationSequence(
+  base::Optional<ActivationSequence> GetWorkerActivationSequence(
       const ExtensionId& extension_id) const;
 
  private:
   ExtensionSet extensions_;
 
   // Maps extension id to ActivationSequence, for worker based extensions.
-  std::map<ExtensionId, int> worker_activation_sequences_;
+  std::map<ExtensionId, ActivationSequence> worker_activation_sequences_;
 
   mutable base::Lock lock_;
 
diff --git a/extensions/renderer/service_worker_data.cc b/extensions/renderer/service_worker_data.cc
index 1f95bd2..8314e4d 100644
--- a/extensions/renderer/service_worker_data.cc
+++ b/extensions/renderer/service_worker_data.cc
@@ -10,7 +10,7 @@
 
 ServiceWorkerData::ServiceWorkerData(
     int64_t service_worker_version_id,
-    int activation_sequence,
+    ActivationSequence activation_sequence,
     ScriptContext* context,
     std::unique_ptr<NativeExtensionBindingsSystem> bindings_system)
     : service_worker_version_id_(service_worker_version_id),
diff --git a/extensions/renderer/service_worker_data.h b/extensions/renderer/service_worker_data.h
index 5cd152a..dbaff48d 100644
--- a/extensions/renderer/service_worker_data.h
+++ b/extensions/renderer/service_worker_data.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "base/macros.h"
+#include "extensions/common/activation_sequence.h"
 #include "extensions/renderer/v8_schema_registry.h"
 
 namespace extensions {
@@ -20,7 +21,7 @@
  public:
   ServiceWorkerData(
       int64_t service_worker_version_id,
-      int activation_sequence,
+      ActivationSequence activation_sequence,
       ScriptContext* context,
       std::unique_ptr<NativeExtensionBindingsSystem> bindings_system);
   ~ServiceWorkerData();
@@ -32,12 +33,14 @@
   int64_t service_worker_version_id() const {
     return service_worker_version_id_;
   }
-  int activation_sequence() const { return activation_sequence_; }
+  ActivationSequence activation_sequence() const {
+    return activation_sequence_;
+  }
   ScriptContext* context() const { return context_; }
 
  private:
   const int64_t service_worker_version_id_;
-  const int activation_sequence_;
+  const ActivationSequence activation_sequence_;
   ScriptContext* const context_ = nullptr;
 
   std::unique_ptr<V8SchemaRegistry> v8_schema_registry_;
diff --git a/extensions/renderer/worker_thread_dispatcher.cc b/extensions/renderer/worker_thread_dispatcher.cc
index 9906e300..189907e 100644
--- a/extensions/renderer/worker_thread_dispatcher.cc
+++ b/extensions/renderer/worker_thread_dispatcher.cc
@@ -264,7 +264,7 @@
 
 void WorkerThreadDispatcher::AddWorkerData(
     int64_t service_worker_version_id,
-    int activation_sequence,
+    ActivationSequence activation_sequence,
     ScriptContext* script_context,
     std::unique_ptr<NativeExtensionBindingsSystem> bindings_system) {
   ServiceWorkerData* data = g_data_tls.Pointer()->Get();
diff --git a/extensions/renderer/worker_thread_dispatcher.h b/extensions/renderer/worker_thread_dispatcher.h
index f13874ef..2bdcdb81 100644
--- a/extensions/renderer/worker_thread_dispatcher.h
+++ b/extensions/renderer/worker_thread_dispatcher.h
@@ -13,6 +13,7 @@
 #include "base/threading/platform_thread.h"
 #include "content/public/renderer/render_thread_observer.h"
 #include "content/public/renderer/worker_thread.h"
+#include "extensions/common/activation_sequence.h"
 #include "extensions/common/extension_id.h"
 #include "ipc/ipc_sync_message_filter.h"
 
@@ -63,7 +64,7 @@
 
   void AddWorkerData(
       int64_t service_worker_version_id,
-      int activation_sequence,
+      ActivationSequence activation_sequence,
       ScriptContext* script_context,
       std::unique_ptr<NativeExtensionBindingsSystem> bindings_system);
   void RemoveWorkerData(int64_t service_worker_version_id);
diff --git a/fuchsia/base/BUILD.gn b/fuchsia/base/BUILD.gn
index 07d2d4e..ed7dbca8 100644
--- a/fuchsia/base/BUILD.gn
+++ b/fuchsia/base/BUILD.gn
@@ -11,11 +11,13 @@
 # Integration helpers for commonly used fuchsia.* APIs.
 source_set("base") {
   sources = [
+    "config_reader.cc",
     "init_logging.cc",
     "mem_buffer_util.cc",
     "string_util.cc",
   ]
   public = [
+    "config_reader.h",
     "init_logging.h",
     "mem_buffer_util.h",
     "string_util.h",
diff --git a/fuchsia/base/config_reader.cc b/fuchsia/base/config_reader.cc
new file mode 100644
index 0000000..f3ac3d5
--- /dev/null
+++ b/fuchsia/base/config_reader.cc
@@ -0,0 +1,38 @@
+// Copyright 2020 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 "fuchsia/base/config_reader.h"
+
+#include "base/files/file_util.h"
+#include "base/json/json_reader.h"
+
+namespace cr_fuchsia {
+
+base::Optional<base::Value> LoadPackageConfig() {
+  constexpr char kConfigPath[] = "/config/data/config.json";
+
+  base::FilePath path(kConfigPath);
+  if (!base::PathExists(path)) {
+    LOG(WARNING) << "Config file doesn't exist: " << path.value();
+    return base::nullopt;
+  }
+
+  std::string file_content;
+  bool loaded = base::ReadFileToString(path, &file_content);
+  if (!loaded) {
+    LOG(WARNING) << "Couldn't read config file: " << path.value();
+    return base::nullopt;
+  }
+
+  base::JSONReader reader;
+  base::Optional<base::Value> parsed = reader.Read(file_content);
+  CHECK(parsed) << "Failed to parse " << path.value() << ": "
+                << reader.GetErrorMessage();
+  CHECK(parsed->is_dict()) << "Config is not a JSON dictinary: "
+                           << path.value();
+
+  return std::move(parsed.value());
+}
+
+}  // namespace cr_fuchsia
diff --git a/fuchsia/base/config_reader.h b/fuchsia/base/config_reader.h
new file mode 100644
index 0000000..7e94d82
--- /dev/null
+++ b/fuchsia/base/config_reader.h
@@ -0,0 +1,20 @@
+// Copyright 2020 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 FUCHSIA_BASE_CONFIG_READER_H_
+#define FUCHSIA_BASE_CONFIG_READER_H_
+
+#include "base/files/file_path.h"
+#include "base/values.h"
+
+namespace cr_fuchsia {
+
+// Loads and parses configuration data from the environment.
+// Returns a null value if the file(s) do not exist.
+// CHECK-fails if the file(s) are present but not parseable.
+base::Optional<base::Value> LoadPackageConfig();
+
+}  // namespace cr_fuchsia
+
+#endif  // FUCHSIA_BASE_CONFIG_READER_H_
diff --git a/fuchsia/engine/BUILD.gn b/fuchsia/engine/BUILD.gn
index 9262076..3f1764cb 100644
--- a/fuchsia/engine/BUILD.gn
+++ b/fuchsia/engine/BUILD.gn
@@ -68,7 +68,6 @@
   deps = [
     ":mojom",
     ":switches",
-    ":web_engine_pak",
     "//base",
     "//base:base_static",
     "//base/util/memory_pressure",
diff --git a/fuchsia/engine/context_provider_impl.cc b/fuchsia/engine/context_provider_impl.cc
index a21c84b..ea6825f5 100644
--- a/fuchsia/engine/context_provider_impl.cc
+++ b/fuchsia/engine/context_provider_impl.cc
@@ -42,6 +42,7 @@
 #include "build/build_config.h"
 #include "components/viz/common/features.h"
 #include "content/public/common/content_switches.h"
+#include "fuchsia/base/config_reader.h"
 #include "fuchsia/engine/common/web_engine_content_client.h"
 #include "fuchsia/engine/switches.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
@@ -114,32 +115,15 @@
   return true;
 }
 
-constexpr char kConfigFileName[] = "/config/data/config.json";
-
-base::Value LoadConfigFrom(const base::FilePath& file_path) {
-  if (!base::PathExists(file_path)) {
-    DLOG(WARNING) << file_path.value()
-                  << " doesn't exist. Using default WebEngine configuration.";
+base::Value LoadConfig() {
+  base::Optional<base::Value> config = cr_fuchsia::LoadPackageConfig();
+  if (!config) {
+    DLOG(WARNING) << "Configuration data not found. Using default "
+                     "WebEngine configuration.";
     return base::Value(base::Value::Type::DICTIONARY);
   }
 
-  std::string file_content;
-  bool loaded = base::ReadFileToString(file_path, &file_content);
-  CHECK(loaded) << "Failed to read " << file_path.value();
-
-  base::JSONReader reader;
-  base::Optional<base::Value> parsed = reader.Read(file_content);
-  CHECK(parsed) << "Failed to parse " << file_path.value() << ": "
-                << reader.GetErrorMessage();
-  CHECK(parsed->is_dict()) << "Config is not a JSON dictinary: "
-                           << file_path.value();
-
-  return std::move(parsed.value());
-}
-
-const base::Value& GetWebEngineConfig() {
-  static base::Value config = LoadConfigFrom(base::FilePath(kConfigFileName));
-  return config;
+  return std::move(*config);
 }
 
 // Returns false if the config is present but has invalid contents.
@@ -303,6 +287,15 @@
   if (params.has_features())
     features = params.features();
 
+  const bool is_headless =
+      (features & fuchsia::web::ContextFeatureFlags::HEADLESS) ==
+      fuchsia::web::ContextFeatureFlags::HEADLESS;
+  if (is_headless) {
+    launch_command.AppendSwitchNative(switches::kOzonePlatform,
+                                      switches::kHeadless);
+    launch_command.AppendSwitch(switches::kHeadless);
+  }
+
   bool enable_vulkan = (features & fuchsia::web::ContextFeatureFlags::VULKAN) ==
                        fuchsia::web::ContextFeatureFlags::VULKAN;
   bool enable_widevine =
@@ -320,21 +313,12 @@
   }
 
   bool enable_drm = enable_widevine || enable_playready;
-  if (enable_drm && !enable_vulkan) {
+  if (enable_drm && !enable_vulkan && !is_headless) {
     DLOG(ERROR) << "WIDEVINE_CDM and PLAYREADY_CDM features require VULKAN.";
     context_request.Close(ZX_ERR_INVALID_ARGS);
     return;
   }
 
-  const bool is_headless =
-      (features & fuchsia::web::ContextFeatureFlags::HEADLESS) ==
-      fuchsia::web::ContextFeatureFlags::HEADLESS;
-  if (is_headless) {
-    launch_command.AppendSwitchNative(switches::kOzonePlatform,
-                                      switches::kHeadless);
-    launch_command.AppendSwitch(switches::kHeadless);
-  }
-
   if (enable_vulkan) {
     if (is_headless) {
       LOG(ERROR) << "VULKAN and HEADLESS features cannot be used together.";
@@ -365,8 +349,9 @@
     launch_command.AppendSwitch(switches::kDisableSoftwareRasterizer);
   }
 
-  const base::Value& web_engine_config =
-      config_for_test_.is_none() ? GetWebEngineConfig() : config_for_test_;
+  base::Value web_engine_config =
+      config_for_test_.is_none() ? LoadConfig() : std::move(config_for_test_);
+
   bool allow_protected_graphics =
       web_engine_config.FindBoolPath("allow-protected-graphics")
           .value_or(false);
diff --git a/fuchsia/runners/cast/cast_runner.cmx b/fuchsia/runners/cast/cast_runner.cmx
index 325ffeb8c..cffe856f 100644
--- a/fuchsia/runners/cast/cast_runner.cmx
+++ b/fuchsia/runners/cast/cast_runner.cmx
@@ -1,5 +1,8 @@
 {
   "sandbox": {
+    "features": [
+      "config-data"
+    ],
     "services": [
       "chromium.cast.ApplicationConfigManager",
       "fuchsia.accessibility.semantics.SemanticsManager",
diff --git a/fuchsia/runners/cast/main.cc b/fuchsia/runners/cast/main.cc
index 1bc4d9f0..3bd65dca 100644
--- a/fuchsia/runners/cast/main.cc
+++ b/fuchsia/runners/cast/main.cc
@@ -8,13 +8,32 @@
 #include "base/fuchsia/default_context.h"
 #include "base/fuchsia/file_utils.h"
 #include "base/message_loop/message_pump_type.h"
+#include "base/optional.h"
 #include "base/run_loop.h"
 #include "base/task/single_thread_task_executor.h"
+#include "base/values.h"
 #include "build/buildflag.h"
+#include "fuchsia/base/config_reader.h"
 #include "fuchsia/base/init_logging.h"
 #include "fuchsia/runners/buildflags.h"
 #include "fuchsia/runners/cast/cast_runner.h"
 
+namespace {
+
+bool IsHeadless() {
+  constexpr char kHeadlessConfigKey[] = "headless";
+
+  base::Optional<base::Value> config = cr_fuchsia::LoadPackageConfig();
+  if (config) {
+    base::Optional<bool> headless = config->FindBoolPath(kHeadlessConfigKey);
+    return headless && *headless;
+  }
+
+  return false;
+}
+
+}  // namespace
+
 int main(int argc, char** argv) {
   base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
   base::RunLoop run_loop;
@@ -27,10 +46,16 @@
   fuchsia::web::ContextFeatureFlags features =
       fuchsia::web::ContextFeatureFlags::NETWORK |
       fuchsia::web::ContextFeatureFlags::AUDIO |
-      fuchsia::web::ContextFeatureFlags::VULKAN |
-      fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER |
-      fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM |
-      fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER_ONLY;
+      fuchsia::web::ContextFeatureFlags::WIDEVINE_CDM;
+
+  if (IsHeadless()) {
+    LOG(WARNING) << "Running in headless mode.";
+    features |= fuchsia::web::ContextFeatureFlags::HEADLESS;
+  } else {
+    features |= fuchsia::web::ContextFeatureFlags::VULKAN |
+                fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER |
+                fuchsia::web::ContextFeatureFlags::HARDWARE_VIDEO_DECODER_ONLY;
+  }
 
   fuchsia::web::CreateContextParams create_context_params;
   create_context_params.set_features(features);
@@ -39,8 +64,6 @@
       base::FilePath(base::fuchsia::kServiceDirectoryPath)));
   CHECK(create_context_params.service_directory());
 
-  // TODO(crbug.com/1025045): Set HEADLESS flag based on system config data.
-
   const char kCastPlayreadyKeySystem[] = "com.chromecast.playready";
   create_context_params.set_playready_key_system(kCastPlayreadyKeySystem);
 
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
index d280ca7..8ad933f 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
@@ -681,15 +681,10 @@
   texture->sampler_state_.wrap_t = GL_CLAMP_TO_EDGE;
   texture->sampler_state_.wrap_s = GL_CLAMP_TO_EDGE;
 
-  // If the backing is already cleared, no need to clear it again.
-  gfx::Rect cleared_rect;
-  if (IsCleared())
-    cleared_rect = gfx::Rect(size());
-
   texture->SetLevelInfo(target, 0, egl_image->GetInternalFormat(),
                         size().width(), size().height(), 1, 0,
                         egl_image->GetDataFormat(), egl_image->GetDataType(),
-                        cleared_rect);
+                        ClearedRect());
   texture->SetLevelImage(target, 0, egl_image.get(), gles2::Texture::BOUND);
   texture->SetImmutable(true, false);
   api->glBindTextureFn(target, old_texture_binding);
@@ -887,7 +882,6 @@
     AHardwareBuffer_Desc hwb_info;
     base::AndroidHardwareBufferCompat::GetInstance().Describe(buffer,
                                                               &hwb_info);
-
     void* address = nullptr;
     if (int error = base::AndroidHardwareBufferCompat::GetInstance().Lock(
             buffer, AHARDWAREBUFFER_USAGE_CPU_WRITE_RARELY, -1, 0, &address)) {
diff --git a/gpu/command_buffer/service/shared_image_representation.cc b/gpu/command_buffer/service/shared_image_representation.cc
index e02b9485..829083c 100644
--- a/gpu/command_buffer/service/shared_image_representation.cc
+++ b/gpu/command_buffer/service/shared_image_representation.cc
@@ -34,6 +34,8 @@
   if (!BeginAccess(mode))
     return nullptr;
 
+  UpdateClearedStateOnBeginAccess();
+
   constexpr GLenum kReadAccess = 0x8AF6;
   if (mode == kReadAccess)
     backing()->OnReadSucceeded();
@@ -61,6 +63,15 @@
     SetClearedRect(cleared_rect);
 }
 
+void SharedImageRepresentationGLTexture::UpdateClearedStateOnBeginAccess() {
+  auto* texture = GetTexture();
+  // Operations outside of the gles2::Texture may have cleared or uncleared it.
+  // Make sure this state is reflected back in gles2::Texture.
+  gfx::Rect cleared_rect = ClearedRect();
+  if (cleared_rect != texture->GetLevelClearedRect(texture->target(), 0))
+    texture->SetLevelClearedRect(texture->target(), 0, cleared_rect);
+}
+
 gpu::TextureBase*
 SharedImageRepresentationGLTexturePassthrough::GetTextureBase() {
   return GetTexturePassthrough().get();
diff --git a/gpu/command_buffer/service/shared_image_representation.h b/gpu/command_buffer/service/shared_image_representation.h
index 798efac..e8a699e 100644
--- a/gpu/command_buffer/service/shared_image_representation.h
+++ b/gpu/command_buffer/service/shared_image_representation.h
@@ -136,7 +136,9 @@
  protected:
   friend class SharedImageRepresentationSkiaGL;
 
-  // Can be overridden to handle clear state tracking when GL access ends.
+  // Can be overridden to handle clear state tracking when GL access begins or
+  // ends.
+  virtual void UpdateClearedStateOnBeginAccess() {}
   virtual void UpdateClearedStateOnEndAccess() {}
 
   // TODO(ericrk): Make these pure virtual and ensure real implementations
@@ -159,6 +161,7 @@
   gpu::TextureBase* GetTextureBase() override;
 
  protected:
+  void UpdateClearedStateOnBeginAccess() override;
   void UpdateClearedStateOnEndAccess() override;
 };
 
diff --git a/gpu/command_buffer/service/shared_image_representation_unittest.cc b/gpu/command_buffer/service/shared_image_representation_unittest.cc
index 81c6055..26b6f78 100644
--- a/gpu/command_buffer/service/shared_image_representation_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_representation_unittest.cc
@@ -74,13 +74,26 @@
   }
   EXPECT_TRUE(representation->IsCleared());
 
-  // We can now begin accdess with |allow_uncleared| == false.
+  // We can now begin access with |allow_uncleared| == false.
   {
     auto scoped_access = representation->BeginScopedAccess(
         GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM,
         SharedImageRepresentation::AllowUnclearedAccess::kNo);
     EXPECT_TRUE(scoped_access);
   }
+
+  // Reset the representation to uncleared. This should unclear the texture on
+  // BeginAccess.
+  representation->SetClearedRect(gfx::Rect());
+  {
+    auto scoped_access = representation->BeginScopedAccess(
+        GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM,
+        SharedImageRepresentation::AllowUnclearedAccess::kYes);
+    ASSERT_TRUE(scoped_access);
+    EXPECT_FALSE(
+        representation->GetTexture()->IsLevelCleared(GL_TEXTURE_2D, 0));
+  }
+  EXPECT_FALSE(representation->IsCleared());
 }
 
 TEST_F(SharedImageRepresentationTest, GLTexturePassthroughClearing) {
diff --git a/infra/config/buckets/try.star b/infra/config/buckets/try.star
index da083493..dbc587a 100644
--- a/infra/config/buckets/try.star
+++ b/infra/config/buckets/try.star
@@ -460,6 +460,35 @@
 )
 
 
+# Used for listing chrome trybots in chromium's commit-queue.cfg without also
+# adding them to chromium's cr-buildbucket.cfg. Note that the recipe these
+# builders run allow only known roller accounts when triggered via the CQ.
+def chrome_internal_verififer(
+    *,
+    builder):
+  luci.cq_tryjob_verifier(
+      builder = 'chrome:try/' + builder,
+      cq_group = 'cq',
+      includable_only = True,
+  )
+
+chrome_internal_verififer(
+    builder = 'chromeos-betty-chrome',
+)
+
+chrome_internal_verififer(
+    builder = 'chromeos-betty-pi-arc-chrome',
+)
+
+chrome_internal_verififer(
+    builder = 'chromeos-eve-compile-chrome',
+)
+
+chrome_internal_verififer(
+    builder = 'chromeos-kevin-compile-chrome',
+)
+
+
 def chromiumos_builder(*, name, **kwargs):
   return try_builder(
       name = name,
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index 94cda54..8e893f2 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -29,6 +29,22 @@
     >
     tryjob: <
       builders: <
+        name: "chrome/try/chromeos-betty-chrome"
+        includable_only: true
+      >
+      builders: <
+        name: "chrome/try/chromeos-betty-pi-arc-chrome"
+        includable_only: true
+      >
+      builders: <
+        name: "chrome/try/chromeos-eve-compile-chrome"
+        includable_only: true
+      >
+      builders: <
+        name: "chrome/try/chromeos-kevin-compile-chrome"
+        includable_only: true
+      >
+      builders: <
         name: "chromium/try/android-asan"
         includable_only: true
       >
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index b8225d0..854f873 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -2428,6 +2428,11 @@
   MaybeSendOverlayInfoToDecoder();
 }
 
+void WebMediaPlayerImpl::OnPowerExperimentState(bool state) {
+  if (power_status_helper_)
+    power_status_helper_->UpdatePowerExperimentState(state);
+}
+
 void WebMediaPlayerImpl::ScheduleRestart() {
   // TODO(watk): All restart logic should be moved into PipelineController.
   if (pipeline_controller_->IsPipelineRunning() &&
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h
index c52c9563..7f5a2efe4 100644
--- a/media/blink/webmediaplayer_impl.h
+++ b/media/blink/webmediaplayer_impl.h
@@ -241,6 +241,7 @@
   void OnSeekBackward(double seconds) override;
   void OnVolumeMultiplierUpdate(double multiplier) override;
   void OnBecamePersistentVideo(bool value) override;
+  void OnPowerExperimentState(bool state) override;
   void RequestRemotePlaybackDisabled(bool disabled) override;
 
 #if defined(OS_ANDROID)
diff --git a/net/cert_net/nss_ocsp_session_url_request.cc b/net/cert_net/nss_ocsp_session_url_request.cc
index a609db7..5c75b40d 100644
--- a/net/cert_net/nss_ocsp_session_url_request.cc
+++ b/net/cert_net/nss_ocsp_session_url_request.cc
@@ -4,6 +4,8 @@
 
 #include "net/cert_net/nss_ocsp_session_url_request.h"
 
+#include "base/memory/scoped_refptr.h"
+#include "base/memory/weak_ptr.h"
 #include "base/message_loop/message_loop_current.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
@@ -76,12 +78,38 @@
   return ocsp_io_loop.get();
 }
 
+class OCSPRequestSessionDelegateFactoryURLRequest
+    : public OCSPRequestSessionDelegateFactory {
+ public:
+  OCSPRequestSessionDelegateFactoryURLRequest(
+      URLRequestContext* request_context)
+      : request_context_(request_context), weak_ptr_factory_(this) {
+    weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
+  }
+
+  scoped_refptr<OCSPRequestSessionDelegate> CreateOCSPRequestSessionDelegate()
+      override;
+
+  ~OCSPRequestSessionDelegateFactoryURLRequest() override = default;
+
+  URLRequestContext* request_context() const { return request_context_; }
+
+ private:
+  URLRequestContext* request_context_;
+
+  base::WeakPtr<OCSPRequestSessionDelegateFactoryURLRequest> weak_ptr_;
+  base::WeakPtrFactory<OCSPRequestSessionDelegateFactoryURLRequest>
+      weak_ptr_factory_;
+};
+
 class OCSPRequestSessionDelegateURLRequest : public OCSPRequestSessionDelegate,
                                              public URLRequest::Delegate {
  public:
-  OCSPRequestSessionDelegateURLRequest(URLRequestContext* request_context)
+  OCSPRequestSessionDelegateURLRequest(
+      base::WeakPtr<OCSPRequestSessionDelegateFactoryURLRequest>
+          delegate_factory)
       : buffer_(base::MakeRefCounted<IOBuffer>(kRecvBufferSize)),
-        request_context_(request_context),
+        delegate_factory_(std::move(delegate_factory)),
         cv_(&lock_) {}
 
   // OCSPRequestSessionDelegate overrides.
@@ -198,14 +226,7 @@
       FinishLoad();  // Will return a nullptr as the result.
       return;
     }
-    if (finished_) {
-      // This may occur if the OCSPIOLoop has already called Shutdown() (i.e.
-      // SetURLRequestContextForNSSHttpIO(nullptr) has been called), but the
-      // NSS worker thread posted a StartLoad task before Shutdown() was called.
-      // Since Shutdown() was called already, the tasks should all have been
-      // cancelled and FinishLoad() should already have been called. We have
-      // to make sure we honor the cancellation because |request_context_| may
-      // no longer be valid.
+    if (!delegate_factory_) {
       return;
     }
 
@@ -233,8 +254,8 @@
           setting: "This feature cannot be disabled by settings."
           policy_exception_justification: "Not implemented."
         })");
-    request_ = request_context_->CreateRequest(params->url, DEFAULT_PRIORITY,
-                                               this, traffic_annotation);
+    request_ = delegate_factory_->request_context()->CreateRequest(
+        params->url, DEFAULT_PRIORITY, this, traffic_annotation);
     request_->SetLoadFlags(LOAD_DISABLE_CACHE);
     request_->set_allow_credentials(false);
 
@@ -266,7 +287,7 @@
       base::AutoLock autolock(lock_);
       finished_ = true;
     }
-    request_context_ = nullptr;
+    delegate_factory_.reset();
     request_.reset();
     GetOCSPIOLoop()->RemoveRequest(this);
 
@@ -277,7 +298,7 @@
 
   std::unique_ptr<URLRequest> request_;  // The actual request this wraps
   scoped_refptr<IOBuffer> buffer_;       // Read buffer
-  URLRequestContext* request_context_;
+  base::WeakPtr<OCSPRequestSessionDelegateFactoryURLRequest> delegate_factory_;
 
   std::unique_ptr<OCSPRequestSessionResult> result_;
   bool finished_ = false;
@@ -341,23 +362,11 @@
     (*request_delegates_.begin())->CancelLoad();
 }
 
-class OCSPRequestSessionDelegateFactoryURLRequest
-    : public OCSPRequestSessionDelegateFactory {
- public:
-  OCSPRequestSessionDelegateFactoryURLRequest(
-      URLRequestContext* request_context)
-      : request_context_(request_context) {}
-  scoped_refptr<OCSPRequestSessionDelegate> CreateOCSPRequestSessionDelegate()
-      override {
-    return base::MakeRefCounted<OCSPRequestSessionDelegateURLRequest>(
-        request_context_);
-  }
-
-  ~OCSPRequestSessionDelegateFactoryURLRequest() override = default;
-
- private:
-  URLRequestContext* request_context_;
-};
+scoped_refptr<OCSPRequestSessionDelegate>
+OCSPRequestSessionDelegateFactoryURLRequest::
+    CreateOCSPRequestSessionDelegate() {
+  return base::MakeRefCounted<OCSPRequestSessionDelegateURLRequest>(weak_ptr_);
+}
 
 void SetURLRequestContextForNSSHttpIO(URLRequestContext* request_context) {
   if (request_context) {
diff --git a/remoting/host/keyboard_layout_monitor_win.cc b/remoting/host/keyboard_layout_monitor_win.cc
index b359fc0..28400d6 100644
--- a/remoting/host/keyboard_layout_monitor_win.cc
+++ b/remoting/host/keyboard_layout_monitor_win.cc
@@ -37,6 +37,11 @@
 
 constexpr base::TimeDelta POLL_INTERVAL =
     base::TimeDelta::FromMilliseconds(1000);
+// If second is equivalent to first (generates the same functions/characters at
+// all shift levels), second will be removed from the map.
+constexpr std::pair<ui::DomCode, ui::DomCode> POSSIBLE_EQUIVALENTS[] = {
+    // These are equivalent on the US QWERTY layout, among others.
+    {ui::DomCode::BACKSLASH, ui::DomCode::INTL_BACKSLASH}};
 
 class KeyboardLayoutMonitorWin : public KeyboardLayoutMonitor {
  public:
@@ -165,6 +170,44 @@
   std::vector<std::pair<std::uint32_t, int>> right_alts;
 
   for (ui::DomCode key : KeyboardLayoutMonitor::kSupportedKeys) {
+    // These keys cannot be injected properly on Windows with the APIs we use.
+    //
+    // The USB keyboard driver translates NumLock to the scancode 0x45, but it
+    // is delivered to applications as 0xE045. Meanwhile, Pause is translated
+    // to the scancode sequence 0xE1 0x1D 0x45 0xE1 0x9D 0xC5, but is delivered
+    // to applications as a plain 0x45.
+    //
+    // Injecting 0x45 using SendInput does get interpreted by Windows as
+    // VK_NUMLOCK, but is not translated to 0xE045 before being delivered to
+    // applications as it is with a physical keyboard. As a result, Chrome (for
+    // example) reports a key value of "NumLock" but a code value of "Pause".
+    //
+    // Injecting 0xE045, on the otherhand, does not get interpreted as
+    // VK_NUMLOCK, so Chrome sees reports a code value of "NumLock", but a key
+    // value of "Unidentified".
+    //
+    // I have not been able to determine any way to use SendInput to inject an
+    // event that Windows interprets as VK_PAUSE.
+    //
+    // NumpadEqual also behaves inconsistently when injected, but in a
+    // different way: while when input using a physical keyboard, the
+    // corresponding scancode (0x59) is always interpreted as VK_CLEAR
+    // regardless of the num lock state. When injected, however, Windows
+    // translates the key to VK_NUMPAD5 when numlock is enabled. Since the
+    // virtual keyboard considers num lock always to be enabled, this
+    // effectively results in an extra 5 key in the NumpadEqual position, which
+    // is both redundant and confusing. Given that most keyboards lack this key,
+    // and those that do have it label it '=', it seems easiest just to exclude
+    // it for now. In the future, we could consider adding support to it for
+    // keyboard layouts that treat is as something other than VK_CLEAR, if such
+    // layouts turn out to exist. (Why Windows maps the USB 'Keypad =' key to
+    // scancode 0x59 in the first place, even though 0x59 does not generate an
+    // '=' character, is unclear.)
+    if (key == ui::DomCode::NUM_LOCK || key == ui::DomCode::PAUSE ||
+        key == ui::DomCode::NUMPAD_EQUAL) {
+      continue;
+    }
+
     std::uint32_t usb_code = ui::KeycodeConverter::DomCodeToUsbKeycode(key);
     int scancode = ui::KeycodeConverter::DomCodeToNativeKeycode(key);
     UINT virtual_key = MapVirtualKeyEx(scancode, MAPVK_VSC_TO_VK_EX, layout);
@@ -175,6 +218,9 @@
 
     if (virtual_key == VK_CAPITAL || virtual_key == VK_NUMLOCK) {
       // Don't send caps or numlock keys until we decide how to handle them.
+      // (We currently skip the key in the NumLock position above due to
+      // difficulties injecting it, but the user still may have mapped a
+      // different key to that function.)
       continue;
     }
 
@@ -200,7 +246,18 @@
       key_state[VK_NUMLOCK] = 1;
       key_state[VK_CAPITAL] = 0;
       WCHAR char_buffer[16];
-      int size = ToUnicodeEx(translated_key, scancode, key_state, char_buffer,
+      // According to the documentation, ToUnicodeEx usually does the
+      // translation solely based on the virtual key, but can use bit 15 of the
+      // scancode to distinguish between a keypress and a key release. This
+      // suggests that the expected format for scancode is the upper word of
+      // lParam from a WM_CHAR, where the top bit similarly distinguished press
+      // versus release. In any event, passing |scancode| as the second
+      // parameter here would thus cause extended scancodes (which have the 15th
+      // bit set) to erroneously be interpreted as key-up events and not
+      // generate the appropriate character. Rather than attempting to munge the
+      // scancode into whatever format ToUnicodeEx expects, passing 0 seems to
+      // work just fine.
+      int size = ToUnicodeEx(translated_key, 0, key_state, char_buffer,
                              base::size(char_buffer), 0, layout);
       if (size < 0) {
         // We don't handle dead keys specially for the layout, but we do
@@ -209,6 +266,29 @@
         size = -size;
       }
       if (size > 0) {
+        if (size == 1 && char_buffer[0] < 0x20) {
+          // Handle known control characters.
+          protocol::LayoutKeyFunction function =
+              protocol::LayoutKeyFunction::UNKNOWN;
+          switch (char_buffer[0]) {
+            case 0x08:
+              function = protocol::LayoutKeyFunction::BACKSPACE;
+              break;
+            case 0x09:
+              function = protocol::LayoutKeyFunction::TAB;
+              break;
+            case 0x0D:
+              function = protocol::LayoutKeyFunction::ENTER;
+              break;
+            case 0x1B:
+              function = protocol::LayoutKeyFunction::ESCAPE;
+              break;
+          }
+          if (function != protocol::LayoutKeyFunction::UNKNOWN) {
+            key_actions[shift_level].set_function(function);
+            continue;
+          }
+        }
         // The key generated at least one character.
         key_actions[shift_level].set_character(
             base::UTF16ToUTF8(base::StringPiece16(char_buffer, size)));
@@ -244,6 +324,46 @@
     }
   }
 
+  // Some layouts have equivalent keys. Remove the redundant keys to make the
+  // layout cleaner.
+  auto* keys = layout_message.mutable_keys();
+  for (const std::pair<ui::DomCode, ui::DomCode>& possible_equivalent :
+       POSSIBLE_EQUIVALENTS) {
+    std::uint32_t code1 =
+        ui::KeycodeConverter::DomCodeToUsbKeycode(possible_equivalent.first);
+    std::uint32_t code2 =
+        ui::KeycodeConverter::DomCodeToUsbKeycode(possible_equivalent.second);
+    auto key_behavior1 = keys->find(code1);
+    auto key_behavior2 = keys->find(code2);
+    if (key_behavior1 != keys->end() && key_behavior2 != keys->end() &&
+        key_behavior1->second.SerializeAsString() ==
+            key_behavior2->second.SerializeAsString()) {
+      keys->erase(key_behavior2);
+    }
+  }
+
+  // There seem to be a number of keys that are mapped to a virtual key in the
+  // layout but don't do anything useful. E.g., the US QWERTY layout maps
+  // NonConvert, which isn't on a standard US keyboard, to VK_OEM_PA1, which
+  // doesn't appear to be useful. To avoid cluttering the on-screen keyboard
+  // with blank, useless keys, just omit unknown keys for now. We can revisit
+  // this if folks send feedback about useful keys being missing.
+  for (auto it = keys->begin(); it != keys->end();) {
+    bool has_action = false;
+    for (const auto& action : it->second.actions()) {
+      if (action.second.has_character() ||
+          (action.second.has_function() &&
+           action.second.function() != protocol::LayoutKeyFunction::UNKNOWN)) {
+        has_action = true;
+      }
+    }
+    if (!has_action) {
+      it = keys->erase(it);
+    } else {
+      ++it;
+    }
+  }
+
   reply_sequence->PostTask(
       FROM_HERE,
       base::BindOnce(&KeyboardLayoutMonitorWin::OnLayoutChanged,
@@ -310,6 +430,8 @@
     return virtual_key;
   }
   switch (virtual_key) {
+    case VK_DELETE:
+      return VK_DECIMAL;
     case VK_INSERT:
       return VK_NUMPAD0;
     case VK_END:
@@ -438,7 +560,7 @@
       return protocol::LayoutKeyFunction::CONTEXT_MENU;
     case VK_PAUSE:
       return protocol::LayoutKeyFunction::PAUSE;
-    case VK_PRINT:
+    case VK_SNAPSHOT:
       return protocol::LayoutKeyFunction::PRINT_SCREEN;
   }
 
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
index 768025c..e13fbc3 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -128,7 +128,7 @@
 #endif  // defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||
         // defined(MEMORY_SANITIZER)
 
-#if BUILDFLAG(CLANG_COVERAGE)
+#if BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
   if (SyscallSets::IsPrctl(sysno)) {
     return Allow();
   }
diff --git a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
index c0a51dad..ddf69c49 100644
--- a/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
+++ b/sandbox/linux/seccomp-bpf-helpers/baseline_policy_unittest.cc
@@ -345,7 +345,7 @@
 #define PR_CAPBSET_READ 23
 #endif
 
-#if !BUILDFLAG(CLANG_COVERAGE)
+#if !BUILDFLAG(CLANG_COVERAGE_INSIDE_SANDBOX)
 BPF_DEATH_TEST_C(BaselinePolicy,
                  PrctlSigsys,
                  DEATH_SEGV_MESSAGE(GetPrctlErrorMessageContentForTests()),
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index 94e9588a..105facc 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -215,6 +215,10 @@
 #define SK_IGNORE_LINEONLY_AA_CONVEX_PATH_OPTS
 #endif
 
+#ifndef SK_SUPPORT_LEGACY_CANVAS_MATRIX_VIRTUALS
+#define SK_SUPPORT_LEGACY_CANVAS_MATRIX_VIRTUALS
+#endif
+
 // Max. verb count for paths rendered by the edge-AA tessellating path renderer.
 #define GR_AA_TESSELLATOR_MAX_VERB_COUNT 100
 
diff --git a/styleguide/web/es.md b/styleguide/web/es.md
index e1ef98a..ab52d3f 100644
--- a/styleguide/web/es.md
+++ b/styleguide/web/es.md
@@ -588,6 +588,47 @@
 
 ---
 
+### Object Literal Extensions
+
+Convenient new ways for object property definition.
+
+**Usage Example:**
+
+```js
+// Computed property name
+const prop = 'foo';
+const o = {
+  [prop]: 'hey',
+  ['b' + 'ar']: 'there',
+};
+console.log(o);  // {foo: 'hey', bar: 'there'}
+
+// Shorthand property
+const foo = 1;
+const bar = 2;
+const o = {foo, bar};
+console.log(o);  // {foo: 1, bar: 2}
+
+// Method property
+const clearSky = {
+  // Basically the same as clouds: function() { return 0; }.
+  clouds() { return 0; },
+  color() { return 'blue'; },
+};
+console.log(clearSky.color());  // 'blue'
+console.log(clearSky.clouds());  // 0
+```
+
+**Documentation:** [link](https://tc39.github.io/ecma262/#sec-object-initialiser)
+[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer)
+
+**Discussion Notes / Link to Thread:**
+https://groups.google.com/a/chromium.org/d/msg/chromium-dev/RqOdTlxuGVg/M7I0CTryDQAJ
+
+Note: clang-format has some issues formatting complex computed property names.
+
+---
+
 ## Banned Features
 
 The following features are banned for Chromium development.
@@ -655,44 +696,6 @@
 
 ---
 
-### Object Literal Extensions
-
-Convenient new ways for object property definition.
-
-**Usage Example:**
-
-```js
-// Computed property name
-const prop = 'foo';
-const o = {
-  [prop]: 'hey',
-  ['b' + 'ar']: 'there',
-};
-console.log(o);  // {foo: 'hey', bar: 'there'}
-
-// Shorthand property
-const foo = 1;
-const bar = 2;
-const o = {foo, bar};
-console.log(o);  // {foo: 1, bar: 2}
-
-// Method property
-const clearSky = {
-  // Basically the same as clouds: function() { return 0; }.
-  clouds() { return 0; },
-  color() { return 'blue'; },
-};
-console.log(clearSky.color());  // 'blue'
-console.log(clearSky.clouds());  // 0
-```
-
-**Documentation:** [link](https://tc39.github.io/ecma262/#sec-object-initialiser)
-[link](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer)
-
-**Discussion Notes / Link to Thread:**
-
----
-
 ### Binary & Octal Literals
 
 **Usage Example:**
diff --git a/testing/buildbot/chromium.gpu.json b/testing/buildbot/chromium.gpu.json
index 8e32529..46a1e2f 100644
--- a/testing/buildbot/chromium.gpu.json
+++ b/testing/buildbot/chromium.gpu.json
@@ -2945,6 +2945,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -2970,6 +2971,7 @@
         "name": "tab_capture_end2end_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -2991,6 +2993,7 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3012,6 +3015,7 @@
         },
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3034,6 +3038,7 @@
         "name": "xr_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3065,6 +3070,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3093,6 +3099,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3121,6 +3128,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3149,6 +3157,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3181,6 +3190,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3222,6 +3232,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3264,6 +3275,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3294,6 +3306,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3323,6 +3336,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3351,6 +3365,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
@@ -3380,6 +3395,7 @@
         "should_retry_with_patch": false,
         "swarming": {
           "can_use_on_swarming_builders": true,
+          "containment_type": "AUTO",
           "dimension_sets": [
             {
               "gpu": "nvidia-quadro-p400-win10-stable",
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index c4c8562c..9579bd3 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -275,32 +275,6 @@
       },
     },
   },
-  'cast_shell_browsertests': {
-    'modifications': {
-      'Cast Audio Linux': {
-        'args': [
-          '--enable-local-file-accesses',
-          '--ozone-platform=headless',
-          '--no-sandbox',
-          '--test-launcher-jobs=1',
-        ],
-        'swarming': {
-          'can_use_on_swarming_builders': False,  # https://crbug.com/861753
-        },
-      },
-      'Cast Linux': {
-        'args': [
-          '--enable-local-file-accesses',
-          '--ozone-platform=headless',
-          '--no-sandbox',
-          '--test-launcher-jobs=1',
-        ],
-        'swarming': {
-          'can_use_on_swarming_builders': False,  # https://crbug.com/861753
-        },
-      },
-    },
-  },
   'cc_unittests': {
     'modifications': {
       'Linux TSan Tests': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index bc57759..59597fac 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -336,7 +336,17 @@
       'cast_base_unittests': {},
       'cast_crash_unittests': {},
       'cast_media_unittests': {},
-      'cast_shell_browsertests': {},
+      'cast_shell_browsertests': {
+        'args': [
+          '--enable-local-file-accesses',
+          '--ozone-platform=headless',
+          '--no-sandbox',
+          '--test-launcher-jobs=1',
+        ],
+        'swarming': {
+          'can_use_on_swarming_builders': False,  # https://crbug.com/861753
+        },
+      },
       'cast_shell_unittests': {},
     },
 
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index aea9c83..66d7f4b10 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2365,9 +2365,6 @@
         'mixins': [
           'win10_nvidia_quadro_p400_stable',
         ],
-        'remove_mixins': [
-          'swarming_containment_auto',
-        ],
         'test_suites': {
           'gtest_tests': 'gpu_win_gtests',
           'gpu_telemetry_tests': 'gpu_common_win_and_linux_telemetry_tests',
diff --git a/testing/scripts/run_performance_tests.py b/testing/scripts/run_performance_tests.py
index f367eed..9f2c09f 100755
--- a/testing/scripts/run_performance_tests.py
+++ b/testing/scripts/run_performance_tests.py
@@ -287,9 +287,10 @@
               command, handle, env=env)
         except OSError as e:
           print('Command to run gtest perf test %s failed with an OSError: %s' %
-                e)
+                (output_paths.name, e))
           return_code = 1
-    if not os.path.exists(output_paths.perf_results):
+    if (not os.path.exists(output_paths.perf_results) and
+        os.path.exists(output_paths.logs)):
       # Get the correct json format from the stdout to write to the perf
       # results file if gtest does not generate one.
       results_processor = generate_legacy_perf_dashboard_json.\
diff --git a/third_party/blink/.style.yapf b/third_party/blink/.style.yapf
new file mode 100644
index 0000000..5548c740
--- /dev/null
+++ b/third_party/blink/.style.yapf
@@ -0,0 +1,3 @@
+[style]
+based_on_style = pep8
+column_limit = 132
diff --git a/third_party/blink/perf_tests/OWNERS b/third_party/blink/perf_tests/OWNERS
index 7ae8de90..2a09c1b 100644
--- a/third_party/blink/perf_tests/OWNERS
+++ b/third_party/blink/perf_tests/OWNERS
@@ -1,8 +1,8 @@
 cbiesinger@chromium.org
-eae@chromium.org
 eyaich@chromium.org
 haraken@chromium.org
 hayato@chromium.org
+ikilpatrick@chromium.org
 kojii@chromium.org
 crouleau@chromium.org
 
diff --git a/third_party/blink/public/mojom/hid/hid.mojom b/third_party/blink/public/mojom/hid/hid.mojom
index 7c3f49b8..5f1c09d8 100644
--- a/third_party/blink/public/mojom/hid/hid.mojom
+++ b/third_party/blink/public/mojom/hid/hid.mojom
@@ -76,10 +76,11 @@
   // Requests access to a device. A chooser dialog is displayed with a list of
   // connected devices. If |filters| is non-empty, only devices that match one
   // or more of the filters will be included in the chooser list. If |filters|
-  // is empty, all connected devices are included. |device| contains the chosen
-  // device, or nullptr if the chooser dialog was canceled.
+  // is empty, all connected devices are included. |devices| contains logical
+  // HID interfaces exposed by the device. If the chooser dialog is canceled,
+  // |devices| is an empty array.
   RequestDevice(array<HidDeviceFilter> filters)
-      => (device.mojom.HidDeviceInfo? device);
+      => (array<device.mojom.HidDeviceInfo> devices);
 
   // Opens a connection to the device with GUID matching |device_guid|. |client|
   // will be notified when an input report is received from the device.
diff --git a/third_party/blink/public/platform/media/webmediaplayer_delegate.h b/third_party/blink/public/platform/media/webmediaplayer_delegate.h
index de3539f5..58493b8 100644
--- a/third_party/blink/public/platform/media/webmediaplayer_delegate.h
+++ b/third_party/blink/public/platform/media/webmediaplayer_delegate.h
@@ -74,6 +74,10 @@
     // Called to set as the persistent video. A persistent video should hide its
     // controls and go fullscreen.
     virtual void OnBecamePersistentVideo(bool value) = 0;
+
+    // Notify the player that it is now eligible to start recording power
+    // measurements if |state| is true, else it is no longer eligible.
+    virtual void OnPowerExperimentState(bool state) {}
   };
 
   // Returns true if the host frame is hidden or closed.
diff --git a/third_party/blink/renderer/core/css/media_query_exp.cc b/third_party/blink/renderer/core/css/media_query_exp.cc
index 3600f2f..b26aff2 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.cc
+++ b/third_party/blink/renderer/core/css/media_query_exp.cc
@@ -80,11 +80,9 @@
            ident == CSSValueID::kRec2020;
   }
 
-  if (RuntimeEnabledFeatures::MediaQueryPrefersColorSchemeEnabled()) {
-    if (media_feature == media_feature_names::kPrefersColorSchemeMediaFeature) {
-      return ident == CSSValueID::kNoPreference || ident == CSSValueID::kDark ||
-             ident == CSSValueID::kLight;
-    }
+  if (media_feature == media_feature_names::kPrefersColorSchemeMediaFeature) {
+    return ident == CSSValueID::kNoPreference || ident == CSSValueID::kDark ||
+           ident == CSSValueID::kLight;
   }
 
   if (media_feature == media_feature_names::kPrefersReducedMotionMediaFeature)
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index 76abff7..ee40d4b 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -1494,8 +1494,6 @@
 }
 
 TEST_F(StyleEngineTest, MediaQueriesChangeColorScheme) {
-  ScopedMediaQueryPrefersColorSchemeForTest feature_scope(true);
-
   GetDocument().body()->SetInnerHTMLFromString(R"HTML(
     <style>
       body { color: red }
@@ -1521,8 +1519,6 @@
 }
 
 TEST_F(StyleEngineTest, MediaQueriesChangeColorSchemeForcedDarkMode) {
-  ScopedMediaQueryPrefersColorSchemeForTest feature_scope(true);
-
   GetDocument().GetSettings()->SetForceDarkModeEnabled(true);
   ColorSchemeHelper color_scheme_helper;
   color_scheme_helper.SetPreferredColorScheme(GetDocument(),
@@ -1668,8 +1664,6 @@
 }
 
 TEST_F(StyleEngineTest, MediaQueriesColorSchemeOverride) {
-  ScopedMediaQueryPrefersColorSchemeForTest feature_scope(true);
-
   EXPECT_EQ(PreferredColorScheme::kLight,
             Platform::Current()->ThemeEngine()->PreferredColorScheme());
 
diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc
index e6a8b0c..9d0ac92 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1212,14 +1212,6 @@
   DCHECK(AllInstances().Contains(this));
   AllInstances().erase(this);
 
-  if (does_composite_) {
-    // This must occur before WillBeDestroyed, since detaching the main frame
-    // will also destroy the WebWidgetClient and the AnimationHost.
-    // TODO(danakj): Since the AnimationHost will be destroyed anyways, there
-    // is probably no good reason to do this at all.
-    GetPage()->WillCloseAnimationHost(nullptr);
-  }
-
   // Initiate shutdown for the entire frameset.  This will cause a lot of
   // notifications to be sent. This will detach all frames in this WebView's
   // frame tree.
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
index 6af3e58..e8b47867 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -21,6 +21,7 @@
 WebViewFrameWidget::~WebViewFrameWidget() = default;
 
 void WebViewFrameWidget::Close() {
+  GetPage()->WillCloseAnimationHost(nullptr);
   // Closing the WebViewFrameWidget happens in response to the local main frame
   // being detached from the Page/WebViewImpl.
   web_view_->SetWebWidget(nullptr);
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker.css b/third_party/blink/renderer/core/html/forms/resources/color_picker.css
index ed9cc009..7ce1de1 100644
--- a/third_party/blink/renderer/core/html/forms/resources/color_picker.css
+++ b/third_party/blink/renderer/core/html/forms/resources/color_picker.css
@@ -16,12 +16,12 @@
   background: #FFFFFF;
   display: flex;
   flex-direction: column;
-  height: 304px;
+  height: 250px;
   width: 232px;
 }
 
 visual-color-picker {
-  height: 59%;
+  height: 71.5%;
   min-height: 0;
 }
 
@@ -167,31 +167,6 @@
   width: 172px;
 }
 
-submission-controls {
-  align-items: center;
-  border-top: 1px solid #CECECE;
-  display: flex;
-  flex-direction: row;
-  height: 13%;
-  min-height: 0;
-}
-
-#submission-controls-padding {
-  height: 100%;
-  width: 84%;
-}
-
-submission-button {
-  padding: 3%;
-  text-align: center;
-  width: 8%;
-}
-
-submission-button:hover {
-  background-color: #F3F3F3;
-  border-radius: 2px;
-}
-
 @media (forced-colors: active) {
   color-viewer {
     forced-color-adjust: none;
diff --git a/third_party/blink/renderer/core/html/forms/resources/color_picker.js b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
index a91dcc3..e02931ec 100644
--- a/third_party/blink/renderer/core/html/forms/resources/color_picker.js
+++ b/third_party/blink/renderer/core/html/forms/resources/color_picker.js
@@ -425,14 +425,11 @@
     super();
 
     this.selectedColor_ = initialColor;
+    this.colorWhenOpened_ = initialColor;
 
     this.visualColorPicker_ = new VisualColorPicker(initialColor);
     this.manualColorPicker_ = new ManualColorPicker(initialColor);
-    this.submissionControls_ = new SubmissionControls(
-        this.onSubmitButtonClick_, this.onCancelButtonClick_);
-    this.append(
-        this.visualColorPicker_, this.manualColorPicker_,
-        this.submissionControls_);
+    this.append(this.visualColorPicker_, this.manualColorPicker_);
 
     this.visualColorPicker_.addEventListener(
         'visual-color-picker-initialized', this.initializeListeners_);
@@ -480,6 +477,9 @@
       this.processingManualColorChange_ = true;
       this.visualColorPicker_.color = newColor;
       this.processingManualColorChange_ = false;
+
+      const selectedValue = newColor.asHex();
+      window.pagePopupController.setValue(selectedValue);
     }
   }
 
@@ -492,6 +492,9 @@
       if (!this.processingManualColorChange_) {
         this.selectedColor = newColor;
         this.manualColorPicker_.color = newColor;
+
+        const selectedValue = newColor.asHex();
+        window.pagePopupController.setValue(selectedValue);
       } else {
         // We are making a visual color change in response to a manual color
         // change. So we do not overwrite the manually specified values and do
@@ -506,10 +509,16 @@
   onKeyDown_ = (event) => {
     switch(event.key) {
       case 'Enter':
-        this.submissionControls_.submitButton.click();
+        window.pagePopupController.closePopup();
         break;
       case 'Escape':
-        this.submissionControls_.cancelButton.click();
+        if (this.selectedColor.equals(this.colorWhenOpened_)) {
+          window.pagePopupController.closePopup();
+        } else {
+          this.manualColorPicker_.dispatchEvent(new CustomEvent(
+              'manual-color-change',
+              {bubbles: true, detail: {color: this.colorWhenOpened_}}));
+        }
         break;
       case 'Tab':
         event.preventDefault();
@@ -539,21 +548,6 @@
         'color-value-container:not(.hidden-color-value-container) > input,' +
         '[tabindex]:not([tabindex=\'-1\'])'));
   }
-
-  static get COMMIT_DELAY_MS() {
-    return 100;
-  }
-
-  onSubmitButtonClick_ = () => {
-    const selectedValue = this.selectedColor_.asHex();
-    window.setTimeout(function() {
-      window.pagePopupController.setValueAndClosePopup(0, selectedValue);
-    }, ColorPicker.COMMIT_DELAY_MS);
-  };
-
-  onCancelButtonClick_ = () => {
-    window.pagePopupController.closePopup();
-  };
 }
 window.customElements.define('color-picker', ColorPicker);
 
@@ -1873,65 +1867,3 @@
   }
 }
 window.customElements.define('channel-label', ChannelLabel);
-
-/**
- * SubmissionControls: Provides functionality to submit or discard a change.
- */
-class SubmissionControls extends HTMLElement {
-  /**
-   * @param {function} submitCallback executed if the submit button is clicked
-   * @param {function} cancelCallback executed if the cancel button is clicked
-   */
-  constructor(submitCallback, cancelCallback) {
-    super();
-
-    const padding = document.createElement('span');
-    padding.setAttribute('id', 'submission-controls-padding');
-    this.append(padding);
-
-    this.submitButton_ = new SubmissionButton(
-        submitCallback,
-        '<svg width="14" height="10" viewBox="0 0 14 10" fill="none" ' +
-            'xmlns="http://www.w3.org/2000/svg"><path d="M13.3516 ' +
-            '1.35156L5 9.71094L0.648438 5.35156L1.35156 4.64844L5 ' +
-            '8.28906L12.6484 0.648438L13.3516 1.35156Z" fill="WindowText"/></svg>');
-    this.cancelButton_ = new SubmissionButton(
-        cancelCallback,
-        '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" ' +
-            'xmlns="http://www.w3.org/2000/svg"><path d="M7.71094 7L13.1016 ' +
-            '12.3984L12.3984 13.1016L7 7.71094L1.60156 13.1016L0.898438 ' +
-            '12.3984L6.28906 7L0.898438 1.60156L1.60156 0.898438L7 ' +
-            '6.28906L12.3984 0.898438L13.1016 1.60156L7.71094 7Z" ' +
-            'fill="WindowText"/></svg>');
-    this.append(this.submitButton_, this.cancelButton_);
-  }
-
-  get submitButton() {
-    return this.submitButton_;
-  }
-
-  get cancelButton() {
-    return this.cancelButton_;
-  }
-}
-window.customElements.define('submission-controls', SubmissionControls);
-
-/**
- * SubmissionButton: Button with a custom look that can be clicked for
- *                   a submission action.
- */
-class SubmissionButton extends HTMLElement {
-  /**
-   * @param {function} clickCallback executed when the button is clicked
-   * @param {string} htmlString custom look for the button
-   */
-  constructor(clickCallback, htmlString) {
-    super();
-
-    this.setAttribute('tabIndex', '0');
-    this.innerHTML = htmlString;
-
-    this.addEventListener('click', clickCallback);
-  }
-}
-window.customElements.define('submission-button', SubmissionButton);
diff --git a/third_party/blink/renderer/core/html/html_meta_element_test.cc b/third_party/blink/renderer/core/html/html_meta_element_test.cc
index 83b8019..39e8faf 100644
--- a/third_party/blink/renderer/core/html/html_meta_element_test.cc
+++ b/third_party/blink/renderer/core/html/html_meta_element_test.cc
@@ -30,13 +30,11 @@
 class HTMLMetaElementTest : public PageTestBase,
                             private ScopedDisplayCutoutAPIForTest,
                             private ScopedMetaColorSchemeForTest,
-                            private ScopedMediaQueryPrefersColorSchemeForTest,
                             private ScopedCSSColorSchemeForTest {
  public:
   HTMLMetaElementTest()
       : ScopedDisplayCutoutAPIForTest(true),
         ScopedMetaColorSchemeForTest(true),
-        ScopedMediaQueryPrefersColorSchemeForTest(true),
         ScopedCSSColorSchemeForTest(true) {}
   void SetUp() override {
     PageTestBase::SetUp();
diff --git a/third_party/blink/renderer/core/inspector/inspector_task_runner.cc b/third_party/blink/renderer/core/inspector/inspector_task_runner.cc
index 8df688f..ec227952 100644
--- a/third_party/blink/renderer/core/inspector/inspector_task_runner.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_task_runner.cc
@@ -38,8 +38,10 @@
       CrossThreadBindOnce(
           &InspectorTaskRunner::PerformSingleInterruptingTaskDontWait,
           WrapRefCounted(this)));
-  if (isolate_)
+  if (isolate_) {
+    AddRef();
     isolate_->RequestInterrupt(&V8InterruptCallback, this);
+  }
 }
 
 void InspectorTaskRunner::AppendTaskDontInterrupt(Task task) {
@@ -69,6 +71,7 @@
 void InspectorTaskRunner::V8InterruptCallback(v8::Isolate*, void* data) {
   InspectorTaskRunner* runner = static_cast<InspectorTaskRunner*>(data);
   Task task = runner->TakeNextInterruptingTask();
+  runner->Release();
   if (!task) {
     return;
   }
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
index 17d1747c..2407dabc 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
@@ -464,17 +464,19 @@
   return size_of_auto_margin;
 }
 
-void FlexLine::ComputeLineItemsPosition(LayoutUnit main_axis_offset,
+void FlexLine::ComputeLineItemsPosition(LayoutUnit main_axis_start_offset,
+                                        LayoutUnit main_axis_end_offset,
                                         LayoutUnit& cross_axis_offset) {
-  this->main_axis_offset = main_axis_offset;
+  this->main_axis_offset = main_axis_start_offset;
   // Recalculate the remaining free space. The adjustment for flex factors
   // between 0..1 means we can't just use remainingFreeSpace here.
-  remaining_free_space = container_main_inner_size;
+  LayoutUnit total_item_size;
   for (size_t i = 0; i < line_items.size(); ++i) {
     FlexItem& flex_item = line_items[i];
     DCHECK(!flex_item.box->IsOutOfFlowPositioned());
-    remaining_free_space -= flex_item.FlexedMarginBoxSize();
+    total_item_size += flex_item.FlexedMarginBoxSize();
   }
+  remaining_free_space = container_main_inner_size - total_item_size;
 
   const StyleContentAlignmentData justify_content =
       FlexLayoutAlgorithm::ResolvedJustifyContent(*algorithm->Style());
@@ -486,7 +488,7 @@
           algorithm->StyleRef(), available_free_space, justify_content,
           line_items.size());
 
-  main_axis_offset += initial_position;
+  LayoutUnit main_axis_offset = initial_position;
   sum_justify_adjustments += initial_position;
   LayoutUnit max_descent;  // Used when align-items: baseline.
   LayoutUnit max_child_cross_axis_extent;
@@ -499,11 +501,21 @@
         !algorithm->StyleRef().ResolvedIsColumnFlexDirection() &&
         !algorithm->IsLeftToRightFlow();
   }
-  LayoutUnit width_for_rtl = container_logical_width;
-  // -webkit-box always started layout at an origin of 0, regardless of
-  // direction.
-  if (should_flip_main_axis && algorithm->StyleRef().IsDeprecatedWebkitBox())
-    width_for_rtl = sum_hypothetical_main_size;
+  LayoutUnit width_when_flipped = container_logical_width;
+  LayoutUnit flipped_offset;
+  // -webkit-box, always did layout starting at 0. ltr and reverse were handled
+  // by reversing the order of iteration. OTOH, flex always iterates in order
+  // and flips the main coordinate. The following gives the same behavior for
+  // -webkit-box while using the same iteration order as flex does by changing
+  // how the flipped coordinate is calculated.
+  if (should_flip_main_axis && algorithm->StyleRef().IsDeprecatedWebkitBox()) {
+    // -webkit-box only distributed space when > 0.
+    width_when_flipped =
+        total_item_size + available_free_space.ClampNegativeToZero();
+    flipped_offset = main_axis_end_offset;
+  } else {
+    main_axis_offset += main_axis_start_offset;
+  }
   for (size_t i = 0; i < line_items.size(); ++i) {
     FlexItem& flex_item = line_items[i];
 
@@ -536,11 +548,11 @@
     // In an RTL column situation, this will apply the margin-right/margin-end
     // on the left. This will be fixed later in
     // LayoutFlexibleBox::FlipForRightToLeftColumn.
-    flex_item.desired_location =
-        LayoutPoint(should_flip_main_axis
-                        ? width_for_rtl - main_axis_offset - child_main_extent
-                        : main_axis_offset,
-                    cross_axis_offset + flex_item.FlowAwareMarginBefore());
+    flex_item.desired_location = LayoutPoint(
+        should_flip_main_axis ? width_when_flipped - main_axis_offset -
+                                    child_main_extent + flipped_offset
+                              : main_axis_offset,
+        cross_axis_offset + flex_item.FlowAwareMarginBefore());
     main_axis_offset += child_main_extent + flex_item.FlowAwareMarginEnd();
 
     if (i != line_items.size() - 1) {
@@ -835,11 +847,14 @@
   ContentPosition position;
   if (is_webkit_box) {
     position = BoxPackToContentPosition(style.BoxPack());
-    // -webkit-box treats end as start for horizontal rtl.
-    if (position == ContentPosition::kFlexEnd &&
-        !style.ResolvedIsColumnReverseFlexDirection() &&
-        !style.IsLeftToRightDirection()) {
-      position = ContentPosition::kFlexStart;
+    // As row-reverse does layout in reverse, it effectively swaps end & start.
+    // -webkit-box didn't do this (-webkit-box always did layout starting at 0,
+    // and increasing).
+    if (style.ResolvedIsRowReverseFlexDirection()) {
+      if (position == ContentPosition::kFlexEnd)
+        position = ContentPosition::kFlexStart;
+      else if (position == ContentPosition::kFlexStart)
+        position = ContentPosition::kFlexEnd;
     }
   } else {
     position =
@@ -903,10 +918,8 @@
     LayoutUnit available_free_space,
     const StyleContentAlignmentData& data,
     unsigned number_of_items) {
-  if (available_free_space <= 0 && style.IsDeprecatedWebkitBox() &&
-      style.ResolvedIsColumnFlexDirection()) {
-    // -webkit-box with vertical orientation and no available spaces positions
-    // relative to the start regardless of ContentPosition.
+  if (available_free_space <= 0 && style.IsDeprecatedWebkitBox()) {
+    // -webkit-box only considers |available_free_space| if > 0.
     return LayoutUnit();
   }
   if (data.GetPosition() == ContentPosition::kFlexEnd)
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
index d26e1367..1818ff6 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.h
@@ -297,6 +297,7 @@
   // cross_axis_size needs to be set correctly on each flex item (to the size
   // the item has without stretching).
   void ComputeLineItemsPosition(LayoutUnit main_axis_offset,
+                                LayoutUnit main_axis_end_offset,
                                 LayoutUnit& cross_axis_offset);
 
   FlexLayoutAlgorithm* algorithm;
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
index 25476355..d15a4d8 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box.cc
@@ -962,8 +962,9 @@
 
     LayoutLineItems(current_line, relayout_children, layout_scope);
 
-    LayoutUnit main_axis_offset = FlowAwareContentInsetStart();
-    current_line->ComputeLineItemsPosition(main_axis_offset, cross_axis_offset);
+    current_line->ComputeLineItemsPosition(FlowAwareContentInsetStart(),
+                                           FlowAwareContentInsetEnd(),
+                                           cross_axis_offset);
     ApplyLineItemsPosition(current_line);
     if (number_of_in_flow_children_on_first_line_ == -1) {
       number_of_in_flow_children_on_first_line_ =
diff --git a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
index f689dd07..1f6d439 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_flex_layout_algorithm.cc
@@ -577,15 +577,24 @@
 scoped_refptr<const NGLayoutResult> NGFlexLayoutAlgorithm::Layout() {
   ConstructAndAppendFlexItems();
 
-  LayoutUnit main_axis_offset = border_scrollbar_padding_.inline_start;
+  LayoutUnit main_axis_start_offset;
+  LayoutUnit main_axis_end_offset;
   LayoutUnit cross_axis_offset = border_scrollbar_padding_.block_start;
   if (is_column_) {
-    main_axis_offset = Style().ResolvedIsColumnReverseFlexDirection()
-                           ? LayoutUnit()
-                           : border_scrollbar_padding_.block_start;
+    const bool is_column_reverse =
+        Style().ResolvedIsColumnReverseFlexDirection();
+    main_axis_start_offset = is_column_reverse
+                                 ? LayoutUnit()
+                                 : border_scrollbar_padding_.block_start;
+    main_axis_end_offset =
+        is_column_reverse ? LayoutUnit() : border_scrollbar_padding_.block_end;
     cross_axis_offset = border_scrollbar_padding_.inline_start;
   } else if (Style().ResolvedIsRowReverseFlexDirection()) {
-    main_axis_offset = border_scrollbar_padding_.inline_end;
+    main_axis_start_offset = border_scrollbar_padding_.inline_end;
+    main_axis_end_offset = border_scrollbar_padding_.inline_start;
+  } else {
+    main_axis_start_offset = border_scrollbar_padding_.inline_start;
+    main_axis_end_offset = border_scrollbar_padding_.inline_end;
   }
   FlexLine* line;
   while (
@@ -657,7 +666,8 @@
     }
     // cross_axis_offset is updated in each iteration of the loop, for passing
     // in to the next iteration.
-    line->ComputeLineItemsPosition(main_axis_offset, cross_axis_offset);
+    line->ComputeLineItemsPosition(main_axis_start_offset, main_axis_end_offset,
+                                   cross_axis_offset);
   }
 
   LayoutUnit intrinsic_block_size = algorithm_->IntrinsicContentBlockSize() +
diff --git a/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc
index 677abb96..c898f4a 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_inline_box_fragment_painter.cc
@@ -178,22 +178,34 @@
     LayoutUnit* offset_on_line,
     LayoutUnit* total_width) const {
   WritingMode writing_mode = inline_box_fragment_.Style().GetWritingMode();
-  NGPaintFragment::FragmentRange fragments =
-      inline_box_paint_fragment_->InlineFragmentsFor(
-          inline_box_fragment_.GetLayoutObject());
+  NGInlineCursor cursor;
+  DCHECK(inline_box_fragment_.GetLayoutObject());
+  cursor.MoveTo(*inline_box_fragment_.GetLayoutObject());
 
   LayoutUnit before;
   LayoutUnit after;
   bool before_self = true;
-  for (auto iter = fragments.begin(); iter != fragments.end(); ++iter) {
-    if (*iter == inline_box_paint_fragment_) {
-      before_self = false;
-      continue;
+  for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
+    if (inline_box_paint_fragment_) {
+      DCHECK(cursor.CurrentPaintFragment());
+      if (cursor.CurrentPaintFragment() == inline_box_paint_fragment_) {
+        before_self = false;
+        continue;
+      }
+    } else {
+      DCHECK(inline_box_item_);
+      DCHECK(cursor.CurrentItem());
+      if (cursor.CurrentItem() == inline_box_item_) {
+        before_self = false;
+        continue;
+      }
     }
+    const NGPhysicalBoxFragment* box_fragment = cursor.CurrentBoxFragment();
+    DCHECK(box_fragment);
     if (before_self)
-      before += NGFragment(writing_mode, iter->PhysicalFragment()).InlineSize();
+      before += NGFragment(writing_mode, *box_fragment).InlineSize();
     else
-      after += NGFragment(writing_mode, iter->PhysicalFragment()).InlineSize();
+      after += NGFragment(writing_mode, *box_fragment).InlineSize();
   }
 
   NGFragment logical_fragment(writing_mode, inline_box_fragment_);
diff --git a/third_party/blink/renderer/modules/hid/hid.cc b/third_party/blink/renderer/modules/hid/hid.cc
index d855bb70..0ad46ee 100644
--- a/third_party/blink/renderer/modules/hid/hid.cc
+++ b/third_party/blink/renderer/modules/hid/hid.cc
@@ -26,7 +26,6 @@
 const char kContextGone[] = "Script context has shut down.";
 const char kFeaturePolicyBlocked[] =
     "Access to the feature \"hid\" is disallowed by feature policy.";
-const char kNoDeviceSelected[] = "No device selected.";
 
 void RejectWithTypeError(const String& message,
                          ScriptPromiseResolver* resolver) {
@@ -211,17 +210,15 @@
 
 void HID::FinishRequestDevice(
     ScriptPromiseResolver* resolver,
-    device::mojom::blink::HidDeviceInfoPtr device_info) {
+    Vector<device::mojom::blink::HidDeviceInfoPtr> device_infos) {
   DCHECK(request_device_promises_.Contains(resolver));
   request_device_promises_.erase(resolver);
 
-  if (device_info) {
-    resolver->Resolve(GetOrCreateDevice(std::move(device_info)));
-  } else {
-    resolver->Reject(MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kNotFoundError, kNoDeviceSelected));
-  }
-  request_device_promises_.erase(resolver);
+  HeapVector<Member<HIDDevice>> devices;
+  for (auto& device_info : device_infos)
+    devices.push_back(GetOrCreateDevice(std::move(device_info)));
+
+  resolver->Resolve(devices);
 }
 
 void HID::EnsureServiceConnection() {
@@ -250,10 +247,8 @@
 
   HeapHashSet<Member<ScriptPromiseResolver>> request_device_promises;
   request_device_promises_.swap(request_device_promises);
-  for (ScriptPromiseResolver* resolver : request_device_promises) {
-    resolver->Reject(MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kNotFoundError, kNoDeviceSelected));
-  }
+  for (ScriptPromiseResolver* resolver : request_device_promises)
+    resolver->Resolve(HeapVector<Member<HIDDevice>>());
 }
 
 void HID::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/modules/hid/hid.h b/third_party/blink/renderer/modules/hid/hid.h
index c5b96ea..773b906 100644
--- a/third_party/blink/renderer/modules/hid/hid.h
+++ b/third_party/blink/renderer/modules/hid/hid.h
@@ -67,7 +67,7 @@
   void FinishGetDevices(ScriptPromiseResolver*,
                         Vector<device::mojom::blink::HidDeviceInfoPtr>);
   void FinishRequestDevice(ScriptPromiseResolver*,
-                           device::mojom::blink::HidDeviceInfoPtr);
+                           Vector<device::mojom::blink::HidDeviceInfoPtr>);
 
   mojo::Remote<mojom::blink::HidService> service_;
   HeapHashSet<Member<ScriptPromiseResolver>> get_devices_promises_;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
index 7201670..21a3c0d 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -522,6 +522,8 @@
 
   // Experimental options provided at creation.
   webrtc::Config config;
+  config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(
+      properties.goog_experimental_noise_suppression));
 
   // If the experimental AGC is enabled, check for overridden config params.
   if (properties.goog_experimental_auto_gain_control) {
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index 0e8b5e0..5431e1c9 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1008,10 +1008,6 @@
       name: "MediaQueryNavigationControls",
     },
     {
-      name: "MediaQueryPrefersColorScheme",
-      status: "stable",
-    },
-    {
       name: "MediaQueryShape",
       status: "experimental",
     },
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
index 1f638a8..f7088043 100755
--- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
+++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -750,6 +750,7 @@
             'webrtc::EchoCanceller3Config',
             'webrtc::EchoCanceller3Factory',
             'webrtc::ExperimentalAgc',
+            'webrtc::ExperimentalNs',
             'webrtc::MediaStreamTrackInterface',
             'webrtc::ObserverInterface',
             'webrtc::StreamConfig',
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 2fd6a49fb..2345cea1 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -7221,23 +7221,6 @@
 crbug.com/1038656 [ Mac ] http/tests/devtools/coverage/coverage-view-unused.js [ Pass Failure ]
 crbug.com/1038656 [ Win ] http/tests/devtools/coverage/coverage-view-unused.js [ Pass Failure ]
 
-# Temporarily disabled to land breakpoints changes in devtools
-crbug.com/963183 http/tests/devtools/persistence/persistence-move-breakpoints.js [ Pass Failure ]
-crbug.com/963183 [ Mac ] http/tests/devtools/persistence/persistence-move-breakpoints-on-reload.js [ Pass Failure ]
-crbug.com/963183 [ Win ] http/tests/devtools/persistence/persistence-move-breakpoints-on-reload.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger-breakpoints/breakpoints-sidebar-pane.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger-breakpoints/set-breakpoint.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger-breakpoints/set-logpoint.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/sources/debugger/js-with-inline-stylesheets.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1.js [ Pass Failure ]
-crbug.com/963183 http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL.js [ Pass Failure ]
-
 crbug.com/1030086 external/wpt/html/infrastructure/safe-passing-of-structured-data/structured-cloning-error-extra.html [ Pass Failure ]
 crbug.com/1030086 external/wpt/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html [ Pass Failure ]
 
diff --git a/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-reverse-variants-ref.html b/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-reverse-variants-ref.html
new file mode 100644
index 0000000..72d0b2c6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-reverse-variants-ref.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<style>
+.flexbox {
+  width: 300px;
+  display: flex;
+  border-style: solid;
+  border-width: 1px 2px 1px 4px;
+}
+
+.reverse {
+  flex-direction: row-reverse;
+}
+
+.redbox {
+  background: red;
+  width: 100px;
+  flex-shrink: 0;
+}
+
+.greenbox {
+  background: green;
+  width: 100px;
+  flex-shrink: 0;
+}
+
+</style>
+<p>Exercises -webkit-box with various box-pack settings when direction is
+   reverse. In all cases the order of letters in the boxes should be
+   alphabetized left to right.
+<p>Start. Boxes should be aligned to left side.
+<div class="flexbox reverse" style="justify-content: flex-end;">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Centered. Boxes should be centered.
+<div class="flexbox reverse" style="justify-content: center;">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>End. Boxes should be aligned to right side.
+<div class="flexbox reverse">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Start, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="flexbox">
+  <div class="greenbox">A</div>
+  <div class="redbox">B</div>
+  <div class="greenbox">C</div>
+  <div class="redbox">D</div>
+</div>
+
+<p>Center, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="flexbox">
+  <div class="greenbox">A</div>
+  <div class="redbox">B</div>
+  <div class="greenbox">C</div>
+  <div class="redbox">D</div>
+</div>
+
+<p>End, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="flexbox">
+  <div class="greenbox">A</div>
+  <div class="redbox">B</div>
+  <div class="greenbox">C</div>
+  <div class="redbox">D</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-reverse-variants.html b/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-reverse-variants.html
new file mode 100644
index 0000000..09dba69
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-reverse-variants.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1034461">
+<link rel="match" href="webkit-box-horizontal-reverse-variants-ref.html">
+<style>
+.webkitbox {
+  width: 300px;
+  display: -webkit-box;
+  -webkit-box-direction: reverse;
+  border-style: solid;
+  border-width: 1px 2px 1px 4px;
+}
+
+.redbox {
+  background: red;
+  width: 100px;
+}
+
+.greenbox {
+  background: green;
+  width: 100px;
+}
+
+</style>
+<p>Exercises -webkit-box with various box-pack settings when direction is
+   reverse. In all cases the order of letters in the boxes should be
+   alphabetized left to right.
+<p>Start. Boxes should be aligned to left side.
+<div class="webkitbox">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Centered. Boxes should be centered.
+<div class="webkitbox" style="-webkit-box-pack: center">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>End. Boxes should be aligned to right side.
+<div class="webkitbox" style="-webkit-box-pack: end">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Start, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="webkitbox">
+  <div class="redbox">D</div>
+  <div class="greenbox">C</div>
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Center, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="webkitbox" style="-webkit-box-pack: center">
+  <div class="redbox">D</div>
+  <div class="greenbox">C</div>
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>End, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="webkitbox" style="-webkit-box-pack: end">
+  <div class="redbox">D</div>
+  <div class="greenbox">C</div>
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-rtl-variants-ref.html b/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-rtl-variants-ref.html
new file mode 100644
index 0000000..42321c5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-rtl-variants-ref.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<style>
+.flexbox {
+  width: 300px;
+  display: flex;
+  border-style: solid;
+  border-width: 1px 2px 1px 4px;
+}
+
+.rtl {
+  direction: rtl;
+}
+
+.redbox {
+  background: red;
+  width: 100px;
+  flex-shrink: 0;
+}
+
+.greenbox {
+  background: green;
+  width: 100px;
+  flex-shrink: 0;
+}
+
+</style>
+<p>Exercises -webkit-box with various box-pack settings when rtl. In all cases
+   the order of letters in the boxes should be alphabetized left to right.
+<p>Start. Boxes should be aligned to right side.
+<div class="flexbox rtl">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Centered. Boxes should be centered.
+<div class="flexbox rtl" style="justify-content: center;">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>End. Boxes should be aligned to left side.
+<div class="flexbox rtl" style="justify-content: flex-end;">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Start, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="flexbox">
+  <div class="greenbox rtl">A</div>
+  <div class="redbox rtl">B</div>
+  <div class="greenbox rtl">C</div>
+  <div class="redbox rtl">D</div>
+</div>
+
+<p>Center, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="flexbox">
+  <div class="greenbox rtl">A</div>
+  <div class="redbox rtl">B</div>
+  <div class="greenbox rtl">C</div>
+  <div class="redbox rtl">D</div>
+</div>
+
+<p>End, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="flexbox">
+  <div class="greenbox rtl">A</div>
+  <div class="redbox rtl">B</div>
+  <div class="greenbox rtl">C</div>
+  <div class="redbox rtl">D</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-rtl-variants.html b/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-rtl-variants.html
new file mode 100644
index 0000000..085a589
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compat/webkit-box-horizontal-rtl-variants.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1034461">
+<link rel="match" href="webkit-box-horizontal-rtl-variants-ref.html">
+<style>
+.webkitbox {
+  width: 300px;
+  display: -webkit-box;
+  direction: rtl;
+  border-style: solid;
+  border-width: 1px 2px 1px 4px;
+}
+
+.redbox {
+  background: red;
+  width: 100px;
+}
+
+.greenbox {
+  background: green;
+  width: 100px;
+}
+
+</style>
+<p>Exercises -webkit-box with various box-pack settings when rtl. In all cases
+   the order of letters in the boxes should be alphabetized left to right.
+<p>Start. Boxes should be aligned to right side.
+<div class="webkitbox">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Centered. Boxes should be centered.
+<div class="webkitbox" style="-webkit-box-pack: center">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>End. Boxes should be aligned to left side.
+<div class="webkitbox" style="-webkit-box-pack: end">
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Start, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="webkitbox">
+  <div class="redbox">D</div>
+  <div class="greenbox">C</div>
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>Center, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="webkitbox" style="-webkit-box-pack: center">
+  <div class="redbox">D</div>
+  <div class="greenbox">C</div>
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
+
+<p>End, content too big. Boxes should start at left edge and extend
+   outside border.
+<div class="webkitbox" style="-webkit-box-pack: end">
+  <div class="redbox">D</div>
+  <div class="greenbox">C</div>
+  <div class="redbox">B</div>
+  <div class="greenbox">A</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/compat/webkit-box-rtl-flex-ref.html b/third_party/blink/web_tests/external/wpt/compat/webkit-box-rtl-flex-ref.html
new file mode 100644
index 0000000..10b9eb73
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compat/webkit-box-rtl-flex-ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<style>
+#flexbox {
+  width: 500px;
+  display: flex;
+  direction: rtl;
+}
+
+#redbox {
+  background: red;
+  flex: 1;
+}
+
+#greenbox {
+  background: green;
+  flex: 1;
+}
+
+</style>
+<p>There should be a green box followed by a red box, both on the same line.
+<div id="flexbox">
+  <div id="redbox">A</div>
+  <div id="greenbox">B</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/compat/webkit-box-rtl-flex.html b/third_party/blink/web_tests/external/wpt/compat/webkit-box-rtl-flex.html
new file mode 100644
index 0000000..d20b3a0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/compat/webkit-box-rtl-flex.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/1034461">
+<link rel="match" href="webkit-box-rtl-flex-ref.html">
+<style>
+#webkitbox {
+  width: 500px;
+  display: -webkit-box;
+  direction: rtl;
+}
+
+#redbox {
+  background: red;
+  -webkit-box-flex: 1;
+}
+
+#greenbox {
+  background: green;
+  -webkit-box-flex: 1;
+}
+
+</style>
+<p>There should be a green box followed by a red box, both on the same line.
+<div id="webkitbox">
+  <div id="redbox">A</div>
+  <div id="greenbox">B</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-expected.txt
new file mode 100644
index 0000000..29efcb5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-expected.txt
@@ -0,0 +1,189 @@
+This is a testharness.js-based test.
+FAIL .grid 1 assert_equals: 
+<div class="grid alignContentSpaceBetween" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 260 but got 40
+FAIL .grid 2 assert_equals: 
+<div class="grid alignContentSpaceAround" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="55" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="55" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="205" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="205" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 55 but got 0
+FAIL .grid 3 assert_equals: 
+<div class="grid alignContentSpaceEvenly" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="73" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="73" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="187" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="187" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 73 but got 0
+PASS .grid 4
+FAIL .grid 5 assert_equals: 
+<div class="grid alignContentSpaceBetween" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 130 but got 40
+FAIL .grid 6 assert_equals: 
+<div class="grid alignContentSpaceAround" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="30" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="30" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="230" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="230" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 30 but got 0
+FAIL .grid 7 assert_equals: 
+<div class="grid alignContentSpaceEvenly" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="45" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="45" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="215" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="215" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 45 but got 0
+PASS .grid 8
+FAIL .grid 9 assert_equals: 
+<div class="grid alignContentSpaceBetween" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="87" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="87" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="173" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="173" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="20" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 87 but got 40
+FAIL .grid 10 assert_equals: 
+<div class="grid alignContentSpaceAround" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="18" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="18" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="93" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="93" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="168" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="168" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="243" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="20" data-offset-y="243" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 18 but got 0
+FAIL .grid 11 assert_equals: 
+<div class="grid alignContentSpaceEvenly" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="28" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="28" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="96" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="96" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="164" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="164" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="232" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="20" data-offset-y="232" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 28 but got 0
+PASS .grid 12
+FAIL .grid 13 assert_equals: 
+<div class="grid alignContentSpaceBetween directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 260 but got 40
+FAIL .grid 14 assert_equals: 
+<div class="grid alignContentSpaceAround directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="55" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="55" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="205" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="205" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 55 but got 0
+FAIL .grid 15 assert_equals: 
+<div class="grid alignContentSpaceEvenly directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="73" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="73" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="187" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="187" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 73 but got 0
+PASS .grid 16
+FAIL .grid 17 assert_equals: 
+<div class="grid alignContentSpaceBetween directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 130 but got 40
+FAIL .grid 18 assert_equals: 
+<div class="grid alignContentSpaceAround directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="30" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="30" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="230" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="230" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 30 but got 0
+FAIL .grid 19 assert_equals: 
+<div class="grid alignContentSpaceEvenly directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="45" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="45" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="215" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="215" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 45 but got 0
+PASS .grid 20
+FAIL .grid 21 assert_equals: 
+<div class="grid alignContentSpaceBetween directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="87" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="87" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="173" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="173" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="180" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="160" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 87 but got 40
+FAIL .grid 22 assert_equals: 
+<div class="grid alignContentSpaceAround directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="18" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="18" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="93" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="93" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="168" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="168" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="180" data-offset-y="243" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="160" data-offset-y="243" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 18 but got 0
+FAIL .grid 23 assert_equals: 
+<div class="grid alignContentSpaceEvenly directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="28" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="28" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="96" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="96" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="164" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="164" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="180" data-offset-y="232" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="160" data-offset-y="232" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+offsetTop expected 28 but got 0
+PASS .grid 24
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-lr-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-lr-expected.txt
new file mode 100644
index 0000000..82b19bbb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-lr-expected.txt
@@ -0,0 +1,207 @@
+This is a testharness.js-based test.
+FAIL .grid 1 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 360 but got 40
+FAIL .grid 2 assert_equals: 
+<div class="grid alignContentSpaceAround verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="80" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="80" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="280" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="280" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 80 but got 0
+FAIL .grid 3 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="107" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="107" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="253" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="253" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 107 but got 0
+PASS .grid 4
+FAIL .grid 5 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 180 but got 40
+FAIL .grid 6 assert_equals: 
+<div class="grid alignContentSpaceAround verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="47" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="47" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="313" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="313" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 47 but got 0
+FAIL .grid 7 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="70" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="70" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="290" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="290" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 70 but got 0
+FAIL .grid 8 assert_equals: 
+<div class="grid stretchedGrid alignContentStretch verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="133" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="133" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="267" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="267" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+    </div>
+width expected 133 but got 134
+FAIL .grid 9 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="120" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="120" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="240" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="240" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 120 but got 40
+FAIL .grid 10 assert_equals: 
+<div class="grid alignContentSpaceAround verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="30" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="30" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="130" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="130" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="230" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="230" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="330" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="330" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 30 but got 0
+FAIL .grid 11 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="48" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="48" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="136" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="136" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="224" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="224" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="312" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="312" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 48 but got 0
+PASS .grid 12
+FAIL .grid 13 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 360 but got 40
+FAIL .grid 14 assert_equals: 
+<div class="grid alignContentSpaceAround verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="80" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="80" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="280" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="280" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 80 but got 0
+FAIL .grid 15 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="107" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="107" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="253" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="253" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 107 but got 0
+PASS .grid 16
+FAIL .grid 17 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 180 but got 40
+FAIL .grid 18 assert_equals: 
+<div class="grid alignContentSpaceAround verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="47" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="47" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="313" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="313" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 47 but got 0
+FAIL .grid 19 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="70" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="70" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="290" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="290" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 70 but got 0
+FAIL .grid 20 assert_equals: 
+<div class="grid stretchedGrid alignContentStretch verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="133" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="133" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="267" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="267" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+    </div>
+width expected 133 but got 134
+FAIL .grid 21 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="120" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="120" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="240" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="240" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 120 but got 40
+FAIL .grid 22 assert_equals: 
+<div class="grid alignContentSpaceAround verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="30" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="30" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="130" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="130" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="230" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="230" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="330" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="330" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 30 but got 0
+FAIL .grid 23 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="48" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="48" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="136" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="136" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="224" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="224" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="312" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="312" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 48 but got 0
+PASS .grid 24
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-lr.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-lr.html
new file mode 100644
index 0000000..8217f4d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-lr.html
@@ -0,0 +1,358 @@
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: aligned content distribution for vertical lr</title>
+<link rel="author" title="Rossana Monteriso" href="mailto:rmonteriso@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#distribution-values">
+<meta name="assert" content="This test checks that the align-content property is applied correctly when using content-distribution values for the vertical-lr writing mode.">
+<meta name="flags" content="ahem">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<link rel="stylesheet" href="/css/support/grid.css">
+
+<style>
+
+.grid {
+    grid-auto-columns: 20px;
+    grid-auto-rows: 40px;
+    position: relative;
+    width: 400px;
+    height: 300px;
+}
+
+.stretchedGrid {
+    grid-auto-rows: auto;
+}
+
+.thirdRowFirstColumn {
+  background-color: green;
+  grid-column: 1;
+  grid-row: 3;
+}
+
+.fourthRowFirstColumn {
+  background-color: deepskyblue;
+  grid-column: 1;
+  grid-row: 4;
+}
+
+.fourthRowSecondColumn {
+  background-color: maroon;
+  grid-column: 2;
+  grid-row: 4;
+}
+
+.thirdRowFirstColumn {
+    background-color: green;
+    grid-column: 1;
+    grid-row: 3;
+}
+
+.fourthRowFirstColumn {
+    background-color: deepskyblue;
+    grid-column: 1;
+    grid-row: 4;
+}
+
+.fourthRowSecondColumn {
+    background-color: maroon;
+    grid-column: 2;
+  	grid-row: 4;
+}
+
+</style>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the align-content property is applied correctly when using content-distribution values in vertical-lr writing mode.</p>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="80" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="80" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="280" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="280" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="107" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="107" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="253" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="253" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="200" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="200" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="200" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="200" data-offset-y="20" data-expected-width="200" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="47" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="47" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="313" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="313" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="70" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="70" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="290" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="290" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="133" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="133" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="267" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="267" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="120" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="120" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="240" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="240" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="30" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="30" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="130" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="130" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="230" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="230" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="330" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="330" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="48" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="48" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="136" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="136" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="224" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="224" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="312" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="312" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalLR" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="100" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="100" data-offset-y="0" data-expected-width="100" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="100" data-offset-y="20" data-expected-width="100" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="100" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="200" data-offset-y="20" data-expected-width="100" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="300" data-offset-y="0" data-expected-width="100" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="300" data-offset-y="20" data-expected-width="100" data-expected-height="20"></div>
+    </div>
+</div>
+
+<!-- RTL direction. -->
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="80" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="80" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="280" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="280" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="107" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="107" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="253" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="253" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="200" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="200" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="200" data-offset-y="280" data-expected-width="200" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="200" data-offset-y="260" data-expected-width="200" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="47" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="47" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="313" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="313" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="70" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="70" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="290" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="290" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="133" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="133" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="267" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="267" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="120" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="120" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="240" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="240" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="30" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="30" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="130" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="130" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="230" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="230" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="330" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="330" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="48" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="48" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="136" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="136" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="224" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="224" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="312" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="312" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalLR directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="100" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="100" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="100" data-offset-y="280" data-expected-width="100" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="100" data-offset-y="260" data-expected-width="100" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="200" data-offset-y="280" data-expected-width="100" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="200" data-offset-y="260" data-expected-width="100" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="300" data-offset-y="280" data-expected-width="100" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="300" data-offset-y="260" data-expected-width="100" data-expected-height="20"></div>
+    </div>
+</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-rl-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-rl-expected.txt
new file mode 100644
index 0000000..732395093
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-rl-expected.txt
@@ -0,0 +1,207 @@
+This is a testharness.js-based test.
+FAIL .grid 1 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 0 but got 320
+FAIL .grid 2 assert_equals: 
+<div class="grid alignContentSpaceAround verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="280" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="280" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="80" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="80" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 280 but got 360
+FAIL .grid 3 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="253" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="253" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="107" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="107" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 253 but got 360
+PASS .grid 4
+FAIL .grid 5 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 180 but got 320
+FAIL .grid 6 assert_equals: 
+<div class="grid alignContentSpaceAround verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="313" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="313" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="47" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="47" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 313 but got 360
+FAIL .grid 7 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="290" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="290" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="70" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="70" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 290 but got 360
+FAIL .grid 8 assert_equals: 
+<div class="grid stretchedGrid alignContentStretch verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="267" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="267" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="133" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="133" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+    </div>
+width expected 133 but got 134
+FAIL .grid 9 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="240" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="240" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="120" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="120" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 240 but got 320
+FAIL .grid 10 assert_equals: 
+<div class="grid alignContentSpaceAround verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="330" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="330" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="230" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="230" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="130" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="130" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="30" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="30" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 330 but got 360
+FAIL .grid 11 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="312" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="312" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="224" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="224" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="136" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="136" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="48" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="48" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 312 but got 360
+PASS .grid 12
+FAIL .grid 13 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 0 but got 320
+FAIL .grid 14 assert_equals: 
+<div class="grid alignContentSpaceAround verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="280" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="280" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="80" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="80" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 280 but got 360
+FAIL .grid 15 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="253" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="253" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="107" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="107" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 253 but got 360
+PASS .grid 16
+FAIL .grid 17 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 180 but got 320
+FAIL .grid 18 assert_equals: 
+<div class="grid alignContentSpaceAround verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="313" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="313" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="47" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="47" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 313 but got 360
+FAIL .grid 19 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="290" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="290" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="70" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="70" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 290 but got 360
+FAIL .grid 20 assert_equals: 
+<div class="grid stretchedGrid alignContentStretch verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="267" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="267" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="133" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="133" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+    </div>
+width expected 133 but got 134
+FAIL .grid 21 assert_equals: 
+<div class="grid alignContentSpaceBetween verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="240" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="240" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="120" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="120" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 240 but got 320
+FAIL .grid 22 assert_equals: 
+<div class="grid alignContentSpaceAround verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="330" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="330" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="230" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="230" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="130" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="130" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="30" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="30" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 330 but got 360
+FAIL .grid 23 assert_equals: 
+<div class="grid alignContentSpaceEvenly verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="312" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="312" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="224" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="224" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="136" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="136" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="48" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="48" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+offsetLeft expected 312 but got 360
+PASS .grid 24
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-rl.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-rl.html
new file mode 100644
index 0000000..096e3fd6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution-vertical-rl.html
@@ -0,0 +1,358 @@
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: aligned content distribution for vertical rl</title>
+<link rel="author" title="Rossana Monteriso" href="mailto:rmonteriso@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#distribution-values">
+<meta name="assert" content="This test checks that the align-content property is applied correctly when using content-distribution values for the vertical-rl writing mode.">
+<meta name="flags" content="ahem">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<link rel="stylesheet" href="/css/support/grid.css">
+
+<style>
+
+.grid {
+    grid-auto-columns: 20px;
+    grid-auto-rows: 40px;
+    position: relative;
+    width: 400px;
+    height: 300px;
+}
+
+.stretchedGrid {
+    grid-auto-rows: auto;
+}
+
+.thirdRowFirstColumn {
+  background-color: green;
+  grid-column: 1;
+  grid-row: 3;
+}
+
+.fourthRowFirstColumn {
+  background-color: deepskyblue;
+  grid-column: 1;
+  grid-row: 4;
+}
+
+.fourthRowSecondColumn {
+  background-color: maroon;
+  grid-column: 2;
+  grid-row: 4;
+}
+
+.thirdRowFirstColumn {
+    background-color: green;
+    grid-column: 1;
+    grid-row: 3;
+}
+
+.fourthRowFirstColumn {
+    background-color: deepskyblue;
+    grid-column: 1;
+    grid-row: 4;
+}
+
+.fourthRowSecondColumn {
+    background-color: maroon;
+    grid-column: 2;
+  	grid-row: 4;
+}
+
+</style>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the align-content property is applied correctly when using content-distribution values for vertical-rl mode.</p>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="280" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="280" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="80" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="80" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="253" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="253" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="107" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="107" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="200" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="200" data-offset-y="20" data-expected-width="200" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="200" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="200" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="313" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="313" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="47" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="47" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="290" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="290" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="70" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="70" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="267" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="267" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="133" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="133" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="133" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="240" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="240" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="120" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="120" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="330" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="330" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="230" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="230" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="130" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="130" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="30" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="30" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="312" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="312" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="224" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="224" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="136" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="136" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="48" data-offset-y="0" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="48" data-offset-y="20" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalRL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="300" data-offset-y="0" data-expected-width="100" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="300" data-offset-y="20" data-expected-width="100" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="100" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="200" data-offset-y="20" data-expected-width="100" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="100" data-offset-y="0" data-expected-width="100" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="100" data-offset-y="20" data-expected-width="100" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="0" data-offset-y="20" data-expected-width="100" data-expected-height="20"></div>
+    </div>
+</div>
+
+<!-- RTL direction. -->
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="280" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="280" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="80" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="80" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="253" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="253" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="107" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="107" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="200" data-offset-y="280" data-expected-width="200" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="200" data-offset-y="260" data-expected-width="200" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="200" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="200" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="313" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="313" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="47" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="47" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="290" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="290" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="180" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="70" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="70" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="267" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="267" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="133" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="133" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="133" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="133" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="360" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="360" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="240" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="240" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="120" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="120" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="330" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="330" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="230" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="230" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="130" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="130" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="30" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="30" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="312" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="312" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="224" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="224" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="136" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="136" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="48" data-offset-y="280" data-expected-width="40" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="48" data-offset-y="260" data-expected-width="40" data-expected-height="20"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch verticalRL directionRTL" data-expected-width="400" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="300" data-offset-y="280" data-expected-width="100" data-expected-height="20"></div>
+        <div class="firstRowSecondColumn" data-offset-x="300" data-offset-y="260" data-expected-width="100" data-expected-height="20"></div>
+        <div class="secondRowFirstColumn" data-offset-x="200" data-offset-y="280" data-expected-width="100" data-expected-height="20"></div>
+        <div class="secondRowSecondColumn" data-offset-x="200" data-offset-y="260" data-expected-width="100" data-expected-height="20"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="100" data-offset-y="280" data-expected-width="100" data-expected-height="20"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="100" data-offset-y="260" data-expected-width="100" data-expected-height="20"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="100" data-expected-height="20"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="0" data-offset-y="260" data-expected-width="100" data-expected-height="20"></div>
+    </div>
+</div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution.html b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution.html
new file mode 100644
index 0000000..7b66ddb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/alignment/grid-align-content-distribution.html
@@ -0,0 +1,358 @@
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: aligned content distribution</title>
+<link rel="author" title="Rossana Monteriso" href="mailto:rmonteriso@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-align-3/#distribution-values">
+<meta name="assert" content="This test checks that the align-content property is applied correctly when using content-distribution values.">
+<meta name="flags" content="ahem">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<link rel="stylesheet" href="/css/support/grid.css">
+
+<style>
+
+.grid {
+    grid-auto-columns: 20px;
+    grid-auto-rows: 40px;
+    position: relative;
+    width: 200px;
+    height: 300px;
+}
+
+.stretchedGrid {
+    grid-auto-rows: auto;
+}
+
+.thirdRowFirstColumn {
+  background-color: green;
+  grid-column: 1;
+  grid-row: 3;
+}
+
+.fourthRowFirstColumn {
+  background-color: deepskyblue;
+  grid-column: 1;
+  grid-row: 4;
+}
+
+.fourthRowSecondColumn {
+  background-color: maroon;
+  grid-column: 2;
+  grid-row: 4;
+}
+
+.thirdRowFirstColumn {
+    background-color: green;
+    grid-column: 1;
+    grid-row: 3;
+}
+
+.fourthRowFirstColumn {
+    background-color: deepskyblue;
+    grid-column: 1;
+    grid-row: 4;
+}
+
+.fourthRowSecondColumn {
+    background-color: maroon;
+    grid-column: 2;
+  	grid-row: 4;
+}
+
+</style>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the align-content property is applied correctly when using content-distribution values.</p>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="55" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="55" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="205" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="205" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="73" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="73" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="187" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="187" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="150"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="0" data-expected-width="20" data-expected-height="150"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="150" data-expected-width="20" data-expected-height="150"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="150" data-expected-width="20" data-expected-height="150"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="30" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="30" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="230" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="230" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="45" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="45" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="215" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="215" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="100"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="0" data-expected-width="20" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="100" data-expected-width="20" data-expected-height="100"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="100" data-expected-width="20" data-expected-height="100"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="100"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="200" data-expected-width="20" data-expected-height="100"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="87" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="87" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="173" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="173" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="20" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="18" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="18" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="93" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="93" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="168" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="168" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="243" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="20" data-offset-y="243" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="28" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="28" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="96" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="96" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="164" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="164" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="232" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="20" data-offset-y="232" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="75"></div>
+        <div class="firstRowSecondColumn" data-offset-x="20" data-offset-y="0" data-expected-width="20" data-expected-height="75"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="75" data-expected-width="20" data-expected-height="75"></div>
+        <div class="secondRowSecondColumn" data-offset-x="20" data-offset-y="75" data-expected-width="20" data-expected-height="75"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="0" data-offset-y="150" data-expected-width="20" data-expected-height="75"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="20" data-offset-y="150" data-expected-width="20" data-expected-height="75"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="0" data-offset-y="225" data-expected-width="20" data-expected-height="75"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="20" data-offset-y="225" data-expected-width="20" data-expected-height="75"></div>
+    </div>
+</div>
+
+<!-- RTL direction. -->
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="55" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="55" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="205" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="205" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="73" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="73" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="187" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="187" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="150"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="0" data-expected-width="20" data-expected-height="150"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="150" data-expected-width="20" data-expected-height="150"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="150" data-expected-width="20" data-expected-height="150"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="30" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="30" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="230" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="230" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="45" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="45" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="130" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="215" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="215" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="100"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="0" data-expected-width="20" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="100" data-expected-width="20" data-expected-height="100"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="100" data-expected-width="20" data-expected-height="100"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="200" data-expected-width="20" data-expected-height="100"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="200" data-expected-width="20" data-expected-height="100"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-between'</p>
+    <div class="grid alignContentSpaceBetween directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="87" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="87" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="173" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="173" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="180" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="160" data-offset-y="260" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-around'</p>
+    <div class="grid alignContentSpaceAround directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="18" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="18" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="93" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="93" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="168" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="168" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="180" data-offset-y="243" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="160" data-offset-y="243" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'space-evenly'</p>
+    <div class="grid alignContentSpaceEvenly directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="28" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="28" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="96" data-expected-width="20" data-expected-height="40"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="96" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="164" data-expected-width="20" data-expected-height="40"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="164" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="180" data-offset-y="232" data-expected-width="20" data-expected-height="40"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="160" data-offset-y="232" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'stretch'</p>
+    <div class="grid stretchedGrid alignContentStretch directionRTL" data-expected-width="200" data-expected-height="300">
+        <div class="firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="75"></div>
+        <div class="firstRowSecondColumn" data-offset-x="160" data-offset-y="0" data-expected-width="20" data-expected-height="75"></div>
+        <div class="secondRowFirstColumn" data-offset-x="180" data-offset-y="75" data-expected-width="20" data-expected-height="75"></div>
+        <div class="secondRowSecondColumn" data-offset-x="160" data-offset-y="75" data-expected-width="20" data-expected-height="75"></div>
+        <div class="thirdRowFirstColumn" data-offset-x="180" data-offset-y="150" data-expected-width="20" data-expected-height="75"></div>
+        <div class="thirdRowSecondColumn" data-offset-x="160" data-offset-y="150" data-expected-width="20" data-expected-height="75"></div>
+        <div class="fourthRowFirstColumn" data-offset-x="180" data-offset-y="225" data-expected-width="20" data-expected-height="75"></div>
+        <div class="fourthRowSecondColumn" data-offset-x="160" data-offset-y="225" data-expected-width="20" data-expected-height="75"></div>
+    </div>
+</div>
+</body>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-content-expected.txt b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-content-expected.txt
new file mode 100644
index 0000000..69de21eb
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-content-expected.txt
@@ -0,0 +1,53 @@
+This test checks that the align-content property is applied correctly.
+
+direction: LTR | align-content: 'center'
+
+PASS
+
+direction: LTR | align-content: 'left'
+
+PASS
+
+direction: LTR | align-content: 'right'
+
+PASS
+
+direction: LTR | align-content: 'start'
+
+PASS
+
+direction: LTR | align-content: 'end'
+
+PASS
+
+direction: LTR | align-content: 'flex-start'
+
+PASS
+
+direction: LTR | align-content: 'flex-end
+
+PASS
+
+direction: LTR | align-content: 'auto' (resolved to 'start')
+
+PASS
+
+direction: RTL | align-content: 'center'
+
+PASS
+
+direction: RTL | align-content: 'left'
+
+PASS
+
+direction: RTL | align-content: 'right'
+
+PASS
+
+direction: RTL | align-content: 'start'
+
+PASS
+
+direction: RTL | align-content: 'end'
+
+PASS
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-content.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-content.html
new file mode 100644
index 0000000..f697eae9
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-content.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link href="resources/grid.css" rel="stylesheet">
+<link href="resources/grid-alignment.css" rel="stylesheet">
+<script src="../../resources/check-layout.js"></script>
+<style>
+body {
+    margin: 0;
+}
+
+.grid {
+    grid: 100px 100px / 50px 50px;
+    position: relative;
+    width: 200px;
+    height: 300px;
+}
+
+.verticalGrid {
+    width: 300px;
+    height: 200px;
+}
+
+.cell {
+    width: 20px;
+    height: 40px;
+}
+</style>
+</head>
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the align-content property is applied correctly.</p>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'center'</p>
+    <div class="grid alignContentCenter" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="0" data-offset-y="50" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="50" data-offset-y="50" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="150" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="50" data-offset-y="150" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'left'</p>
+    <div class="grid alignContentLeft" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="50" data-offset-y="0" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="50" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'right'</p>
+    <div class="grid alignContentRight" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="50" data-offset-y="0" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="50" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'start'</p>
+    <div class="grid alignContentStart" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="50" data-offset-y="0" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="50" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'end'</p>
+    <div class="grid alignContentEnd" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="0" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="50" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="50" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'flex-start'</p>
+    <div class="grid alignContentFlexStart" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="50" data-offset-y="0" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="50" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'flex-end</p>
+    <div class="grid alignContentFlexEnd" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="0" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="50" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="50" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- Default alignment and initial values. -->
+<div style="position: relative">
+    <p>direction: LTR | align-content: 'auto' (resolved to 'start')</p>
+    <div class="grid" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="50" data-offset-y="0" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="50" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- RTL direction. -->
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'center'</p>
+    <div class="grid directionRTL alignContentCenter" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="180" data-offset-y="50" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="100" data-offset-y="50" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="150" data-offset-y="150" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="130" data-offset-y="150" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'left'</p>
+    <div class="grid directionRTL alignContentLeft" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="150" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="130" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'right'</p>
+    <div class="grid directionRTL alignContentRight" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="150" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="130" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'start'</p>
+    <div class="grid directionRTL alignContentStart" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="150" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="130" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <p>direction: RTL | align-content: 'end'</p>
+    <div class="grid directionRTL alignContentEnd" data-expected-width="200" data-expected-height="300">
+        <div class="cell firstRowFirstColumn" data-offset-x="180" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="firstRowSecondColumn" data-offset-x="100" data-offset-y="100" data-expected-width="50" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="150" data-offset-y="200" data-expected-width="50" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="130" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-expected.txt b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-expected.txt
new file mode 100644
index 0000000..5ec7e800
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-expected.txt
@@ -0,0 +1,18 @@
+This test checks that the align-self property is applied correctly.
+
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-margin-border-padding-vertical-lr.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-margin-border-padding-vertical-lr.html
new file mode 100644
index 0000000..4b81040c22
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-margin-border-padding-vertical-lr.html
@@ -0,0 +1,236 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet">
+<link href="resources/grid.css" rel="stylesheet">
+<link href="resources/grid-alignment.css" rel="stylesheet">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/check-layout-th.js"></script>
+<style>
+body {
+    margin: 0;
+}
+
+.grid {
+    grid-template-columns: 100px 200px;
+    grid-template-rows: 200px 200px;
+    padding: 10px 15px 20px 30px;
+    border-width: 5px 10px 15px 20px;
+    border-style: dotted;
+    border-color: blue;
+    position: relative;
+}
+
+.cell {
+    width: 20px;
+    height: 40px;
+    margin: 4px 8px 12px 16px;
+}
+</style>
+</head>
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the 'margin', 'border' and 'padding' properties are applied together correctly for 'align' and 'justify' properties on vertical-LR grids.</p>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'auto' | justify-items: 'auto'</p>
+    <div class="grid fit-content verticalLR" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"   data-offset-x="46"  data-offset-y="14"  data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell firstRowSecondColumn"  data-offset-x="46"  data-offset-y="114" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowFirstColumn"  data-offset-x="246" data-offset-y="14"  data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="246" data-offset-y="114" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'center' | justify-items: 'center'</p>
+    <div class="grid fit-content verticalLR itemsCenter" data-expected-width="475" data-expected-height="350">
+        <div class="firstRowFirstColumn cell"     data-offset-x="124"  data-offset-y="36"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"   data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230"  data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="324"  data-offset-y="186" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'end' | justify-items: 'end'</p>
+    <div class="grid fit-content verticalLR itemsEnd" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="202" data-offset-y="58" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="402" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'left' | justify-items: 'left'</p>
+    <div class="grid fit-content verticalLR itemsLeft" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="246" data-offset-y="114" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'start' | justify-items: 'right'</p>
+    <div class="grid fit-content verticalLR alignItemsStart justifyItemsRight" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="58" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="246" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (ortho) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content directionLTR verticalLR itemsSelfStart" data-expected-width="475" data-expected-height="350">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="58" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="246" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (ortho) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content directionLTR verticalLR itemsSelfEnd" data-expected-width="475" data-expected-height="350">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="202" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="402" data-offset-y="114" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (parall) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content directionLTR verticalLR itemsSelfStart" data-expected-width="475" data-expected-height="350">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="14" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="246" data-offset-y="114" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (parall) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content directionLTR verticalLR itemsSelfEnd" data-expected-width="475" data-expected-height="350">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="202" data-offset-y="58"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="402" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- RTL direction. -->
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'auto' | justify-items: 'auto'</p>
+    <div class="grid fit-content verticalLR directionRTL" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"   data-offset-x="46"  data-offset-y="258" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell firstRowSecondColumn"  data-offset-x="46"  data-offset-y="158" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowFirstColumn"  data-offset-x="246" data-offset-y="258" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="246" data-offset-y="158" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'center' | justify-items: 'center'</p>
+    <div class="grid fit-content verticalLR directionRTL itemsCenter" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="124" data-offset-y="236" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="324" data-offset-y="86" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'end' | justify-items: 'end'</p>
+    <div class="grid fit-content verticalLR directionRTL itemsEnd"       data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="202" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="402" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'start' | justify-items: 'left'</p>
+    <div class="grid fit-content verticalLR directionRTL alignItemsStart justifyItemsLeft"      data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="246" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'start' | justify-items: 'right'</p>
+    <div class="grid fit-content verticalLR directionRTL alignItemsStart justifyItemsRight"     data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="246" data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (ortho) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content verticalLR directionRTL itemsSelfStart" data-expected-width="475" data-expected-height="350">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="246" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (ortho) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content verticalLR directionRTL itemsSelfEnd" data-expected-width="475" data-expected-height="350">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="202" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="402" data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (parall) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content verticalLR directionRTL itemsSelfStart" data-expected-width="475" data-expected-height="350">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="246" data-offset-y="158"  data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (parall) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content verticalLR directionRTL itemsSelfEnd" data-expected-width="475" data-expected-height="350">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="202" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="402" data-offset-y="14" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-margin-border-padding-vertical-rl.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-margin-border-padding-vertical-rl.html
new file mode 100644
index 0000000..d41754fd
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-margin-border-padding-vertical-rl.html
@@ -0,0 +1,237 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet">
+<link href="resources/grid.css" rel="stylesheet">
+<link href="resources/grid-alignment.css" rel="stylesheet">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/check-layout-th.js"></script>
+<style>
+body {
+    margin: 0;
+}
+
+.grid {
+    grid-template-columns: 100px 200px;
+    grid-template-rows: 200px 200px;
+    padding: 10px 15px 20px 30px;
+    border-width: 5px 10px 15px 20px;
+    border-style: dotted;
+    border-color: blue;
+    position: relative;
+}
+
+.cell {
+    width: 20px;
+    height: 40px;
+    margin: 4px 8px 12px 16px;
+}
+</style>
+</head>
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the 'margin', 'border' and 'padding' properties are applied together correctly for 'align' and 'justify' properties on vertical-RL grids.</p>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'auto' | justify-items: 'auto'</p>
+    <div class="grid fit-content verticalRL" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"   data-offset-x="402" data-offset-y="14"  data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell firstRowSecondColumn"  data-offset-x="402" data-offset-y="114" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowFirstColumn"  data-offset-x="202" data-offset-y="14"  data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="202" data-offset-y="114" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'center' | justify-items: 'center'</p>
+    <div class="grid fit-content verticalRL itemsCenter" data-expected-width="475" data-expected-height="350">
+        <div class="firstRowFirstColumn cell"     data-offset-x="324" data-offset-y="36"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="124" data-offset-y="186" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'end' | justify-items: 'end'</p>
+    <div class="grid fit-content verticalRL itemsEnd" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="58" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="46"  data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'left' | justify-items: 'left'</p>
+    <div class="grid fit-content verticalRL itemsLeft" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="402" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="114" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'start' | justify-items: 'right'</p>
+    <div class="grid fit-content verticalRL alignItemsStart justifyItemsRight" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="402" data-offset-y="58" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (ortho) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content directionLTR verticalRL itemsSelfStart" data-expected-width="475" data-expected-height="350">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="402" data-offset-y="58" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (ortho) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content directionLTR verticalRL itemsSelfEnd" data-expected-width="475" data-expected-height="350">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="46"  data-offset-y="114" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (parall) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content directionLTR verticalRL itemsSelfStart" data-expected-width="475" data-expected-height="350">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="402" data-offset-y="14" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="114" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (parall) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content directionLTR verticalRL itemsSelfEnd" data-expected-width="475" data-expected-height="350">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="58"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="110" data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="46"  data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+
+<!-- RTL direction. -->
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'auto' | justify-items: 'auto'</p>
+    <div class="grid fit-content verticalRL directionRTL" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"   data-offset-x="402" data-offset-y="258" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell firstRowSecondColumn"  data-offset-x="402" data-offset-y="158" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowFirstColumn"  data-offset-x="202" data-offset-y="258" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="202" data-offset-y="158" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'center' | justify-items: 'center'</p>
+    <div class="grid fit-content verticalRL directionRTL itemsCenter" data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="324" data-offset-y="236" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="124" data-offset-y="86" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'end' | justify-items: 'end'</p>
+    <div class="grid fit-content verticalRL directionRTL itemsEnd"       data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="46"  data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'start' | justify-items: 'left'</p>
+    <div class="grid fit-content verticalRL directionRTL alignItemsStart justifyItemsLeft"      data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="402" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'start' | justify-items: 'right'</p>
+    <div class="grid fit-content verticalRL directionRTL alignItemsStart justifyItemsRight"     data-expected-width="475" data-expected-height="350">
+        <div class="cell firstRowFirstColumn"     data-offset-x="402" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (ortho) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content verticalRL directionRTL itemsSelfStart" data-expected-width="475" data-expected-height="350">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="402" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (ortho) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content verticalRL directionRTL itemsSelfEnd" data-expected-width="475" data-expected-height="350">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="46"  data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (parall) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content verticalRL directionRTL itemsSelfStart" data-expected-width="475" data-expected-height="350">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="402" data-offset-y="258" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="158"  data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (parall) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content verticalRL directionRTL itemsSelfEnd" data-expected-width="475" data-expected-height="350">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="230" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="200" data-expected-height="100"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="46"  data-offset-y="14" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-margin-border-padding.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-margin-border-padding.html
new file mode 100644
index 0000000..f43f0e80
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-margin-border-padding.html
@@ -0,0 +1,236 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet">
+<link href="resources/grid.css" rel="stylesheet">
+<link href="resources/grid-alignment.css" rel="stylesheet">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/check-layout-th.js"></script>
+<style>
+body {
+    margin: 0;
+}
+
+.grid {
+    grid-template-columns: 100px 200px;
+    grid-template-rows: 200px 200px;
+    padding: 10px 15px 20px 30px;
+    border-width: 5px 10px 15px 20px;
+    border-style: dotted;
+    border-color: blue;
+    position: relative;
+}
+
+.cell {
+    width: 20px;
+    height: 40px;
+    margin: 4px 8px 12px 16px;
+}
+</style>
+</head>
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the 'margin', 'border' and 'padding' properties are applied together correctly for 'align' and 'justify' properties.</p>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'auto' | justify-items: 'auto'</p>
+    <div class="grid fit-content" data-expected-width="375" data-expected-height="450">
+        <div class="cell firstRowFirstColumn"   data-offset-x="46"  data-offset-y="14"  data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell firstRowSecondColumn"  data-offset-x="146" data-offset-y="14"  data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowFirstColumn"  data-offset-x="46"  data-offset-y="214" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="146" data-offset-y="214" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'center' | justify-items: 'center'</p>
+    <div class="grid fit-content itemsCenter" data-expected-width="375" data-expected-height="450">
+        <div class="firstRowFirstColumn cell"     data-offset-x="74"  data-offset-y="86" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="130" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="224" data-offset-y="286" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'end' | justify-items: 'end'</p>
+    <div class="grid fit-content itemsEnd" data-expected-width="375" data-expected-height="450">
+        <div class="cell firstRowFirstColumn"     data-offset-x="102" data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="130" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="302" data-offset-y="358" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'start' | justify-items: 'left'</p>
+    <div class="grid fit-content alignItemsStart justifyItemsLeft" data-expected-width="375" data-expected-height="450">
+        <div class="cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="130" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="146" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR | align-items: 'start' | justify-items: 'right'</p>
+    <div class="grid fit-content alignItemsStart justifyItemsRight" data-expected-width="375" data-expected-height="450">
+        <div class="cell firstRowFirstColumn"     data-offset-x="102" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="130" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="302" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (ortho) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content directionLTR itemsSelfStart" data-expected-width="375" data-expected-height="450">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="102" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="130" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="302" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (ortho) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content directionLTR itemsSelfEnd" data-expected-width="375" data-expected-height="450">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="46"  data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="130" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="146" data-offset-y="358" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR (parall) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content directionLTR itemsSelfStart" data-expected-width="375" data-expected-height="450">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="46" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="130" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="146" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: LTR - (parall) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content directionLTR itemsSelfEnd" data-expected-width="375" data-expected-height="450">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="102"  data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="130" data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="30"  data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="302" data-offset-y="358" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- RTL direction. -->
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'auto' | justify-items: 'auto'</p>
+    <div class="grid fit-content directionRTL" data-expected-width="375" data-expected-height="450">
+        <div class="cell firstRowFirstColumn"   data-offset-x="302" data-offset-y="14"  data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell firstRowSecondColumn"  data-offset-x="202" data-offset-y="14"  data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowFirstColumn"  data-offset-x="302" data-offset-y="214" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell secondRowSecondColumn" data-offset-x="202" data-offset-y="214" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'center' | justify-items: 'center'</p>
+    <div class="grid fit-content directionRTL itemsCenter" data-expected-width="375" data-expected-height="450">
+        <div class="cell firstRowFirstColumn"     data-offset-x="274" data-offset-y="86" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="124" data-offset-y="286" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'end' | justify-items: 'end'</p>
+    <div class="grid fit-content directionRTL itemsEnd"       data-expected-width="375" data-expected-height="450">
+        <div class="cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="46"  data-offset-y="358" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'start' | justify-items: 'left'</p>
+    <div class="grid fit-content directionRTL alignItemsStart justifyItemsLeft"      data-expected-width="375" data-expected-height="450">
+        <div class="cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="46"  data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL | align-items: 'start' | justify-items: 'right'</p>
+    <div class="grid fit-content directionRTL alignItemsStart justifytemsRight"     data-expected-width="375" data-expected-height="450">
+        <div class="cell firstRowFirstColumn"     data-offset-x="302" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (ortho) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content directionRTL itemsSelfStart" data-expected-width="375" data-expected-height="450">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="46"  data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (ortho) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content directionRTL itemsSelfEnd" data-expected-width="375" data-expected-height="450">
+        <div class="directionLTR cell firstRowFirstColumn"     data-offset-x="302" data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionLTR selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionLTR selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="directionLTR cell secondRowSecondColumn"   data-offset-x="202" data-offset-y="358" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (parall) | align-items: 'self-start' | justify-items: 'self-start'</p>
+    <div class="grid fit-content directionRTL itemsSelfStart" data-expected-width="375" data-expected-height="450">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="302" data-offset-y="14"  data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="202"  data-offset-y="214" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+<div>
+    <p>border: 5px 10px 15px 20px | padding: 10px 15px 20px 30px | margin: 4px 8px 12px 16px<br>
+        direction: RTL (parall) | align-items: 'self-end' | justify-items: 'self-end'</p>
+    <div class="grid fit-content directionRTL itemsSelfEnd" data-expected-width="375" data-expected-height="450">
+        <div class="directionRTL cell firstRowFirstColumn"     data-offset-x="246" data-offset-y="158" data-expected-width="20"  data-expected-height="40"></div>
+        <div class="directionRTL selfStretch firstRowSecondColumn" data-offset-x="30"  data-offset-y="10"  data-expected-width="200" data-expected-height="200"></div>
+        <div class="directionRTL selfStretch secondRowFirstColumn" data-offset-x="230" data-offset-y="210" data-expected-width="100" data-expected-height="200"></div>
+        <div class="directionRTL cell secondRowSecondColumn"   data-offset-x="46" data-offset-y="358" data-expected-width="20"  data-expected-height="40"></div>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-overflow-expected.txt b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-overflow-expected.txt
new file mode 100644
index 0000000..ee761ee
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-overflow-expected.txt
@@ -0,0 +1,10 @@
+This test checks that the 'overflow' keyword is applied correctly for 'align' and 'justify' properties.
+
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-overflow.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-overflow.html
new file mode 100644
index 0000000..53a4c9f2
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-overflow.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet">
+<link href="resources/grid.css" rel="stylesheet">
+<link href="resources/grid-alignment.css" rel="stylesheet">
+<script src="../../resources/check-layout.js"></script>
+<style>
+body {
+    margin: 0;
+}
+
+.grid {
+    grid-template-columns: 150px 150px;
+    grid-template-rows: 120px 120px 120px;
+    margin-bottom: 20px;
+}
+
+.cellOverflowWidth {
+    width: 180px;
+    height: 40px;
+}
+
+.cellOverflowHeight {
+    width: 50px;
+    height: 150px;
+}
+
+.cellWithNoOverflow {
+    width: 50px;
+    height: 40px;
+}
+
+.thirdRowFirstColumn {
+    background-color: green;
+    grid-column: 1;
+    grid-row: 3;
+}
+</style>
+</head>
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the 'overflow' keyword is applied correctly for 'align' and 'justify' properties.</p>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsCenter justifyItemsCenter" data-expected-width="300" data-expected-height="360">
+        <div class="cellOverflowWidth  firstRowFirstColumn" data-offset-x="-15" data-offset-y="40" data-expected-width="180" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow secondRowFirstColumn" data-offset-x="50" data-offset-y="160" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowFirstColumn" data-offset-x="50" data-offset-y="280" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow firstRowSecondColumn" data-offset-x="200" data-offset-y="40" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellOverflowWidth  secondRowSecondColumn" data-offset-x="135" data-offset-y="160" data-expected-width="180" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowSecondColumn" data-offset-x="200" data-offset-y="280" data-expected-width="50" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsCenterUnsafe justifyItemsCenterUnsafe" data-expected-width="300" data-expected-height="360">
+        <div class="cellOverflowHeight firstRowFirstColumn" data-offset-x="50" data-offset-y="-15" data-expected-width="50" data-expected-height="150"></div>
+        <div class="cellWithNoOverflow secondRowFirstColumn" data-offset-x="50" data-offset-y="160" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowFirstColumn" data-offset-x="50" data-offset-y="280" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow firstRowSecondColumn" data-offset-x="200" data-offset-y="40" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellOverflowHeight secondRowSecondColumn" data-offset-x="200" data-offset-y="105" data-expected-width="50" data-expected-height="150"></div>
+        <div class="cellWithNoOverflow thirdRowSecondColumn" data-offset-x="200" data-offset-y="280" data-expected-width="50" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsCenterSafe justifyItemsCenterSafe" data-expected-width="300" data-expected-height="360">
+        <div class="cellOverflowWidth  firstRowFirstColumn" data-offset-x="0" data-offset-y="40" data-expected-width="180" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow secondRowFirstColumn" data-offset-x="50" data-offset-y="160" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowFirstColumn" data-offset-x="50" data-offset-y="280" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow firstRowSecondColumn" data-offset-x="200" data-offset-y="40" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellOverflowWidth  secondRowSecondColumn" data-offset-x="150" data-offset-y="160" data-expected-width="180" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowSecondColumn" data-offset-x="200" data-offset-y="280" data-expected-width="50" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsCenterSafe justifyItemsCenterSafe" data-expected-width="300" data-expected-height="360">
+        <div class="cellOverflowHeight firstRowFirstColumn" data-offset-x="50" data-offset-y="0" data-expected-width="50" data-expected-height="150"></div>
+        <div class="cellWithNoOverflow secondRowFirstColumn" data-offset-x="50" data-offset-y="160" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowFirstColumn" data-offset-x="50" data-offset-y="280" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow firstRowSecondColumn" data-offset-x="200" data-offset-y="40" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellOverflowHeight secondRowSecondColumn alignSelfCenterSafe justifySelfCenterSafe" data-offset-x="200" data-offset-y="120" data-expected-width="50" data-expected-height="150"></div>
+        <div class="cellWithNoOverflow thirdRowSecondColumn" data-offset-x="200" data-offset-y="280" data-expected-width="50" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsEnd justifyItemsEnd" data-expected-width="300" data-expected-height="360">
+        <div class="cellOverflowWidth  firstRowFirstColumn" data-offset-x="-30" data-offset-y="80" data-expected-width="180" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow secondRowFirstColumn justifySelfCenter" data-offset-x="50" data-offset-y="200" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowFirstColumn" data-offset-x="100" data-offset-y="320" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow firstRowSecondColumn" data-offset-x="250" data-offset-y="80" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellOverflowWidth  secondRowSecondColumn" data-offset-x="120" data-offset-y="200" data-expected-width="180" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowSecondColumn" data-offset-x="250" data-offset-y="320" data-expected-width="50" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsEndUnsafe justifyItemsEndUnsafe" data-expected-width="300" data-expected-height="360">
+        <div class="cellOverflowHeight firstRowFirstColumn" data-offset-x="100" data-offset-y="-30" data-expected-width="50" data-expected-height="150"></div>
+        <div class="cellWithNoOverflow secondRowFirstColumn" data-offset-x="100" data-offset-y="200" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowFirstColumn" data-offset-x="100" data-offset-y="320" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow firstRowSecondColumn alignSelfCenter" data-offset-x="250" data-offset-y="40" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellOverflowHeight secondRowSecondColumn" data-offset-x="250" data-offset-y="90" data-expected-width="50" data-expected-height="150"></div>
+        <div class="cellWithNoOverflow thirdRowSecondColumn" data-offset-x="250" data-offset-y="320" data-expected-width="50" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsEndSafe justifyItemsEndSafe" data-expected-width="300" data-expected-height="360">
+        <div class="cellOverflowWidth  firstRowFirstColumn" data-offset-x="0" data-offset-y="80" data-expected-width="180" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow secondRowFirstColumn justifySelfCenterUnsafe" data-offset-x="50" data-offset-y="200" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowFirstColumn" data-offset-x="100" data-offset-y="320" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow firstRowSecondColumn" data-offset-x="250" data-offset-y="80" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellOverflowWidth  secondRowSecondColumn" data-offset-x="150" data-offset-y="200" data-expected-width="180" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowSecondColumn" data-offset-x="250" data-offset-y="320" data-expected-width="50" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsEndSafe justifyItemsEndSafe" data-expected-width="300" data-expected-height="360">
+        <div class="cellOverflowHeight firstRowFirstColumn" data-offset-x="100" data-offset-y="0" data-expected-width="50" data-expected-height="150"></div>
+        <div class="cellWithNoOverflow secondRowFirstColumn" data-offset-x="100" data-offset-y="200" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow thirdRowFirstColumn" data-offset-x="100" data-offset-y="320" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellWithNoOverflow firstRowSecondColumn alignSelfCenterUnsafe" data-offset-x="250" data-offset-y="40" data-expected-width="50" data-expected-height="40"></div>
+        <div class="cellOverflowHeight secondRowSecondColumn" data-offset-x="250" data-offset-y="120" data-expected-width="50" data-expected-height="150"></div>
+        <div class="cellWithNoOverflow thirdRowSecondColumn" data-offset-x="250" data-offset-y="320" data-expected-width="50" data-expected-height="40"></div>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-stretch-expected.txt b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-stretch-expected.txt
new file mode 100644
index 0000000..9911dff2
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-stretch-expected.txt
@@ -0,0 +1,18 @@
+This test checks that the 'stretch' value is applied correctly for 'align' and 'justify' properties.
+
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
+PASS
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-stretch-with-orthogonal-flows.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-stretch-with-orthogonal-flows.html
new file mode 100644
index 0000000..75d58ea
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-stretch-with-orthogonal-flows.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<link href="resources/grid.css" rel="stylesheet">
+<link href="resources/grid-alignment.css" rel="stylesheet">
+<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/check-layout-th.js"></script>
+<style>
+body {
+    margin: 0;
+}
+.container {
+    position: relative;
+}
+.grid {
+    grid-template-columns: 100px 100px;
+    grid-template-rows: 150px 150px;
+    font: 10px/1 Ahem;
+}
+.widthAndHeightSet {
+    width: 20px;
+    height: 40px;
+}
+.onlyWidthSet { width: 20px; }
+.onlyHeightSet { height: 40px; }
+.maxHeight { max-height: 160px; }
+.maxWidth { max-width: 90px; }
+.minWidth { min-width: 120px; }
+.minHeight { min-height: 220px; }
+.topAutoMargin { margin-top: auto; }
+.bottomAutoMargin { margin-bottom: auto; }
+.leftAutoMargin { margin-left: auto; }
+.rightAutoMargin { margin-right: auto; }
+</style>
+<body onload="checkLayout('.grid')">
+<div id="log"></div>
+<p>This test checks that stretching alignment works as expected with orthogonal flows.</p>
+
+<p>HORIZONTAL vs VERTICAL-RL</p>
+<p><b>row1/col1:</b> fixed width and height - <b>row1/col2:</b> fixed width and auto height - <b>row2/col1:</b> auto width and fixed height - <b>row2/col2:</b> auto width and height, but start.</p>
+<div class="container">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="300">
+        <div class="verticalRL firstRowFirstColumn   selfStretch   widthAndHeightSet " data-expected-width="20"  data-expected-height="40">XXX</div>
+        <div class="verticalRL firstRowSecondColumn  sefStretch    onlyWidthSet      " data-expected-width="20"  data-expected-height="150">XXX</div>
+        <div class="verticalRL secondRowFirstColumn  selfStretch    onlyHeightSet     " data-expected-width="100" data-expected-height="40">XXX</div>
+        <div class="verticalRL secondRowSecondColumn selfSelfStart                   " data-expected-width="10"  data-expected-height="30">XXX</div>
+    </div>
+</div>
+<p><b>row1/co1l:</b> bottom auto margin - <b>row1/col2:</b> left auto margin - <b>row2/col1:</b> top auto margin - <b>row2/col2:</b> right auto margin.</p>
+<div class="container">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="300">
+        <div class="verticalRL firstRowFirstColumn   selfStretch bottomAutoMargin " data-expected-width="100" data-expected-height="30">XXX</div>
+        <div class="verticalRL firstRowSecondColumn  seffStretch leftAutoMargin   " data-expected-width="10"  data-expected-height="150">XXX</div>
+        <div class="verticalRL secondRowFirstColumn  selffStretch topAutoMargin    " data-expected-width="100" data-expected-height="30">XXX</div>
+        <div class="verticalRL secondRowSecondColumn selffStretch rightAutoMargin  " data-expected-width="10"  data-expected-height="150">XXX</div>
+    </div>
+</div>
+
+<p>HORIZONTAL vs VERTICAL-LR</p>
+<p><b>row1/col1:</b> fixed width and height - <b>row1/col2:</b> fixed width and auto height - <b>row2/col1:</b> auto width and fixed height - <b>row2/col2:</b> auto width and height, but start.</p>
+<div class="container">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="300">
+        <div class="verticalLR firstRowFirstColumn   selfStretch   widthAndHeightSet " data-expected-width="20"  data-expected-height="40">XXX</div>
+        <div class="verticalLR firstRowSecondColumn  sefStretch    onlyWidthSet      " data-expected-width="20"  data-expected-height="150">XXX</div>
+        <div class="verticalLR secondRowFirstColumn  selfStretch    onlyHeightSet     " data-expected-width="100" data-expected-height="40">XXX</div>
+        <div class="verticalLR secondRowSecondColumn selfSelfStart                   " data-expected-width="10"  data-expected-height="30">XXX</div>
+    </div>
+</div>
+<p><b>row1/co1l:</b> bottom auto margin - <b>row1/col2:</b> left auto margin - <b>row2/col1:</b> top auto margin - <b>row2/col2:</b> right auto margin.</p>
+<div class="container">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="300">
+        <div class="verticalLR firstRowFirstColumn   selfStretch bottomAutoMargin " data-expected-width="100" data-expected-height="30">XXX</div>
+        <div class="verticalLR firstRowSecondColumn  seffStretch leftAutoMargin   " data-expected-width="10"  data-expected-height="150">XXX</div>
+        <div class="verticalLR secondRowFirstColumn  selffStretch topAutoMargin    " data-expected-width="100" data-expected-height="30">XXX</div>
+        <div class="verticalLR secondRowSecondColumn selffStretch rightAutoMargin  " data-expected-width="10"  data-expected-height="150">XXX</div>
+    </div>
+</div>
+
+<p>VERTICAL-RL vs HORIZONTAL</p>
+<p><b>row1/col1:</b> fixed width and height - <b>row1/col2:</b> fixed width and auto height - <b>row2/col1:</b> auto width and fixed height - <b>row2/col2:</b> auto width and height, but start.</p>
+<div class="container">
+    <div class="grid fit-content verticalRL" data-expected-width="300" data-expected-height="200">
+        <div class="horizonalTB firstRowFirstColumn   selfStretch   widthAndHeightSet " data-expected-width="20"  data-expected-height="40">XXX</div>
+        <div class="horizonalTB firstRowSecondColumn  sefStretch    onlyWidthSet      " data-expected-width="20"  data-expected-height="100">XXX</div>
+        <div class="horizonalTB secondRowFirstColumn  selfStretch    onlyHeightSet     " data-expected-width="150" data-expected-height="40">XXX</div>
+        <div class="horizonalTB secondRowSecondColumn selfSelfStart                   " data-expected-width="10"  data-expected-height="30">XXX</div>
+    </div>
+</div>
+<p><b>row1/co1l:</b> bottom auto margin - <b>row1/col2:</b> left auto margin - <b>row2/col1:</b> top auto margin - <b>row2/col2:</b> right auto margin.</p>
+<div class="container">
+    <div class="grid fit-content verticalRL" data-expected-width="300" data-expected-height="200">
+        <div class="horizonalTB firstRowFirstColumn   selfStretch bottomAutoMargin " data-expected-width="150" data-expected-height="30">XXX</div>
+        <div class="horizonalTB firstRowSecondColumn  seffStretch leftAutoMargin   " data-expected-width="10"  data-expected-height="100">XXX</div>
+        <div class="horizonalTB secondRowFirstColumn  selffStretch topAutoMargin    " data-expected-width="150" data-expected-height="30">XXX</div>
+        <div class="horizonalTB secondRowSecondColumn selffStretch rightAutoMargin  " data-expected-width="10"  data-expected-height="100">XXX</div>
+    </div>
+</div>
+
+<p>VERTICAL-LR vs HORIZONTAL</p>
+<p><b>row1/col1:</b> fixed width and height - <b>row1/col2:</b> fixed width and auto height - <b>row2/col1:</b> auto width and fixed height - <b>row2/col2:</b> auto width and height, but start.</p>
+<div class="container">
+    <div class="grid fit-content verticalLR" data-expected-width="300" data-expected-height="200">
+        <div class="horizonalTB firstRowFirstColumn   selfStretch   widthAndHeightSet " data-expected-width="20"  data-expected-height="40">XXX</div>
+        <div class="horizonalTB firstRowSecondColumn  sefStretch    onlyWidthSet      " data-expected-width="20"  data-expected-height="100">XXX</div>
+        <div class="horizonalTB secondRowFirstColumn  selfStretch    onlyHeightSet     " data-expected-width="150" data-expected-height="40">XXX</div>
+        <div class="horizonalTB secondRowSecondColumn selfSelfStart                   " data-expected-width="10"  data-expected-height="30">XXX</div>
+    </div>
+</div>
+<p><b>row1/co1l:</b> bottom auto margin - <b>row1/col2:</b> left auto margin - <b>row2/col1:</b> top auto margin - <b>row2/col2:</b> right auto margin.</p>
+<div class="container">
+    <div class="grid fit-content verticalLR" data-expected-width="300" data-expected-height="200">
+        <div class="horizonalTB firstRowFirstColumn   selfStretch bottomAutoMargin " data-expected-width="150" data-expected-height="30">XXX</div>
+        <div class="horizonalTB firstRowSecondColumn  seffStretch leftAutoMargin   " data-expected-width="10"  data-expected-height="100">XXX</div>
+        <div class="horizonalTB secondRowFirstColumn  selffStretch topAutoMargin    " data-expected-width="150" data-expected-height="30">XXX</div>
+        <div class="horizonalTB secondRowSecondColumn selffStretch rightAutoMargin  " data-expected-width="10"  data-expected-height="100">XXX</div>
+    </div>
+</div>
+
+</body>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-stretch.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-stretch.html
new file mode 100644
index 0000000..3a1d6613
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align-justify-stretch.html
@@ -0,0 +1,184 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet">
+<link href="resources/grid.css" rel="stylesheet">
+<link href="resources/grid-alignment.css" rel="stylesheet">
+<script src="../../resources/check-layout.js"></script>
+<style>
+body {
+    margin: 0;
+}
+
+.grid {
+    grid-template-columns: 100px 100px;
+    grid-template-rows: 200px 200px;
+    margin-bottom: 20px;
+}
+
+.widthAndHeightSet {
+    width: 20px;
+    height: 40px;
+}
+.onlyWidthSet { width: 20px; }
+.onlyHeightSet { height: 40px; }
+.maxHeight { max-height: 160px; }
+.maxWidth { max-width: 90px; }
+.minWidth { min-width: 120px; }
+.minHeight { min-height: 220px; }
+</style>
+</head>
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the 'stretch' value is applied correctly for 'align' and 'justify' properties.</p>
+
+<div style="position: relative">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+        <div class="alignSelfStretch justifySelfStart firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="0" data-expected-height="200"></div>
+        <div class="widthAndHeightSet alignSelfStretch justifySelfStart firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet alignSelfStretch justifySelfStart secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="200"></div>
+        <div class="onlyHeightSet alignSelfStretch justifySelfStart secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="0" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+        <div class="alignSelfStart justifySelfStretch firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="0"></div>
+        <div class="widthAndHeightSet alignSelfStart justifySelfStretch firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet alignSelfStart justifySelfStretch secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="0"></div>
+        <div class="onlyHeightSet alignSelfStart justifySelfStretch secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="100" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+        <div class="maxHeight alignSelfStretch justifySelfStart firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="0" data-expected-height="160"></div>
+        <div class="minWidth widthAndHeightSet alignSelfStretch justifySelfStart firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="120" data-expected-height="40"></div>
+        <div class="minHeight onlyWidthSet alignSelfStretch justifySelfStart secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="220"></div>
+        <div class="maxWidth onlyHeightSet alignSelfStretch justifySelfStretch secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="90" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+        <div class="alignSelfStretch justifySelfStretch firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="widthAndHeightSet alignSelfStretch justifySelfStretch firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet alignSelfStretch justifySelfStretch secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="200"></div>
+        <div class="onlyHeightSet alignSelfStretch justifySelfStretch secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="100" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsStretch justifyItemsStart" data-expected-width="200" data-expected-height="400">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="0" data-expected-height="200"></div>
+        <div class="widthAndHeightSet firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="200"></div>
+        <div class="onlyHeightSet secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="0" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsStart justifyItemsStretch" data-expected-width="200" data-expected-height="400">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="0"></div>
+        <div class="widthAndHeightSet firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="0"></div>
+        <div class="onlyHeightSet secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="100" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsStretch justifyItemsStretch" data-expected-width="200" data-expected-height="400">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="widthAndHeightSet firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="200"></div>
+        <div class="onlyHeightSet secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="100" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content alignItemsAuto justifyItemsAuto" data-expected-width="200" data-expected-height="400">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="widthAndHeightSet firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="200"></div>
+        <div class="onlyHeightSet secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="100" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="widthAndHeightSet firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="200"></div>
+        <div class="onlyHeightSet secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="100" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="100" data-expected-height="200"></div>
+        <div class="secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="100" data-expected-height="200"></div>
+    </div>
+</div>
+
+<!-- RTL direction (it should not affect the block-flow direction). -->
+<div style="position: relative">
+    <div class="grid fit-content directionRTL alignItemsStretch justifyItemsStretch" data-expected-width="200" data-expected-height="400">
+        <div class="firstRowFirstColumn" data-offset-x="100" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="widthAndHeightSet firstRowSecondColumn" data-offset-x="80" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet secondRowFirstColumn" data-offset-x="180" data-offset-y="200" data-expected-width="20" data-expected-height="200"></div>
+        <div class="onlyHeightSet secondRowSecondColumn" data-offset-x="0" data-offset-y="200" data-expected-width="100" data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- RTL direction (it should not affect the block-flow) with opposite directions grid container vs grid item. -->
+<div style="position: relative">
+    <div class="grid fit-content alignItemsStretch justifyItemsStretch" data-expected-width="200" data-expected-height="400">
+        <div class="firstRowFirstColumn  directionRTL" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="widthAndHeightSet firstRowSecondColumn  directionRTL" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet secondRowFirstColumn  directionRTL" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="200"></div>
+        <div class="onlyHeightSet secondRowSecondColumn  directionRTL" data-offset-x="100" data-offset-y="200" data-expected-width="100" data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- Vertical RL writing mode. -->
+<div style="position: relative">
+    <div class="grid fit-content verticalRL alignItemsStretch justifyItemsStretch" data-expected-width="400" data-expected-height="200">
+        <div class="firstRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="200" data-expected-height="100"></div>
+        <div class="widthAndHeightSet firstRowSecondColumn" data-offset-x="380" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="100"></div>
+        <div class="onlyHeightSet secondRowSecondColumn" data-offset-x="0" data-offset-y="100" data-expected-width="200" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content verticalRL" data-expected-width="400" data-expected-height="200">
+        <div class="firstRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="200" data-expected-height="100"></div>
+        <div class="firstRowSecondColumn" data-offset-x="200" data-offset-y="100" data-expected-width="200" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="200" data-expected-height="100"></div>
+        <div class="secondRowSecondColumn" data-offset-x="0" data-offset-y="100" data-expected-width="200" data-expected-height="100"></div>
+    </div>
+</div>
+
+<!-- Vertical LR writing mode. -->
+<div style="position: relative">
+    <div class="grid fit-content verticalLR alignItemsStretch justifyItemsStretch" data-expected-width="400" data-expected-height="200">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="200" data-expected-height="100"></div>
+        <div class="widthAndHeightSet firstRowSecondColumn" data-offset-x="0" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="onlyWidthSet secondRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="20" data-expected-height="100"></div>
+        <div class="onlyHeightSet secondRowSecondColumn" data-offset-x="200" data-offset-y="100" data-expected-width="200" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content verticalLR" data-expected-width="400" data-expected-height="200">
+        <div class="firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="200" data-expected-height="100"></div>
+        <div class="firstRowSecondColumn" data-offset-x="0" data-offset-y="100" data-expected-width="200" data-expected-height="100"></div>
+        <div class="secondRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="200" data-expected-height="100"></div>
+        <div class="secondRowSecondColumn" data-offset-x="200" data-offset-y="100" data-expected-width="200" data-expected-height="100"></div>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/css-grid-layout/grid-align.html b/third_party/blink/web_tests/fast/css-grid-layout/grid-align.html
new file mode 100644
index 0000000..b628063
--- /dev/null
+++ b/third_party/blink/web_tests/fast/css-grid-layout/grid-align.html
@@ -0,0 +1,240 @@
+<!DOCTYPE html>
+<html>
+<head>
+<link href="../css-intrinsic-dimensions/resources/width-keyword-classes.css" rel="stylesheet">
+<link href="resources/grid.css" rel="stylesheet">
+<link href="resources/grid-alignment.css" rel="stylesheet">
+<script src="../../resources/check-layout.js"></script>
+<style>
+body {
+    margin: 0;
+}
+
+.grid {
+    grid-template-columns: 100px 100px;
+    grid-template-rows: 200px 200px;
+    margin-bottom: 20px;
+}
+
+.cell {
+    width: 20px;
+    height: 40px;
+}
+
+.item {
+    width: 8px;
+    height: 16px;
+    background: black;
+}
+
+</style>
+</head>
+<body onload="checkLayout('.grid')">
+
+<p>This test checks that the align-self property is applied correctly.</p>
+
+<div style="position: relative">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+        <div class="alignSelfStretch firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell alignSelfStart firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfEnd firstRowSecondColumn" data-offset-x="100" data-offset-y="160" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfCenter secondRowFirstColumn" data-offset-x="0" data-offset-y="280" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfRight secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfLeft secondRowSecondColumn" data-offset-x="100" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+        <div class="cell alignSelfFlexEnd firstRowFirstColumn" data-offset-x="0" data-offset-y="160" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfFlexStart firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfSelfStart secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfSelfEnd secondRowSecondColumn" data-offset-x="100" data-offset-y="360" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- Default alignment and initial values. -->
+<div style="position: relative">
+    <div class="grid fit-content alignItemsCenter" data-expected-width="200" data-expected-height="400">
+        <div class="cell alignSelfAuto firstRowFirstColumn" data-offset-x="0" data-offset-y="80" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell firstRowSecondColumn" data-offset-x="100" data-offset-y="80" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfSelfStart secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfSelfEnd secondRowSecondColumn" data-offset-x="100" data-offset-y="360" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+        <div class="alignSelfAuto firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+        <div class="cell firstRowSecondColumn" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfStart secondRowFirstColumn" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfEnd secondRowSecondColumn" data-offset-x="100" data-offset-y="360" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- RTL direction (it should not affect the block-flow direction). -->
+<div style="position: relative">
+     <div class="grid fit-content directionRTL" data-expected-width="200" data-expected-height="400">
+         <div class="alignSelfStretch firstRowFirstColumn" data-offset-x="100" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+         <div class="cell alignSelfStart firstRowSecondColumn" data-offset-x="80" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfEnd firstRowSecondColumn" data-offset-x="80" data-offset-y="160" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfCenter secondRowFirstColumn" data-offset-x="180" data-offset-y="280" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfRight secondRowSecondColumn" data-offset-x="80" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfLeft secondRowSecondColumn" data-offset-x="80" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+     </div>
+</div>
+
+<div style="position: relative">
+     <div class="grid fit-content directionRTL" data-expected-width="200" data-expected-height="400">
+         <div class="cell alignSelfFlexEnd firstRowFirstColumn" data-offset-x="180" data-offset-y="160" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfFlexStart firstRowSecondColumn" data-offset-x="80" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfSelfStart secondRowFirstColumn" data-offset-x="180" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfSelfEnd secondRowSecondColumn" data-offset-x="80" data-offset-y="360" data-expected-width="20" data-expected-height="40"></div>
+     </div>
+</div>
+
+<!-- RTL direction (it should not affect the block-flow) with opposite directions grid container vs grid item. -->
+<div style="position: relative">
+     <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+         <div class="alignSelfStretch firstRowFirstColumn directionRTL" data-offset-x="0" data-offset-y="0" data-expected-width="100" data-expected-height="200"></div>
+         <div class="cell alignSelfStart firstRowSecondColumn directionRTL" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfEnd firstRowSecondColumn directionRTL" data-offset-x="100" data-offset-y="160" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfCenter secondRowFirstColumn directionRTL" data-offset-x="0" data-offset-y="280" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfRight secondRowSecondColumn directionRTL" data-offset-x="100" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfLeft secondRowSecondColumn directionRTL" data-offset-x="100" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+     </div>
+</div>
+
+<div style="position: relative">
+     <div class="grid fit-content" data-expected-width="200" data-expected-height="400">
+         <div class="cell alignSelfFlexEnd firstRowFirstColumn directionRTL" data-offset-x="0" data-offset-y="160" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfFlexStart firstRowSecondColumn directionRTL" data-offset-x="100" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfSelfStart secondRowFirstColumn directionRTL" data-offset-x="0" data-offset-y="200" data-expected-width="20" data-expected-height="40"></div>
+         <div class="cell alignSelfSelfEnd secondRowSecondColumn directionRTL" data-offset-x="100" data-offset-y="360" data-expected-width="20" data-expected-height="40"></div>
+     </div>
+</div>
+
+<!-- Vertical RL writing mode. -->
+<div style="position: relative">
+    <div class="grid fit-content verticalRL" data-expected-width="400" data-expected-height="200">
+        <div class="alignSelfStretch firstRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell alignSelfStart firstRowSecondColumn" data-offset-x="380" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfEnd firstRowSecondColumn" data-offset-x="200" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfCenter secondRowFirstColumn" data-offset-x="90" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfRight secondRowSecondColumn" data-offset-x="180" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfLeft secondRowSecondColumn" data-offset-x="180" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content verticalRL" data-expected-width="400" data-expected-height="200">
+        <div class="cell alignSelfFlexEnd firstRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfFlexStart firstRowSecondColumn" data-offset-x="380" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfSelfStart secondRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfSelfEnd secondRowSecondColumn" data-offset-x="0" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- Vertical LR writing mode. -->
+<div style="position: relative">
+    <div class="grid fit-content verticalLR" data-expected-width="400" data-expected-height="200">
+        <div class="alignSelfStretch firstRowFirstColumn" data-offset-x="0" data-offset-y="0" data-expected-width="200" data-expected-height="100"></div>
+        <div class="cell alignSelfStart firstRowSecondColumn" data-offset-x="0" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfEnd firstRowSecondColumn" data-offset-x="180" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfCenter secondRowFirstColumn" data-offset-x="290" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfRight secondRowSecondColumn" data-offset-x="200" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfLeft secondRowSecondColumn" data-offset-x="200" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content verticalLR" data-expected-width="400" data-expected-height="200">
+        <div class="cell alignSelfFlexEnd firstRowFirstColumn" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfFlexStart firstRowSecondColumn" data-offset-x="0" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfSelfStart secondRowFirstColumn" data-offset-x="200" data-offset-y="0" data-expected-width="20" data-expected-height="40"></div>
+        <div class="cell alignSelfSelfEnd secondRowSecondColumn" data-offset-x="380" data-offset-y="100" data-expected-width="20" data-expected-height="40"></div>
+    </div>
+</div>
+
+<!-- Vertical RL writing mode with opposite block-flow directions grid container vs grid item. -->
+<div style="position: relative">
+    <div class="grid fit-content verticalRL" data-expected-width="400" data-expected-height="200">
+        <div class="alignSelfStretch firstRowFirstColumn verticalLR" data-offset-x="200" data-offset-y="0" data-expected-width="200" data-expected-height="100">
+        </div>
+        <div class="cell alignSelfStart firstRowSecondColumn verticalLR" data-offset-x="380" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfEnd firstRowSecondColumn verticalLR" data-offset-x="200" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfCenter secondRowFirstColumn verticalLR" data-offset-x="90" data-offset-y="0" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfRight secondRowSecondColumn verticalLR" data-offset-x="180" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfLeft secondRowSecondColumn verticalLR" data-offset-x="180" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content verticalRL" data-expected-width="400" data-expected-height="200">
+        <div class="cell alignSelfFlexEnd firstRowFirstColumn verticalLR" data-offset-x="200" data-offset-y="0" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfFlexStart firstRowSecondColumn verticalLR" data-offset-x="380" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfSelfStart secondRowFirstColumn verticalLR" data-offset-x="0" data-offset-y="0" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfSelfEnd secondRowSecondColumn verticalLR" data-offset-x="180" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+    </div>
+</div>
+
+<!-- Vertical LR writing mode with opposite block-flow directions grid container vs grid item. -->
+<div style="position: relative">
+    <div class="grid fit-content verticalLR" data-expected-width="400" data-expected-height="200">
+        <div class="alignSelfStretch firstRowFirstColumn verticalRL" data-offset-x="0" data-offset-y="0" data-expected-width="200" data-expected-height="100">
+        </div>
+        <div class="cell alignSelfStart firstRowSecondColumn verticalRL" data-offset-x="0" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfEnd firstRowSecondColumn verticalRL" data-offset-x="180" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfCenter secondRowFirstColumn verticalRL" data-offset-x="290" data-offset-y="0" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfRight secondRowSecondColumn verticalRL" data-offset-x="200" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfLeft secondRowSecondColumn verticalRL" data-offset-x="200" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+    </div>
+</div>
+
+<div style="position: relative">
+    <div class="grid fit-content verticalLR" data-expected-width="400" data-expected-height="200">
+        <div class="cell alignSelfFlexEnd firstRowFirstColumn verticalRL" data-offset-x="180" data-offset-y="0" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfFlexStart firstRowSecondColumn verticalRL" data-offset-x="0" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfSelfStart secondRowFirstColumn verticalRL" data-offset-x="380" data-offset-y="0" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+        <div class="cell alignSelfSelfEnd secondRowSecondColumn verticalRL" data-offset-x="200" data-offset-y="100" data-expected-width="20" data-expected-height="40">
+            <div class="item"></div>
+        </div>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/fast/forms/resources/picker-common.js b/third_party/blink/web_tests/fast/forms/resources/picker-common.js
index 8db456d..9444002 100644
--- a/third_party/blink/web_tests/fast/forms/resources/picker-common.js
+++ b/third_party/blink/web_tests/fast/forms/resources/picker-common.js
@@ -11,8 +11,8 @@
     setTimeout(popupOpenCallback, 20);
 }
 
-function waitUntilClosing(callback, customDelay) {
-    setTimeout(callback, (customDelay !== undefined) ? customDelay : 1);
+function waitUntilClosing(callback) {
+    setTimeout(callback, 1);
 }
 
 function rootWindow() {
@@ -34,6 +34,34 @@
 // - INPUT color with DATALIST
 // - INPUT date/datetime-local/month/week
 function openPicker(element, callback, errorCallback) {
+    popupWindow = openPickerHelper(element);
+    if (typeof callback === "function" && popupWindow)
+        setPopupOpenCallback(callback);
+    else if (typeof errorCallback === "function" && !popupWindow)
+        errorCallback();
+}
+
+// openPickerWithPromise opens a picker UI for the following types:
+// - menulist SELECT
+// - INPUT color
+// - INPUT date/datetime-local/month/week
+//
+// Returns a Promise that resolves when the popup has been opened.
+function openPickerWithPromise(element) {
+    return new Promise(function(resolve, reject) {
+        popupWindow = openPickerHelper(element);
+        if (popupWindow) {
+            popupWindow.addEventListener("didOpenPicker", resolve, false);
+        } else {
+            reject();
+        }
+    });
+}
+
+// Helper function for openPicker and openPickerWithPromise.
+// Performs the keystrokes that will cause the picker to open,
+// and returns the popup window, or null.
+function openPickerHelper(element) {
     element.offsetTop; // Force to lay out
     element.focus();
     if (element.tagName === "SELECT") {
@@ -45,11 +73,7 @@
             eventSender.keyDown("ArrowDown", ["altKey"]);
         }
     }
-    popupWindow = internals.pagePopupWindow;
-    if (typeof callback === "function" && popupWindow)
-        setPopupOpenCallback(callback);
-    else if (typeof errorCallback === "function" && !popupWindow)
-        errorCallback();
+    return internals.pagePopupWindow;
 }
 
 function clickToOpenPicker(x, y, callback, errorCallback) {
diff --git a/third_party/blink/web_tests/hid/hidDevice_deviceInfo.html b/third_party/blink/web_tests/hid/hidDevice_deviceInfo.html
index b6055c95..7f18408 100644
--- a/third_party/blink/web_tests/hid/hidDevice_deviceInfo.html
+++ b/third_party/blink/web_tests/hid/hidDevice_deviceInfo.html
@@ -93,8 +93,11 @@
   fake.setSelectedDevice(guid);
 
   await trustedClick();
-  const d = await navigator.hid.requestDevice({filters: []});
+  const devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array, 'devices instanceof Array');
+  assert_equals(devices.length, 1, 'devices.length');
 
+  const d = devices[0];
   assert_true(d instanceof HIDDevice, 'device instanceof HIDDevice');
   assert_false(d.opened, 'device.opened');
   assert_equals(d.vendorId, kTestVendorId, 'device.vendorId');
@@ -168,8 +171,11 @@
   fake.setSelectedDevice(guid);
 
   await trustedClick();
-  const d = await navigator.hid.requestDevice({filters: []});
+  const devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array, 'devices instanceof Array');
+  assert_equals(devices.length, 1, 'devices.length');
 
+  const d = devices[0];
   assert_true(d instanceof HIDDevice, 'device instanceof HIDDevice');
   assert_equals(d.collections.length, 1, 'device.collections.length');
 
diff --git a/third_party/blink/web_tests/hid/hidDevice_openAndClose.html b/third_party/blink/web_tests/hid/hidDevice_openAndClose.html
index 6be9af6a..57c3fb1 100644
--- a/third_party/blink/web_tests/hid/hidDevice_openAndClose.html
+++ b/third_party/blink/web_tests/hid/hidDevice_openAndClose.html
@@ -18,7 +18,11 @@
   fake.setSelectedDevice(guid);
 
   await trustedClick();
-  const device = await navigator.hid.requestDevice({filters: []});
+  const devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array);
+  assert_equals(devices.length, 1);
+
+  const device = devices[0];
   assert_true(device instanceof HIDDevice);
   assert_false(device.opened);
 
@@ -31,7 +35,12 @@
   fake.setSelectedDevice(guid);
 
   await trustedClick();
-  const device = await navigator.hid.requestDevice({filters: []});
+  const devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array);
+  assert_equals(devices.length, 1);
+
+  const device = devices[0];
+  assert_true(device instanceof HIDDevice);
   assert_false(device.opened);
 
   await device.open();
@@ -46,7 +55,12 @@
   fake.setSelectedDevice(guid);
 
   await trustedClick();
-  const device = await navigator.hid.requestDevice({filters: []});
+  const devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array);
+  assert_equals(devices.length, 1);
+
+  const device = devices[0];
+  assert_true(device instanceof HIDDevice);
   assert_false(device.opened);
 
   await device.open();
@@ -58,7 +72,12 @@
   fake.setSelectedDevice(guid);
 
   await trustedClick();
-  const device = await navigator.hid.requestDevice({filters: []});
+  const devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array);
+  assert_equals(devices.length, 1);
+
+  const device = devices[0];
+  assert_true(device instanceof HIDDevice);
   assert_false(device.opened);
 
   const firstRequest = device.open();
diff --git a/third_party/blink/web_tests/hid/hidDevice_reports.html b/third_party/blink/web_tests/hid/hidDevice_reports.html
index 83d8088..a00047a 100644
--- a/third_party/blink/web_tests/hid/hidDevice_reports.html
+++ b/third_party/blink/web_tests/hid/hidDevice_reports.html
@@ -19,11 +19,16 @@
 // the newly created device, and returns the HidConnection fake.
 async function addAndOpenDevice(fake) {
   const deviceInfo = fake.makeDevice(kTestVendorId, kTestProductId);
-  const guid = fake.addDevice(deviceInfo);
-  fake.setSelectedDevice(guid);
+  const guid = deviceInfo.guid;
+  const key = fake.addDevice(deviceInfo);
+  fake.setSelectedDevice(key);
 
   await trustedClick();
-  const device = await navigator.hid.requestDevice({filters: []});
+  const devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array);
+  assert_equals(devices.length, 1);
+
+  const device = devices[0];
   assert_true(device instanceof HIDDevice);
 
   await device.open();
diff --git a/third_party/blink/web_tests/hid/hid_requestDevice.html b/third_party/blink/web_tests/hid/hid_requestDevice.html
index edb92ac..7e89005 100644
--- a/third_party/blink/web_tests/hid/hid_requestDevice.html
+++ b/third_party/blink/web_tests/hid/hid_requestDevice.html
@@ -25,27 +25,32 @@
   interceptor.start();
 
   await trustedClick();
+  let devices = null;
   try {
-    await promise_rejects(
-        t, 'NotFoundError', navigator.hid.requestDevice({filters: []}));
+    devices = await navigator.hid.requestDevice({filters: []});
   } finally {
     interceptor.stop();
   }
-}, 'requestDevice() rejects if Mojo service connection fails');
+  assert_true(devices instanceof Array);
+  assert_equals(devices.length, 0);
+}, 'requestDevice() returns an empty array if Mojo service connection fails');
 
 hid_test(async (t, fake) => {
   await trustedClick();
-  return promise_rejects(
-      t, 'NotFoundError', navigator.hid.requestDevice({filters: []}));
-}, 'requestDevice() rejects if no device has been selected');
+  let devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array);
+  assert_equals(devices.length, 0);
+}, 'requestDevice() returns an empty array if no device has been selected');
 
 hid_test(async (t, fake) => {
   let guid = fake.addDevice(fake.makeDevice(kTestVendorId, kTestProductId));
   fake.setSelectedDevice(guid);
 
   await trustedClick();
-  let device = await navigator.hid.requestDevice({filters: []});
-  assert_true(device instanceof HIDDevice);
+  let devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array);
+  assert_equals(devices.length, 1);
+  assert_true(devices[0] instanceof HIDDevice);
 }, 'requestDevice() returns the selected device');
 
 hid_test(async (t, fake) => {
@@ -53,12 +58,56 @@
   fake.setSelectedDevice(guid);
 
   await trustedClick();
-  let firstDevice = await navigator.hid.requestDevice({filters: []});
-  assert_true(firstDevice instanceof HIDDevice);
-  let secondDevice = await navigator.hid.requestDevice({filters: []});
-  assert_true(secondDevice instanceof HIDDevice);
-  assert_true(firstDevice === secondDevice);
+  let firstDevices = await navigator.hid.requestDevice({filters: []});
+  assert_true(firstDevices instanceof Array);
+  assert_equals(firstDevices.length, 1);
+  assert_true(firstDevices[0] instanceof HIDDevice);
+  let secondDevices = await navigator.hid.requestDevice({filters: []});
+  assert_false(firstDevices === secondDevices);
+  assert_true(secondDevices instanceof Array);
+  assert_equals(secondDevices.length, 1);
+  assert_true(secondDevices[0] instanceof HIDDevice);
+  assert_true(firstDevices[0] === secondDevices[0]);
 }, 'requestDevice() returns the same device object every time');
 
+hid_test(async (t, fake) => {
+  // Construct two HidDeviceInfo objects with the same physical device ID to
+  // simulate a device with two HID interfaces.
+  let device0 = fake.makeDevice(kTestVendorId, kTestProductId);
+  let device1 = fake.makeDevice(kTestVendorId, kTestProductId);
+  device1.physicalDeviceId = device0.physicalDeviceId;
+  let key0 = fake.addDevice(device0);
+  let key1 = fake.addDevice(device1);
+  assert_equals(key0, key1);
+  fake.setSelectedDevice(key0);
+
+  await trustedClick();
+  let devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array);
+  assert_equals(2, devices.length);
+  assert_true(devices[0] instanceof HIDDevice);
+  assert_true(devices[1] instanceof HIDDevice);
+  assert_false(devices[0] === devices[1]);
+}, 'requestDevice() returns 2 HIDDevices for a device with 2 HID interfaces');
+
+hid_test(async (t, fake) => {
+  // Construct two HidDeviceInfo objects with empty physical device IDs.
+  let device0 = fake.makeDevice(kTestVendorId, kTestProductId);
+  let device1 = fake.makeDevice(kTestVendorId, kTestProductId);
+  device0.physicalDeviceId = '';
+  device1.physicalDeviceId = '';
+  let key0 = fake.addDevice(device0);
+  let key1 = fake.addDevice(device1);
+  assert_not_equals('', key0);
+  assert_not_equals(key1, key0);
+  fake.setSelectedDevice(key0);
+
+  await trustedClick();
+  let devices = await navigator.hid.requestDevice({filters: []});
+  assert_true(devices instanceof Array);
+  assert_equals(1, devices.length);
+  assert_true(devices[0] instanceof HIDDevice);
+}, 'requestDevice() does not merge devices with empty physical device IDs');
+
 </script>
 </body>
diff --git a/third_party/blink/web_tests/hid/resources/hid-test-utils.js b/third_party/blink/web_tests/hid/resources/hid-test-utils.js
index ed12d48..4950dc8 100644
--- a/third_party/blink/web_tests/hid/resources/hid-test-utils.js
+++ b/third_party/blink/web_tests/hid/resources/hid-test-utils.js
@@ -152,7 +152,7 @@
   reset() {
     this.devices_ = new Map();
     this.fakeConnections_ = new Map();
-    this.selectedDevice_ = null;
+    this.selectedDevices_ = [];
   }
 
   // Creates and returns a HidDeviceInfo with the specified device IDs.
@@ -171,24 +171,32 @@
     return info;
   }
 
-  // Simulates a connected device. Returns the device GUID. A device connection
-  // event is not generated.
+  // Simulates a connected device. Returns the key used to store the device in
+  // the map. The key is either the physical device ID, or the device GUID if it
+  // has no physical device ID. A device connection event is not generated.
   addDevice(deviceInfo) {
-    this.devices_.set(deviceInfo.guid, deviceInfo);
-    return deviceInfo.guid;
+    let key = deviceInfo.physicalDeviceId;
+    if (key.length === 0)
+      key = deviceInfo.guid;
+    let devices = this.devices_.get(key);
+    if (devices === undefined)
+      devices = [];
+    devices.push(deviceInfo);
+    this.devices_.set(key, devices);
+    return key;
   }
 
   // Simulates disconnecting a connected device. A device disconnection event is
   // not generated.
-  removeDevice(guid) {
-    this.devices_.delete(guid);
+  removeDevice(key) {
+    this.devices_.delete(key);
   }
 
-  // Sets the GUID of the device that will be returned as the selected item the
-  // next time requestDevice is called. The device with this GUID must have been
+  // Sets the key of the device that will be returned as the selected item the
+  // next time requestDevice is called. The device with this key must have been
   // previously added with addDevice.
-  setSelectedDevice(guid) {
-    this.selectedDevice_ = this.devices_.get(guid);
+  setSelectedDevice(key) {
+    this.selectedDevices_ = this.devices_.get(key);
   }
 
   // Returns the fake HidConnection object for this device, if there is one. A
@@ -205,13 +213,17 @@
   // devices that the client has already been granted permission to access, but
   // for the fake implementation all simulated devices are returned.
   async getDevices() {
-    return { devices: Array.from(this.devices_.values()) };
+    let devices = [];
+    this.devices_.forEach((value) => {
+      devices = devices.concat(value);
+    });
+    return { devices: devices };
   }
 
-  // Simulates a device chooser prompt, returning |selectedDevice_| as the
+  // Simulates a device chooser prompt, returning |selectedDevices_| as the
   // simulated selection. |filters| is ignored.
   async requestDevice(filters) {
-    return { device: this.selectedDevice_ };
+    return { devices: this.selectedDevices_ };
   }
 
   // Returns a fake connection to the device with the specified GUID. If
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/threads-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/threads-a11y-test-expected.txt
new file mode 100644
index 0000000..e2103dcc
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/threads-a11y-test-expected.txt
@@ -0,0 +1,7 @@
+Testing accessibility in the threads sidebar pane.
+Threads sidebar pane content:
+ Mainworker-source.jspaused
+Running the axe-core linter on the threads sidebar pane.
+aXe violations: []
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/threads-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/threads-a11y-test.js
new file mode 100644
index 0000000..34aaca9
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/threads-a11y-test.js
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  TestRunner.addResult('Testing accessibility in the threads sidebar pane.');
+
+  await TestRunner.loadModule('axe_core_test_runner');
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.showPanel('sources');
+  await SourcesTestRunner.startDebuggerTestPromise(/* quiet */ true);
+
+  await TestRunner.evaluateInPagePromise(`new Worker('../../sources/resources/worker-source.js')`);
+  await SourcesTestRunner.waitUntilPausedPromise();
+  const sourcesPanel = UI.panels.sources;
+  sourcesPanel._showThreadsIfNeeded();
+
+  const threadsSidebarPane = await sourcesPanel._threadsSidebarPane.widget();
+  const threadsSidebarElement = threadsSidebarPane.contentElement;
+  TestRunner.addResult(`Threads sidebar pane content:\n ${threadsSidebarElement.deepTextContent()}`);
+  TestRunner.addResult('Running the axe-core linter on the threads sidebar pane.');
+  await AxeCoreTestRunner.runValidation(threadsSidebarElement);
+  TestRunner.completeTest();
+
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/jump-to-previous-editing-location.js b/third_party/blink/web_tests/http/tests/devtools/jump-to-previous-editing-location.js
index 2459b5a..881bbec 100644
--- a/third_party/blink/web_tests/http/tests/devtools/jump-to-previous-editing-location.js
+++ b/third_party/blink/web_tests/http/tests/devtools/jump-to-previous-editing-location.js
@@ -206,7 +206,7 @@
       function onScriptSource(uiSourceCode) {
         var linkifier = new Components.Linkifier();
         var anchorURI = uiSourceCode.url();
-        var anchor = linkifier.linkifyScriptLocation(SDK.targetManager.mainTarget(), null, anchorURI, 10, 1);
+        var anchor = linkifier.linkifyScriptLocation(SDK.targetManager.mainTarget(), null, anchorURI, 10, {columnNumber: 1});
         var info = Components.Linkifier._linkInfo(anchor);
         Common.Revealer.reveal(info.uiLocation).then(function() {
           TestRunner.addResult('Selection: ' + panel.visibleView.textEditor.selection().toString());
diff --git a/third_party/blink/web_tests/http/tests/devtools/persistence/persistence-move-breakpoints-on-reload.js b/third_party/blink/web_tests/http/tests/devtools/persistence/persistence-move-breakpoints-on-reload.js
index 70da08c..6f722751 100644
--- a/third_party/blink/web_tests/http/tests/devtools/persistence/persistence-move-breakpoints-on-reload.js
+++ b/third_party/blink/web_tests/http/tests/devtools/persistence/persistence-move-breakpoints-on-reload.js
@@ -51,11 +51,10 @@
   ]);
 
   function dumpBreakpointSidebarPane() {
-    var paneElement = self.runtime.sharedInstance(Sources.JavaScriptBreakpointsSidebarPane).contentElement;
-    var empty = paneElement.querySelector('.gray-info-message');
-    if (empty)
-      return TestRunner.textContentWithLineBreaks(empty);
-    var entries = Array.from(paneElement.querySelectorAll('.breakpoint-entry'));
+    var pane = self.runtime.sharedInstance(Sources.JavaScriptBreakpointsSidebarPane);
+    if (!pane._emptyElement.classList.contains('hidden'))
+      return TestRunner.textContentWithLineBreaks(pane._emptyElement);
+    var entries = Array.from(pane.contentElement.querySelectorAll('.breakpoint-entry'));
     for (var entry of entries) {
       var uiLocation = entry[Sources.JavaScriptBreakpointsSidebarPane._locationSymbol];
       TestRunner.addResult('    ' + uiLocation.uiSourceCode.url() + ':' + uiLocation.lineNumber);
diff --git a/third_party/blink/web_tests/http/tests/devtools/persistence/persistence-move-breakpoints.js b/third_party/blink/web_tests/http/tests/devtools/persistence/persistence-move-breakpoints.js
index 3db85e02..5c7492f2 100644
--- a/third_party/blink/web_tests/http/tests/devtools/persistence/persistence-move-breakpoints.js
+++ b/third_party/blink/web_tests/http/tests/devtools/persistence/persistence-move-breakpoints.js
@@ -53,11 +53,10 @@
   ]);
 
   function dumpBreakpointSidebarPane() {
-    var paneElement = self.runtime.sharedInstance(Sources.JavaScriptBreakpointsSidebarPane).contentElement;
-    var empty = paneElement.querySelector('.gray-info-message');
-    if (empty)
-      return TestRunner.textContentWithLineBreaks(empty);
-    var entries = Array.from(paneElement.querySelectorAll('.breakpoint-entry'));
+    var pane = self.runtime.sharedInstance(Sources.JavaScriptBreakpointsSidebarPane);
+    if (!pane._emptyElement.classList.contains('hidden'))
+      return TestRunner.textContentWithLineBreaks(pane._emptyElement);
+    var entries = Array.from(pane.contentElement.querySelectorAll('.breakpoint-entry'));
     for (var entry of entries) {
       var uiLocation = entry[Sources.JavaScriptBreakpointsSidebarPane._locationSymbol];
       TestRunner.addResult('    ' + uiLocation.uiSourceCode.url() + ':' + uiLocation.lineNumber);
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-sidebar-pane-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-sidebar-pane-expected.txt
index 16c1c8c..ab8eea9 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-sidebar-pane-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/breakpoints-sidebar-pane-expected.txt
@@ -4,16 +4,16 @@
 
 Set first breakpoint.
 Breakpoint sidebar pane 
-a.js:18  return a + b;
+a.js:18checked  return a + b;
 
 Set second breakpoint on the same line.
 Breakpoint sidebar pane 
-a.js:18:3return a + b;
-a.js:18:16
+a.js:18:3checkedreturn a + b;
+a.js:18:16checked
 
 Set a third breakpoint on a different line.
 Breakpoint sidebar pane 
-a.js:17  b = b / 2;
-a.js:18:3return a + b;
-a.js:18:16
+a.js:17checked  b = b / 2;
+a.js:18:3checkedreturn a + b;
+a.js:18:16checked
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps-expected.txt
index e653e02..eeafdaf8 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/debugger-reload-breakpoints-with-source-maps-expected.txt
@@ -1,7 +1,7 @@
 Tests "reload" from within inspector window while on pause.
 Breakpoint sidebar pane before reload:
-source1.js:17}
+source1.js:17checked}
 Page reloaded.
 Breakpoint sidebar pane after reload:
-source1.js:17}
+source1.js:17checked}
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints-expected.txt
index ae9cd336..6441a350 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/disable-breakpoints-expected.txt
@@ -8,7 +8,7 @@
 Call stack:
     0) testFunction (disable-breakpoints.js:13)
 Breakpoint sidebar pane 
-disable-breakpoints.js:13          var x = Math.sqrt(10);
+disable-breakpoints.js:13checked          var x = Math.sqrt(10);
 Script execution resumed.
 Test function finished.
 Disabling breakpoints...
@@ -24,11 +24,11 @@
 Call stack:
     0) testFunction (disable-breakpoints.js:13)
 Breakpoint sidebar pane 
-disable-breakpoints.js:13          var x = Math.sqrt(10);
+disable-breakpoints.js:13checked          var x = Math.sqrt(10);
 Script execution resumed.
 Test function finished.
 Breakpoint sidebar pane 
-disable-breakpoints.js:13          var x = Math.sqrt(10);
+disable-breakpoints.js:13checked          var x = Math.sqrt(10);
 Breakpoints removed.
 Breakpoint sidebar pane 
 No breakpoints
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-breakpoint-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-breakpoint-expected.txt
index 8c47bb6..cea819f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-breakpoint-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-breakpoint-expected.txt
@@ -8,7 +8,7 @@
 Call stack:
     0) testFunction (set-breakpoint.html:14)
 Breakpoint sidebar pane 
-set-breakpoint.html:14  var x = Math.sqrt(10);
+set-breakpoint.html:14checked  var x = Math.sqrt(10);
 Breakpoint sidebar pane 
 No breakpoints
 Script execution resumed.
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint-expected.txt
index 7ae6c9d..8c4a0c2 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-conditional-breakpoint-expected.txt
@@ -8,7 +8,7 @@
 Call stack:
     0) testFunction (set-breakpoint.html:14)
 Breakpoint sidebar pane 
-set-breakpoint.html:14  var x = Math.sqrt(10);
+set-breakpoint.html:14checked  var x = Math.sqrt(10);
 Breakpoint sidebar pane 
 No breakpoints
 Script execution resumed.
@@ -18,7 +18,7 @@
 Set timer for test function.
 Test function finished.
 Breakpoint sidebar pane 
-set-breakpoint.html:14  var x = Math.sqrt(10);
+set-breakpoint.html:14checked  var x = Math.sqrt(10);
 Breakpoints removed.
 Breakpoint sidebar pane 
 No breakpoints
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-logpoint-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-logpoint-expected.txt
index c80454f76..609cc02 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-logpoint-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-breakpoints/set-logpoint-expected.txt
@@ -6,7 +6,7 @@
 Set timer for test function.
 Test function finished.
 Breakpoint sidebar pane 
-set-breakpoint.html:15  return x;
+set-breakpoint.html:15checked  return x;
 Message count: 1
 VM:1 x is 3.1622776601683795
 Breakpoints removed.
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint-expected.txt
index a8349836..5f61f1f 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint-expected.txt
@@ -2,6 +2,6 @@
 
 Script execution paused.
 Breakpoint sidebar pane while paused
-click-breakpoints.js:4    var b = 1; // The breakpoint should happen here
+click-breakpoints.js:4checked breakpoint hit    var b = 1; // The breakpoint should happen here
 Script execution resumed.
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint.js b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint.js
index a52ae876..b8f9506 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint.js
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/click-gutter-breakpoint.js
@@ -44,14 +44,11 @@
         .then(runScript);
   }
 
-  function runScript() {
-    Promise
-        .all([
-          SourcesTestRunner.waitBreakpointSidebarPane(),
-          new Promise(resolve => SourcesTestRunner.waitUntilPaused(resolve))
-        ])
-        .then(() => SourcesTestRunner.dumpBreakpointSidebarPane('while paused'))
-        .then(() => SourcesTestRunner.completeDebuggerTest());
+  async function runScript() {
     TestRunner.evaluateInPageWithTimeout('f2()');
+    await SourcesTestRunner.waitUntilPausedPromise();
+    await SourcesTestRunner.waitBreakpointSidebarPane();
+    SourcesTestRunner.dumpBreakpointSidebarPane('while paused');
+    SourcesTestRunner.completeDebuggerTest();
   }
 })();
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2-expected.txt
index b703bf6a..d6f3d79 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-2-expected.txt
@@ -6,7 +6,7 @@
 Running: testBreakpointsSetAndRemoveInFormattedSource
 Script execution paused.
 Breakpoint sidebar pane while paused in pretty printed
-unformatted2.js:formatted:4    var c = 3;
+unformatted2.js:formatted:4checked    var c = 3;
 Breakpoint sidebar pane while paused in raw
 No breakpoints
 Script execution resumed.
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3-expected.txt
index a3f6f63..ae64391 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger-ui/script-formatter-breakpoints-3-expected.txt
@@ -6,8 +6,8 @@
 Running: testBreakpointsSetInFormattedAndRemoveInOriginalSource
 Script execution paused.
 Breakpoint sidebar pane while paused in pretty printed
-unformatted3.js:formatted:4    var c = 3;
+unformatted3.js:formatted:4checked    var c = 3;
 Breakpoint sidebar pane while paused in raw
-unformatted3.js:3    var a=0;var b=1;var c=3;var d=4;var e=5;
+unformatted3.js:3checked breakpoint hit    var a=0;var b=1;var c=3;var d=4;var e=5;
 Script execution resumed.
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/sources/debugger/js-with-inline-stylesheets-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sources/debugger/js-with-inline-stylesheets-expected.txt
index fbc526e..55c319a 100644
--- a/third_party/blink/web_tests/http/tests/devtools/sources/debugger/js-with-inline-stylesheets-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/sources/debugger/js-with-inline-stylesheets-expected.txt
@@ -8,7 +8,7 @@
 Call stack:
     0) testFunction (js-with-inline-stylesheets.js:13)
 Breakpoint sidebar pane 
-js-with-inline-stylesheets.js:13          var x = Math.sqrt(10);
+js-with-inline-stylesheets.js:13checked          var x = Math.sqrt(10);
 Breakpoint sidebar pane 
 No breakpoints
 Script execution resumed.
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1-expected.txt
index 0505d56a..ac0d031 100644
--- a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-1-expected.txt
@@ -6,12 +6,12 @@
 Running: testBreakpointsInOriginalAndFormattedSource
 Script execution paused.
 Breakpoint sidebar pane while paused in raw
-script-formatter-breakpoints-1.html:10    var f=0;
+script-formatter-breakpoints-1.html:10checked breakpoint hit    var f=0;
 Script execution resumed.
 Script execution paused.
 Breakpoint sidebar pane while paused in pretty printed
-script-formatter-breakpoints-1.html:formatted:12                var f = 0;
+script-formatter-breakpoints-1.html:formatted:12checked breakpoint hit                var f = 0;
 Breakpoint sidebar pane while paused in raw
-script-formatter-breakpoints-1.html:10    var f=0;
+script-formatter-breakpoints-1.html:10checked breakpoint hit    var f=0;
 Script execution resumed.
 
diff --git a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL-expected.txt b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL-expected.txt
index 57424018..c05ee21 100644
--- a/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/startup/sources/debugger/script-formatter-breakpoints-inline-with-sourceURL-expected.txt
@@ -6,14 +6,14 @@
 Running: testBreakpointsInOriginalAndFormattedSource
 Script execution paused.
 Breakpoint sidebar pane while paused in raw
-named-inline-script.js:5      console.log("Hello!");
+named-inline-script.js:5checked breakpoint hit      console.log("Hello!");
 Script execution resumed.
 Script execution paused.
 Breakpoint sidebar pane while paused in pretty printed
-named-inline-script.js:formatted:3    console.log("Hello!");
+named-inline-script.js:formatted:3checked breakpoint hit    console.log("Hello!");
 Breakpoint sidebar pane while paused after removing breakpoint in pretty printed and closing pretty printed
 No breakpoints
 Breakpoint sidebar pane while paused in original script again
-named-inline-script.js:5      console.log("Hello!");
+named-inline-script.js:5checked breakpoint hit      console.log("Hello!");
 Script execution resumed.
 
diff --git a/third_party/blink/web_tests/platform/linux/paint/invalidation/overflow/float-overflow-right-expected.txt b/third_party/blink/web_tests/platform/linux/paint/invalidation/overflow/float-overflow-right-expected.txt
index 6215329..b13aa04 100644
--- a/third_party/blink/web_tests/platform/linux/paint/invalidation/overflow/float-overflow-right-expected.txt
+++ b/third_party/blink/web_tests/platform/linux/paint/invalidation/overflow/float-overflow-right-expected.txt
@@ -308,6 +308,16 @@
         },
         {
           "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 255, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 249, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
           "rect": [701, 216, 40, 10],
           "reason": "geometry"
         },
@@ -397,16 +407,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutBlockFlow (floating) DIV",
-          "rect": [671, 255, 40, 10],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutBlockFlow (floating) DIV",
-          "rect": [671, 249, 40, 10],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
           "rect": [701, 543, 32, 20],
           "reason": "geometry"
@@ -418,6 +418,16 @@
         },
         {
           "object": "InlineTextBox 'x'",
+          "rect": [725, 257, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 251, 16, 17],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
           "rect": [725, 218, 16, 17],
           "reason": "geometry"
         },
@@ -427,16 +437,6 @@
           "reason": "geometry"
         },
         {
-          "object": "InlineTextBox 'x'",
-          "rect": [695, 257, 16, 17],
-          "reason": "geometry"
-        },
-        {
-          "object": "InlineTextBox 'x'",
-          "rect": [695, 251, 16, 17],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'x'",
           "rect": [727, 101, 16, 16],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
index ad5b3bc1..2524885 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
index 22fc39b..66f21ee 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
index a96af5a..769d4d1 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
index 72ada91..740bb2f 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
index d8e9a504..646c4ba 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
index a96af5a..769d4d1 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
index 97aa69b..2d1d977 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
index ebd6006..c9401392 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
index 409dc5b2..3d2a7e7 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
index 52973e1..aa8d65d 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
index 39155a4..edee1e2 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
index 91164820..2b0342b 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
index 1f84516..b82223be 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
index af23e04..e6f4b7eb 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
index 24db645..ffe957ed 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
index 67907e2..2e8fedb 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
index fa934f7..620d5c1 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
index fa934f7..620d5c1 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
index a132af6..58280c8 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
index 3676107..e2e039b 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
index 7f986f3a..0794555 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
index ded640b..03cecbd 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
index 068caaf..30f2afb 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
index b3f45d31..262b2f7 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
index e4d0dbc0..6a95095 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
index fbc265b0..5d72b31 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
index c7ff79e..b48a52d 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/controls-refresh/color/color-picker-appearance-zoom200-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/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
new file mode 100644
index 0000000..6695ec70
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-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/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
new file mode 100644
index 0000000..6695ec70
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-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/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
index 4f360c7..6695ec70 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
index 846b92b..1221349 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
index 2a39465..c18f1c6a 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
index a42e174..e558a6f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
index 97fdfd85..a3bc7785 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
index 2a39465..c18f1c6a 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
index cc02adf..0df61e8 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
index 2cf06642..a2ebf3aa 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
index 582b1f2..673b7d9 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
index 2eb7348a..1934d5f2 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
index 9c95a4c..b7023a1 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
index ca4f6c2..c97b53e8 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
index f5c0963..365e8e9 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
index d5243473..976df5f 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
index 95dbb3f5..78d9117 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
index 1264cde8..d5e7852 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
index 5e7e76b..5a72d10 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
index 5e7e76b..5a72d10 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
index f295b54..744026d 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
index 59b51d32..6fbf45a 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
index 256a39c..2b63d63e 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
index f95e09d..430ad28 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
index 4261cf7..4989767 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
index e7fbb0c5..18412aa5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
index 0ca136c..47cc3f7 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
index 52b8c37..cacbc05 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
index 7846219..c4565d5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/paint/invalidation/overflow/float-overflow-right-expected.txt b/third_party/blink/web_tests/platform/mac/paint/invalidation/overflow/float-overflow-right-expected.txt
index b8a349e4..bce4e50 100644
--- a/third_party/blink/web_tests/platform/mac/paint/invalidation/overflow/float-overflow-right-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/paint/invalidation/overflow/float-overflow-right-expected.txt
@@ -308,6 +308,16 @@
         },
         {
           "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 255, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 249, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
           "rect": [701, 216, 40, 10],
           "reason": "geometry"
         },
@@ -397,16 +407,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutBlockFlow (floating) DIV",
-          "rect": [671, 255, 40, 10],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutBlockFlow (floating) DIV",
-          "rect": [671, 249, 40, 10],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
           "rect": [701, 543, 32, 20],
           "reason": "geometry"
@@ -418,6 +418,16 @@
         },
         {
           "object": "InlineTextBox 'x'",
+          "rect": [725, 256, 16, 18],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 250, 16, 18],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
           "rect": [725, 217, 16, 18],
           "reason": "geometry"
         },
@@ -427,16 +437,6 @@
           "reason": "geometry"
         },
         {
-          "object": "InlineTextBox 'x'",
-          "rect": [695, 256, 16, 18],
-          "reason": "geometry"
-        },
-        {
-          "object": "InlineTextBox 'x'",
-          "rect": [695, 250, 16, 18],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'x'",
           "rect": [727, 100, 16, 17],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
index 3700a096..0506476 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
index 103fc646..d76ad62 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
index 513826a7..472b7cbb 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
index 7d3cd3b..2777e5e 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
index 782ffd67..3a771b5 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
index 513826a7..472b7cbb 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
index 5afd3ef..d7224dd 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
index 7a25036..d4c7f47 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
index c2e5b5a..8dc58ec 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
index 9376492..8380ffd7 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
index 7d69248..236fb85 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
index 3053c524..4d7a46a 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
index 2dbe819c..b2b9877 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
index 701399db..2266fbee 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
index 8c38887..1331ff29 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
index 7b30bdb..8b06bcf 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
index b3072285..2bf6e04b 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
index b3072285..2bf6e04b 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
index 0e60d0e6a..307468f7 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
index 4789f2a..f3dd485 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
index 7fbc4d3..130d9bc 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
index 2f3017d5..896918f 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
index 7263ab6..cd8e63ea 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
index 0faa811..6acfe9c3 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
index 16d1491..b4d50022 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
index 91efffd..1ae83c7 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
index e9f0823..9ea95f9 100644
--- a/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
+++ b/third_party/blink/web_tests/platform/mac/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/paint/invalidation/overflow/float-overflow-right-expected.txt b/third_party/blink/web_tests/platform/win/paint/invalidation/overflow/float-overflow-right-expected.txt
index fbb570ea..643980c 100644
--- a/third_party/blink/web_tests/platform/win/paint/invalidation/overflow/float-overflow-right-expected.txt
+++ b/third_party/blink/web_tests/platform/win/paint/invalidation/overflow/float-overflow-right-expected.txt
@@ -308,6 +308,16 @@
         },
         {
           "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 255, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 249, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
           "rect": [701, 216, 40, 10],
           "reason": "geometry"
         },
@@ -397,16 +407,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutBlockFlow (floating) DIV",
-          "rect": [671, 255, 40, 10],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutBlockFlow (floating) DIV",
-          "rect": [671, 249, 40, 10],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
           "rect": [701, 543, 32, 20],
           "reason": "geometry"
@@ -418,6 +418,16 @@
         },
         {
           "object": "InlineTextBox 'x'",
+          "rect": [725, 256, 16, 18],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 250, 16, 18],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
           "rect": [725, 217, 16, 18],
           "reason": "geometry"
         },
@@ -427,16 +437,6 @@
           "reason": "geometry"
         },
         {
-          "object": "InlineTextBox 'x'",
-          "rect": [695, 256, 16, 18],
-          "reason": "geometry"
-        },
-        {
-          "object": "InlineTextBox 'x'",
-          "rect": [695, 250, 16, 18],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'x'",
           "rect": [727, 101, 16, 16],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
index 39becea09..53e7683 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh-hc/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
index 2727347ca..d3c96b03 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color-scheme/color/color-picker-appearance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
index bb943ed..e96b861 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-click-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
index 0393f9d..92f8ebe 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
index 090a8c6..6776ef2 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-down-keyboard-navigation-from-top-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
index bb943ed..e96b861 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-drag-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
index 2a681ea..f0644fe 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
index f78fec6..3f4655f 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-bottom-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
index 599c090..4dfe404 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-left-keyboard-navigation-from-top-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
index e587ded..5e43cb4 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-bottom-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
index 8af1519d..9dbfbbd 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-right-keyboard-navigation-from-top-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
index b2792dd..2a47431 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-left-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
index 0afe6028..6bd37a8c 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-color-well-up-keyboard-navigation-from-bottom-right-corner-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
index a453c44..5e07307 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hex-format-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
index 77a9a66..1e6b80ff 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hsl-format-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
index 337eb82..85ffe05 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-accelerated-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
index d7a19a4..c959607d 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-click-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
index d7a19a4..c959607d 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-drag-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
index 98b5324..758ce17 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-keyboard-navigation-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
index ff4b7b44..dbf011f4 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-left-edge-zero-hue-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
index 500ab54f..47407a4 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-hue-slider-right-edge-zero-hue-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
index c3305767..6b94fb1 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-imperfect-match-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
index cbc0181..b985cc1 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-manual-color-change-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
index 82ae927..87be1f6 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-set-value-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
index 77f76f7..f989b7a 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-value-attribute-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
index 06e0ff9..edbf519b 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-zoom125-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
index 3515be60..c8f313e 100644
--- a/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/controls-refresh/color/color-picker-appearance-zoom200-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/paint/invalidation/overflow/float-overflow-right-expected.txt b/third_party/blink/web_tests/platform/win7/paint/invalidation/overflow/float-overflow-right-expected.txt
new file mode 100644
index 0000000..643980c
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win7/paint/invalidation/overflow/float-overflow-right-expected.txt
@@ -0,0 +1,563 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 537, 62, 37],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 531, 62, 37],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 498, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 492, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 459, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 453, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 420, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 414, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutFlexibleBox DIV class='outer box'",
+          "rect": [677, 249, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutFlexibleBox DIV class='outer box'",
+          "rect": [677, 243, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutFlexibleBox DIV class='outer box'",
+          "rect": [677, 210, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutFlexibleBox DIV class='outer box'",
+          "rect": [677, 204, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 171, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 165, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 132, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 126, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 93, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 87, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 54, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 48, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 15, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='outer'",
+          "rect": [677, 9, 62, 22],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 366, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 360, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 327, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 321, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 288, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutTable TABLE class='outer'",
+          "rect": [681, 282, 58, 28],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 504, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 498, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 465, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 459, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 426, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 420, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 216, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow DIV",
+          "rect": [683, 210, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 177, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 171, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 138, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 132, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 99, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 93, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 60, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 54, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 21, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [683, 15, 50, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGTableCell TD",
+          "rect": [689, 374, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGTableCell TD",
+          "rect": [689, 368, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGTableCell TD",
+          "rect": [689, 335, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGTableCell TD",
+          "rect": [689, 329, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGTableCell TD",
+          "rect": [689, 296, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGTableCell TD",
+          "rect": [689, 290, 42, 12],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 543, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 537, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 504, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 498, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 465, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 459, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 426, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 420, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 255, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 249, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 216, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutBlockFlow (floating) DIV",
+          "rect": [701, 210, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 177, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 171, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 99, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [701, 93, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [695, 60, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [695, 54, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [695, 21, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [695, 15, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [693, 138, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [693, 132, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [690, 375, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [690, 369, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [690, 336, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [690, 330, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [690, 297, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (floating) DIV",
+          "rect": [690, 291, 40, 10],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [701, 543, 32, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV",
+          "rect": [701, 537, 32, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 256, 16, 18],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 250, 16, 18],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 217, 16, 18],
+          "reason": "geometry"
+        },
+        {
+          "object": "InlineTextBox 'x'",
+          "rect": [725, 211, 16, 18],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [727, 101, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [727, 95, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [727, 62, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [727, 56, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [726, 377, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [726, 371, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [726, 338, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [726, 332, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 545, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 539, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 506, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 500, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 467, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 461, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 428, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 422, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 179, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 173, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 140, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [725, 134, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [721, 23, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [721, 17, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [718, 299, 16, 16],
+          "reason": "geometry"
+        },
+        {
+          "object": "NGPhysicalTextFragment 'x'",
+          "rect": [718, 293, 16, 16],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-enter-submission.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-enter-submission.html
deleted file mode 100644
index b6e958f..0000000
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-enter-submission.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src='../../../resources/testharness.js'></script>
-<script src='../../../resources/testharnessreport.js'></script>
-<script src='../../../fast/forms/resources/picker-common.js'></script>
-</head>
-<body>
-<input type='color' id='color' value='#80d9ff'>
-<script>
-'use strict';
-
-let t = async_test('Color picker: Pressing enter to submit color selection.');
-t.step(() => {
-  let colorControl = document.getElementById('color');
-  openPicker(colorControl, t.step_func(() => {
-    popupWindow.focus();
-    const popupDocument = popupWindow.document;
-    const colorWell = popupDocument.querySelector('color-well');
-    const colorWellRect = colorWell.getBoundingClientRect();
-    eventSender.mouseMoveTo(colorWellRect.left, colorWellRect.top);
-    eventSender.mouseDown();
-    eventSender.mouseMoveTo(colorWellRect.left + (colorWellRect.width * 4 / 10), colorWellRect.top + (colorWellRect.height * 6 / 10));
-    eventSender.mouseUp();
-    eventSender.keyDown('Enter');
-    waitUntilClosing(t.step_func_done(() => {
-      assert_equals(colorControl.value, '#3d5a66', 'Color control value should update after pressing enter.');
-    }), popupWindow.eval('ColorPicker').COMMIT_DELAY_MS);
-  }), t.step_func_done(() => {
-    assert_true(true, 'Popup did not open.');
-  }));
-});
-</script>
-</body>
-</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-format.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-format.html
new file mode 100644
index 0000000..b1882bd
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-format.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<script src='../../../fast/forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color' value='#000000'>
+<script>
+'use strict';
+
+promise_test(() => {
+  let colorControl = document.getElementById('color');
+  return openPickerWithPromise(colorControl)
+  .then(() => {
+    internals.pagePopupWindow.focus();
+    const popupDocument = internals.pagePopupWindow.document;
+    const formatToggler = popupDocument.querySelector('format-toggler');
+    formatToggler.click();  // first click changes format to HSL
+    formatToggler.click();  // second click changes format to Hex
+
+    eventSender.keyDown('Escape');
+    assert_equals(internals.pagePopupWindow, null, 'Single escape should close popup if manual color entry format but not color value changed');
+  });
+}, "Color picker: Format changes with no color value change don't affect escape behavior");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-keyboard-change.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-keyboard-change.html
new file mode 100644
index 0000000..0f88b34
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-keyboard-change.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<script src='../../../fast/forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color' value='#80d9ff'>
+<script>
+'use strict';
+
+promise_test(() => {
+  let colorControl = document.getElementById('color');
+  return openPickerWithPromise(colorControl)
+  .then(() => {
+    internals.pagePopupWindow.focus();
+    const popupDocument = internals.pagePopupWindow.document;
+    const colorWell = popupDocument.querySelector('color-well');
+    const rValueContainer = popupDocument.getElementById('rValueContainer');
+    const rValueContainerRect = rValueContainer.getBoundingClientRect();
+    eventSender.mouseMoveTo(rValueContainerRect.left + 1, rValueContainerRect.top);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+    assert_equals(rValueContainer.value, '128');
+    eventSender.keyDown('Delete');
+    assert_equals(rValueContainer.value, '28');
+    assert_equals(colorControl.value, '#1cd9ff', 'Expected color value to change from manualinput.');
+    eventSender.keyDown('Escape');
+    assert_equals(colorControl.value, '#80d9ff', 'Color control value should have reverted back after pressing escape.');
+    assert_equals(rValueContainer.value, '128');
+    assert_not_equals(internals.pagePopupWindow, null, 'Popup should still be open after escape that reverted color value.');
+    eventSender.keyDown('Escape');
+    assert_equals(internals.pagePopupWindow, null, 'Popup should close after second escape.');
+  });
+}, "Color picker: Pressing escape once discards manual color selection but keeps popup open, pressing again closes popup");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-mouse-change.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-mouse-change.html
new file mode 100644
index 0000000..0b36c36
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-mouse-change.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<script src='../../../fast/forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color' value='#80d9ff'>
+<script>
+'use strict';
+
+promise_test(() => {
+  let colorControl = document.getElementById('color');
+  return openPickerWithPromise(colorControl)
+  .then(() => {
+    internals.pagePopupWindow.focus();
+    const popupDocument = internals.pagePopupWindow.document;
+    const colorWell = popupDocument.querySelector('color-well');
+    const colorWellRect = colorWell.getBoundingClientRect();
+    eventSender.mouseMoveTo(colorWellRect.left, colorWellRect.top);
+    eventSender.mouseDown();
+    eventSender.mouseMoveTo(colorWellRect.left + (colorWellRect.width * 4 / 10), colorWellRect.top + (colorWellRect.height * 6 / 10));
+    eventSender.mouseUp();
+    assert_equals(colorControl.value, '#3d5a66', 'Expected color value to change from mouse input.');
+    eventSender.keyDown('Escape');
+    assert_equals(colorControl.value, '#80d9ff', 'Color control value should have reverted back after pressing escape.');
+    assert_not_equals(internals.pagePopupWindow, null, 'Popup should still be open after escape that reverted color value.');
+    eventSender.keyDown('Escape');
+    assert_equals(internals.pagePopupWindow, null, 'Popup should close after second escape.');
+  });
+}, "Color picker: Pressing escape once discards color selection but keeps popup open, pressing again closes popup");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-nochange.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-nochange.html
new file mode 100644
index 0000000..6db5d20
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-nochange.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<script src='../../../fast/forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color' value='#80d9ff'>
+<script>
+'use strict';
+
+promise_test(() => {
+  let colorControl = document.getElementById('color');
+  return openPickerWithPromise(colorControl)
+  .then(() => {
+    internals.pagePopupWindow.focus();
+    eventSender.keyDown('Escape');
+    assert_equals(internals.pagePopupWindow, null, 'Popup should close after pressing escape with no changes.');
+  });
+}, "Color picker: Pressing escape with no changes should close the popup");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-revert.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-revert.html
new file mode 100644
index 0000000..07137c1
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-revert.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<script src='../../../fast/forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color' value='#3d5a66'>
+<script>
+'use strict';
+
+promise_test(() => {
+  let colorControl = document.getElementById('color');
+  return openPickerWithPromise(colorControl)
+  .then(() => {
+    internals.pagePopupWindow.focus();
+    const popupDocument = internals.pagePopupWindow.document;
+    const colorWell = popupDocument.querySelector('color-well');
+    const colorWellRect = colorWell.getBoundingClientRect();
+    eventSender.mouseMoveTo(colorWellRect.left + (colorWellRect.width * 2 / 10), colorWellRect.top + (colorWellRect.height * 3 / 10));
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+    assert_equals(colorControl.value, '#8fa8b3', 'Mouse input should have changed color value.');
+
+    eventSender.mouseMoveTo(colorWellRect.left + (colorWellRect.width * 4 / 10), colorWellRect.top + (colorWellRect.height * 6 / 10));
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+    assert_equals(colorControl.value, '#3d5a66', 'Mouse input should have changed color back to original value.');
+    eventSender.keyDown('Escape');
+    assert_equals(internals.pagePopupWindow, null, 'Single escape should change popup since its value was switched back the value when opened');
+  });
+}, "Color picker: Pressing escape closes popup after value is changed and then set back to original value.");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-slider.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-slider.html
new file mode 100644
index 0000000..02f6f903
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation-slider.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<script src='../../../fast/forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color' value='#000000'>
+<script>
+'use strict';
+
+promise_test(() => {
+  let colorControl = document.getElementById('color');
+  return openPickerWithPromise(colorControl)
+  .then(() => {
+    internals.pagePopupWindow.focus();
+    const popupDocument = internals.pagePopupWindow.document;
+    const hueSlider = popupDocument.querySelector('hue-slider');
+    const hueSliderRect = hueSlider.getBoundingClientRect();
+    eventSender.mouseMoveTo(hueSliderRect.left, hueSliderRect.top);
+    eventSender.mouseDown();
+    eventSender.mouseMoveTo(hueSliderRect.left + (hueSliderRect.width / 2), hueSliderRect.top);
+    eventSender.mouseUp();
+    assert_equals(colorControl.value, '#000000', 'Hue slider should not have changed value when starting from #000000.');
+
+    eventSender.keyDown('Escape');
+    assert_equals(internals.pagePopupWindow, null, 'Single escape should close popup if hue slider but not color value changed');
+  });
+}, "Color picker: Hue slider change with no color value change doesn't affect escape behavior");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation.html
deleted file mode 100644
index 85543c53..0000000
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-escape-cancellation.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<script src='../../../resources/testharness.js'></script>
-<script src='../../../resources/testharnessreport.js'></script>
-<script src='../../../fast/forms/resources/picker-common.js'></script>
-</head>
-<body>
-<input type='color' id='color' value='#80d9ff'>
-<script>
-'use strict';
-
-let t = async_test('Color picker: Pressing escape to discard color selection.');
-t.step(() => {
-  let colorControl = document.getElementById('color');
-  openPicker(colorControl, t.step_func(() => {
-    popupWindow.focus();
-    const popupDocument = popupWindow.document;
-    const colorWell = popupDocument.querySelector('color-well');
-    const colorWellRect = colorWell.getBoundingClientRect();
-    eventSender.mouseMoveTo(colorWellRect.left, colorWellRect.top);
-    eventSender.mouseDown();
-    eventSender.mouseMoveTo(colorWellRect.left + (colorWellRect.width * 4 / 10), colorWellRect.top + (colorWellRect.height * 6 / 10));
-    eventSender.mouseUp();
-    eventSender.keyDown('Escape');
-    waitUntilClosing(t.step_func_done(() => {
-      assert_equals(colorControl.value, '#80d9ff', 'Color control value should remain the same after pressing escape.');
-    }), popupWindow.eval('ColorPicker').COMMIT_DELAY_MS);
-  }), t.step_func_done(() => {
-    assert_true(true, 'Popup did not open.');
-  }));
-});
-</script>
-</body>
-</html>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-events-change-revert.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-events-change-revert.html
new file mode 100644
index 0000000..57577f0
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-events-change-revert.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<script src='../../../fast/forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color1' value='#000000' oninput='inputEventCount++;' onchange='changeEventCount++;'>
+<script>
+'use strict';
+
+var inputEventCount = 0;
+var changeEventCount = 0;
+
+promise_test(() => {
+  inputEventCount = 0;
+  changeEventCount = 0;
+  let colorControl = document.getElementById('color1');
+  colorControl.value = '#000000';
+  return openPickerWithPromise(colorControl)
+  .then(() => {
+    internals.pagePopupWindow.focus();
+    const popupDocument = internals.pagePopupWindow.document;
+    const rValueContainer = popupDocument.getElementById('rValueContainer');
+    rValueContainer.focus();
+
+    assert_equals(rValueContainer.value, '0');
+    eventSender.keyDown('1');
+    assert_equals(rValueContainer.value, '01');
+    assert_equals(inputEventCount, 1, 'Input event should have fired after color value change.');
+    assert_equals(changeEventCount, 0, 'No change event should have fired before popup is closed.');
+
+    eventSender.keyDown('Escape');
+    assert_equals(rValueContainer.value, '0');
+    assert_equals(inputEventCount, 2, 'Additional input event should have fired after color value is reset.');
+    assert_equals(changeEventCount, 0, 'No change event should have fired before popup is closed.');
+
+    eventSender.keyDown('Enter');
+    assert_equals(internals.pagePopupWindow, null, "Popup should have closed from Enter key");
+    assert_equals(inputEventCount, 2, 'No extra input event should fire when closing popup.');
+    return new Promise((resolve) => {
+      window.setTimeout(() => {
+        assert_equals(changeEventCount, 0, 'Change event should not fire if value when opening popup is restored.');
+        resolve();
+      }, 0)
+    });
+  });
+}, "Color picker: Test that change is not fired if original value is restored");
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-events-change.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-events-change.html
new file mode 100644
index 0000000..59bdf17
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-events-change.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<script src='../../../fast/forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color1' value='#000000' oninput='inputEventCount++;' onchange='changeEventCount++;'>
+<script>
+'use strict';
+
+var inputEventCount = 0;
+var changeEventCount = 0;
+
+promise_test(() => {
+  inputEventCount = 0;
+  changeEventCount = 0;
+  let colorControl = document.getElementById('color1');
+  return openPickerWithPromise(colorControl)
+  .then(() => {
+    internals.pagePopupWindow.focus();
+    const popupDocument = internals.pagePopupWindow.document;
+    const rValueContainer = popupDocument.getElementById('rValueContainer');
+    rValueContainer.focus();
+
+    assert_equals(rValueContainer.value, '0');
+    eventSender.keyDown('1');
+    assert_equals(rValueContainer.value, '01');
+    assert_equals(inputEventCount, 1, 'Input event should have fired after color value change.');
+    assert_equals(changeEventCount, 0, 'No change event should have fired before popup is closed.');
+
+    eventSender.keyDown('2');
+    assert_equals(rValueContainer.value, '012');
+    assert_equals(inputEventCount, 2, 'Additional input event should have fired after additional color value change.');
+    assert_equals(changeEventCount, 0, 'No change event should have fired before popup is closed.');
+
+    eventSender.keyDown('Enter');
+    assert_equals(internals.pagePopupWindow, null, "Popup should have closed from Enter key");
+    assert_equals(inputEventCount, 2, 'No extra input event should fire when closing popup.');
+    assert_equals(changeEventCount, 0, 'Change event is fired asynchronously after closing popup.');
+    return new Promise((resolve) => {
+      window.setTimeout(() => {
+        assert_equals(changeEventCount, 1, 'Change event should have fired (once) asynchronously after closing popup.');
+        resolve();
+      }, 0)
+    });
+  });
+}, "Color picker: Test firing change and input events");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-events-nochange.html b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-events-nochange.html
new file mode 100644
index 0000000..05bf94d
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-events-nochange.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<script src='../../../fast/forms/resources/picker-common.js'></script>
+</head>
+<body>
+<input type='color' id='color1' value='#000000' oninput='inputEventCount++;' onchange='changeEventCount++;'>
+<script>
+'use strict';
+
+var inputEventCount = 0;
+var changeEventCount = 0;
+
+promise_test(() => {
+  let colorControl = document.getElementById('color1');
+  return openPickerWithPromise(colorControl)
+  .then(() => {
+    internals.pagePopupWindow.focus();
+    eventSender.keyDown('Enter');
+    assert_equals(inputEventCount, 0, 'No input event should have fired if color value was not changed.');
+    return new Promise((resolve) => {
+      window.setTimeout(() => {
+        assert_equals(changeEventCount, 0, 'No change event should have fired if color value was not changed.');
+        resolve();
+      }, 0)
+    });
+  });
+}, "Color picker: Opening and closing popup with no changes should not fire input/change events");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-tab-navigation-expected.txt b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-tab-navigation-expected.txt
index 4fe22dc..ebad46c 100644
--- a/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-tab-navigation-expected.txt
+++ b/third_party/blink/web_tests/virtual/controls-refresh/color/color-picker-tab-navigation-expected.txt
@@ -5,11 +5,7 @@
 GVALUECONTAINER
 BVALUECONTAINER
 FORMAT-TOGGLER
-SUBMISSION-BUTTON
-SUBMISSION-BUTTON
 FORMAT-TOGGLER clicked. Active color format changed.
-SUBMISSION-BUTTON
-SUBMISSION-BUTTON
 COLOR-SELECTION-RING
 COLOR-SELECTION-RING
 HVALUECONTAINER
@@ -17,8 +13,6 @@
 LVALUECONTAINER
 FORMAT-TOGGLER
 FORMAT-TOGGLER clicked. Active color format changed.
-SUBMISSION-BUTTON
-SUBMISSION-BUTTON
 COLOR-SELECTION-RING
 COLOR-SELECTION-RING
 HEXVALUECONTAINER
diff --git a/tools/grit/repack.gni b/tools/grit/repack.gni
index 268edc83..ef06311 100644
--- a/tools/grit/repack.gni
+++ b/tools/grit/repack.gni
@@ -54,12 +54,8 @@
     script = "//tools/grit/pak_util.py"
 
     inputs = invoker.sources
-    foreach(_invoker_source, invoker.sources) {
-      inputs += [ "${_invoker_source}.info" ]
-    }
     outputs = [
       invoker.output,
-      "${invoker.output}.info",
     ]
 
     args = [ "repack" ]
@@ -194,5 +190,8 @@
                              "testonly",
                            ])
     public_deps = _locale_targets
+    if (!defined(invoker.copy_data_to_bundle) || !invoker.copy_data_to_bundle) {
+      data_deps = public_deps
+    }
   }
 }
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 1241f52..83344f8 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1230,7 +1230,6 @@
       # TODO(https://crbug.com/912946): Remove this if statement.
       if ((is_msan and f == 'instrumented_libraries_prebuilt/') or
           f == 'mr_extension/' or # https://crbug.com/997947
-          f == 'locales/' or
           f.startswith('nacl_test_data/') or
           f.startswith('ppapi_nacl_tests_libs/') or
           (is_cros and f in (  # https://crbug.com/1002509
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index a9aeab3..1b82a685 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -536,7 +536,10 @@
       'Android N5 Swarm': 'android_release_bot_minimal_symbols',
       'Android N5X Swarm': 'android_release_bot_minimal_symbols_arm64',
       'Linux Swarm': 'release_bot_minimal_symbols',
-      'Mac Swarm': 'release_bot_mac_strip_minimal_symbols',
+
+      # Use the same config with 'Mac Builder' to pass test for stack trace.
+      'Mac Swarm': 'gpu_tests_release_bot_minimal_symbols',
+
       'Windows Swarm': 'release_bot_minimal_symbols',
     },
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 51a47fa..1798727 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -47619,11 +47619,17 @@
 </enum>
 
 <enum name="OSXFastUserSwitchEvent">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <int value="0" label="User Became Active"/>
   <int value="1" label="User Became Inactive"/>
 </enum>
 
 <enum name="OSXFilesystem">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <int value="0" label="Unknown"/>
   <int value="1" label="Other"/>
   <int value="2" label="Apple Xsan"/>
@@ -47746,6 +47752,9 @@
 </enum>
 
 <enum name="OSXOtherInstancesCheckResult">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <int value="0" label="Old-style update"/>
   <int value="1" label="No other instances at start"/>
   <int value="2" label="User did not authorize"/>
@@ -47787,6 +47796,9 @@
 </enum>
 
 <enum name="OSXStagingDirectoryStep">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <int value="0" label="failure"/>
   <int value="1" label="NSItemReplacementDirectory"/>
   <int value="2" label="Sibling directory"/>
@@ -47797,6 +47809,9 @@
 </enum>
 
 <enum name="OSXStartupUpdateState">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <int value="0" label="Update key not set"/>
   <int value="1" label="Update key set, staged copy present"/>
   <int value="2" label="Update key set, staged copy not present"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c084c63..5df8935 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -15637,7 +15637,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.2DLayerBridgeIsDeferred" enum="BooleanSuccess"
-    expires_after="2019-12-31">
+    expires_after="2021-01-31">
   <obsolete>
     Deprecated as the deferral path is always used now - 11/2019.
   </obsolete>
@@ -15650,7 +15650,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.2DPrintingAsVector" enum="BooleanSuccess"
-    expires_after="2020-12-31">
+    expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
   <owner>juanmihd@chromium.org</owner>
   <summary>
@@ -15659,29 +15659,29 @@
 </histogram>
 
 <histogram name="Blink.Canvas.ContextType" enum="CanvasContextType"
-    expires_after="2020-01-20">
+    expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Records the context type names used to create canvas rendering contexts.
   </summary>
 </histogram>
 
 <histogram name="Blink.Canvas.CreateImageBitmapSource"
-    enum="CanvasCreateImageBitmapSource" expires_after="2019-12-31">
+    enum="CanvasCreateImageBitmapSource" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     The source from which an ImageBitmap is created by a createImageBitmap call.
   </summary>
 </histogram>
 
 <histogram name="Blink.Canvas.DrawImage" units="microseconds"
-    expires_after="2018-10-26">
+    expires_after="2021-01-31">
   <obsolete>
     Replaced with Blink.Canvas.DrawImage.Duration in 10/2018.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <summary>
     Time spent on 2D canvas drawImage API call.
 
@@ -15694,9 +15694,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.DrawImage.Duration"
-    units="microseconds" expires_after="M81">
+    units="microseconds" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Time spent on the main thread during a 2D canvas drawImage API call.
 
@@ -15707,9 +15707,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.DrawImage.SqrtNumberOfPixels"
-    units="sqrt(pixels)" expires_after="2019-12-31">
+    units="sqrt(pixels)" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Stores the square root of the number of pixels drawn into a Canvas.
     Different histograms per CanvasImageSource.
@@ -15721,7 +15721,7 @@
   <obsolete>
     Replaced with Blink.Canvas.GetImageDataScaledDuration in 10/2018.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <summary>
     Time spent on 2D canvas getImageData API call.
 
@@ -15734,9 +15734,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.GetImageDataScaledDuration"
-    units="microseconds * 10/sqrt(pixels)" expires_after="2019-12-31">
+    units="microseconds * 10/sqrt(pixels)" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Time in microseconds * 10 (10e-5 seconds) spent on the 2D canvas
     getImageData API call, divided by the square root of the total number of
@@ -15755,7 +15755,7 @@
     Canvas doesn't disable deferral anymore since July 2019.
   </obsolete>
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     The reasons why a GPU accelerated canvas stopped deferring its rendering
     operations.
@@ -15763,7 +15763,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.HasRendered" enum="Boolean"
-    expires_after="2020-10-24">
+    expires_after="2021-01-31">
   <owner>juanmihd@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -15772,7 +15772,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.HibernationEvents" enum="CanvasHibernationEvent"
-    expires_after="2019-12-31">
+    expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
   <summary>
     Records the occurrence of events related to 2D canvas GPU resource
@@ -15781,7 +15781,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.IsComposited" enum="BooleanSuccess"
-    expires_after="2020-10-24">
+    expires_after="2021-01-31">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -15791,7 +15791,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.NumCanvasesPerPage" units="canvases"
-    expires_after="2020-8-30">
+    expires_after="2021-01-31">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -15804,7 +15804,7 @@
   <obsolete>
     Deprecated in 10/2018. Offscreen Canvas no longer needs commits.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <owner>xidachen@chromium.org</owner>
   <summary>
     Wall clock durations of OffscreenCanvas.commit() calls.
@@ -15822,7 +15822,7 @@
   <obsolete>
     Replaced with Blink.Canvas.PutImageDataScaledDuration in 10/2018.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <summary>
     Time spent on 2D canvas putImageData API call.
 
@@ -15835,9 +15835,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.PutImageDataScaledDuration"
-    units="microseconds * 10/sqrt(pixels)" expires_after="2019-12-31">
+    units="microseconds * 10/sqrt(pixels)" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Time in microseconds * 10 (10e-5 seconds) spent on the 2D canvas
     putImageData API call, divided by the square root of the total number of
@@ -15850,7 +15850,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.RasterDuration" units="microseconds"
-    expires_after="2020-07-01">
+    expires_after="2021-01-31">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -15861,9 +15861,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.RequestedImageMimeTypes"
-    enum="RequestedImageMimeType" expires_after="2019-12-31">
+    enum="RequestedImageMimeType" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Records the occurence of image file formats passed into toDataURL and toBlob
     functions in canvas.
@@ -15871,21 +15871,21 @@
 </histogram>
 
 <histogram name="Blink.Canvas.ResourceProviderIsAccelerated"
-    enum="BooleanHardwareAccelerated" expires_after="2022-8-30">
+    enum="BooleanHardwareAccelerated" expires_after="2021-01-31">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>Logs if the canvas resource provider is accelerated or not.</summary>
 </histogram>
 
 <histogram name="Blink.Canvas.ResourceProviderType"
-    enum="CanvasResourceProviderType" expires_after="2022-8-30">
+    enum="CanvasResourceProviderType" expires_after="2021-01-31">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>Records the type of resource provider used for a canvas.</summary>
 </histogram>
 
 <histogram name="Blink.Canvas.ResourceProviderUsage"
-    enum="CanvasResourceProviderUsage" expires_after="2022-8-30">
+    enum="CanvasResourceProviderUsage" expires_after="2021-01-31">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -15894,7 +15894,7 @@
 </histogram>
 
 <histogram name="Blink.Canvas.SqrtNumberOfPixels" units="sqrt(pixels)"
-    expires_after="2020-8-30">
+    expires_after="2021-01-31">
   <owner>aaronhk@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -15904,9 +15904,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.ToBlob.CompleteEncodingDelay"
-    units="microseconds" expires_after="M81">
+    units="microseconds" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     This metric measures the total time spent on completing encoding all the
     rows of an image (jpeg or png), as part of a canvas.toBlob API call.
@@ -15936,8 +15936,8 @@
   <obsolete>
     Replaced with Blink.Canvas.ToBlob.CompleteEncodingDelay in 2017/12.
   </obsolete>
-  <owner>junov@chromium.org</owner>
-  <owner>xlai@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     This metric measures the total time spent on encoding all the rows of an
     image (jpeg or png), excluding the waiting time of next idle periods. This
@@ -15947,9 +15947,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.ToBlob.IdleTaskStatus"
-    enum="IdleTaskStatus" expires_after="M78">
+    enum="IdleTaskStatus" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Records the status of the idle task when finishing a toBlob call.
 
@@ -15961,9 +15961,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.ToBlob.InitiateEncodingDelay"
-    units="microseconds" expires_after="M81">
+    units="microseconds" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     This metric measures the time spent from initiating image encoding (jpeg or
     png) on idle task to the actual execution time of initiation, as part of a
@@ -15986,9 +15986,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.ToBlob.ScaledDuration"
-    units="microseconds/sqrt(pixels)" expires_after="2019-12-31">
+    units="microseconds/sqrt(pixels)" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Time in microseconds spent on the 2D canvas toBlob API call, divided by the
     square root of the total number of pixels of the canvas.
@@ -16009,8 +16009,8 @@
   <obsolete>
     Replaced with Blink.Canvas.ToBlob.ScaledDuration in 10/2018.
   </obsolete>
-  <owner>junov@chromium.org</owner>
-  <owner>xlai@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Time spent on 2D canvas toBlob API call.
 
@@ -16032,7 +16032,7 @@
   <obsolete>
     Replaced with Blink.Canvas.ToDataURLScaledDuration in 10/2018.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <summary>
     Time spent on 2D canvas toDataURL API call.
 
@@ -16045,9 +16045,9 @@
 </histogram>
 
 <histogram base="true" name="Blink.Canvas.ToDataURLScaledDuration"
-    units="microseconds/sqrt(pixels)" expires_after="2019-12-31">
+    units="microseconds/sqrt(pixels)" expires_after="2021-01-31">
   <owner>fserb@chromium.org</owner>
-  <owner>davidqu@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Time spent on 2D canvas toDataURL API call divided by the square root of the
     total number of pixels of the image. Smaller is faster.
@@ -20842,7 +20842,7 @@
   <obsolete>
     Replaced with Blink.Canvas.ContextType in 10/2018.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <owner>kbr@chromium.org</owner>
   <summary>
     Records the context type names used to create canvas rendering contexts.
@@ -20854,7 +20854,7 @@
   <obsolete>
     Replaced with Blink.Canvas.CreateImageBitmapSource in 10/2018.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <owner>zakerinasab@chromium.org</owner>
   <summary>
     The source from which an ImageBitmap is created by a createImageBitmap call.
@@ -20866,7 +20866,7 @@
   <obsolete>
     Deprecated 11/2017 with removal of Display List Canvas 2D mode.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <summary>
     The reasons why a canvas initially set to display list mode had to fall back
     to direct rasterization mode.
@@ -20880,7 +20880,7 @@
     Replaced with Blink.Canvas.GPUAccelerated2DCanvasDisableDeferralReason in
     10/2018.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <summary>
     The reasons why a GPU accelerated canvas stopped deferring its rendering
     operations.
@@ -20892,7 +20892,8 @@
   <obsolete>
     Replaced with Blink.Canvas.HibernationEvents in 10/2018.
   </obsolete>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Records the occurrence of events related to 2D canvas GPU resource
     hibernation.
@@ -20905,7 +20906,7 @@
     Deprecated 10/2018 with Blink.OffscreenCanvas histograms
   </obsolete>
   <owner>xidachen@chromium.org</owner>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <summary>
     The type of code path that OffscreenCanvas's commit API goes through.
   </summary>
@@ -20916,7 +20917,7 @@
   <obsolete>
     Replaced with Blink.Canvas.RequestedImageMimeTypes in 10/2018.
   </obsolete>
-  <owner>xlai@chromium.org</owner>
+  <owner>aaronhk@chromium.org</owner>
   <summary>
     Records the occurence of image file formats passed into toDataURL and toBlob
     functions in canvas.
@@ -41443,6 +41444,17 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Enterprise.MachineLevelUserCloudPolicyEnrollment.UnenrollSuccess"
+    enum="BooleanSuccess" expires_after="2020-10-01">
+  <owner>domfc@chromium.org</owner>
+  <owner>zmin@chromium.org</owner>
+  <summary>
+    Records whether a browser unenrollment was completed succcessfully by
+    writing an invalid DM token to storage or not.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.ONC.PolicyValidation" enum="BooleanSuccess">
   <owner>mnissler@chromium.org</owner>
   <summary>Result of the OpenNetworkConfiguration policy validation.</summary>
@@ -56290,7 +56302,10 @@
 </histogram>
 
 <histogram name="GPU.GPUInitializationTime" units="ms"
-    expires_after="2020-07-30">
+    expires_after="2020-01-08">
+  <obsolete>
+    Deprecated 01/2020. Moved to GPU.GPUInitializationTime.V2.
+  </obsolete>
   <owner>magchen@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
   <summary>
@@ -56300,6 +56315,17 @@
   </summary>
 </histogram>
 
+<histogram name="GPU.GPUInitializationTime.V2" units="ms"
+    expires_after="2021-01-08">
+  <owner>magchen@chromium.org</owner>
+  <owner>zmo@chromium.org</owner>
+  <summary>
+    The time between the GPU process starts and the GPU Info is collected at GPU
+    process startup and recorded in browser process when this piece info is sent
+    back to browser process. The range is between 200ms and 5s.
+  </summary>
+</histogram>
+
 <histogram name="GPU.GPUProcessDetailedExitStatus"
     enum="ProcessDetailedExitStatus" expires_after="2020-05-03">
   <owner>wnwen@chromium.org</owner>
@@ -100462,7 +100488,7 @@
 </histogram>
 
 <histogram name="OffscreenCanvas.TextMetrics.SetFont" units="microseconds"
-    expires_after="M90">
+    expires_after="2021-01-31">
   <owner>yiyix@chromium.org</owner>
   <owner>fserb@chromium.org</owner>
   <summary>
@@ -102763,6 +102789,9 @@
 
 <histogram name="OSX.FastUserSwitch" enum="OSXFastUserSwitchEvent"
     expires_after="2019-12-31">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <owner>avi@chromium.org</owner>
   <owner>rsesek@chromium.org</owner>
   <owner>mark@chromium.org</owner>
@@ -102839,6 +102868,9 @@
 
 <histogram name="OSX.InstallationFilesystem" enum="OSXFilesystem"
     expires_after="2019-12-31">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <owner>avi@chromium.org</owner>
   <owner>rsesek@chromium.org</owner>
   <owner>mark@chromium.org</owner>
@@ -102893,6 +102925,9 @@
 
 <histogram base="true" name="OSX.OtherInstances" units="units"
     expires_after="2019-12-31">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <owner>avi@chromium.org</owner>
   <owner>rsesek@chromium.org</owner>
   <owner>mark@chromium.org</owner>
@@ -102903,6 +102938,9 @@
 
 <histogram name="OSX.OtherInstancesCheckResult"
     enum="OSXOtherInstancesCheckResult" expires_after="2019-12-31">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <owner>avi@chromium.org</owner>
   <owner>rsesek@chromium.org</owner>
   <owner>mark@chromium.org</owner>
@@ -102959,6 +102997,9 @@
 
 <histogram name="OSX.StagingDirectoryLocation2" enum="OSXStagingDirectoryStep"
     expires_after="2019-12-31">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <owner>avi@chromium.org</owner>
   <owner>rsesek@chromium.org</owner>
   <owner>mark@chromium.org</owner>
@@ -102970,6 +103011,9 @@
 
 <histogram name="OSX.StartupUpdateState" enum="OSXStartupUpdateState"
     expires_after="2019-12-31">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <owner>avi@chromium.org</owner>
   <owner>rsesek@chromium.org</owner>
   <owner>mark@chromium.org</owner>
@@ -106374,6 +106418,10 @@
 
     Do not modify this metric in any way without contacting
     speed-metrics-dev@chromium.org AND chrome-analysis-team@google.com.
+
+    WARNING: The long tail of this metric is buggy on Mac (overflows for a
+    subset of users) which can lead to surprising false regressions or
+    improvements.
   </summary>
 </histogram>
 
@@ -132688,15 +132736,21 @@
 </histogram>
 
 <histogram name="SBIRS.PSSDataStoreSize" units="bytes" expires_after="M81">
+  <obsolete>
+    Deprecated Jan 2020.
+  </obsolete>
   <owner>grt@google.com</owner>
   <summary>
-    The size, in bytes, of a profile's platform state store. This hisogram is
+    The size, in bytes, of a profile's platform state store. This histogram is
     logged on each write, which always replaces any previous contents.
   </summary>
 </histogram>
 
 <histogram name="SBIRS.PSSLoadResult" enum="PlatformStateStoreLoadResult"
     expires_after="2020-07-06">
+  <obsolete>
+    Deprecated Jan 2020.
+  </obsolete>
   <owner>grt@google.com</owner>
   <summary>The result of loading data from the platform state store.</summary>
 </histogram>
@@ -148236,13 +148290,17 @@
 </histogram>
 
 <histogram name="Startup.BrowserMessageLoopStartHardFaultCount" units="faults"
-    expires_after="2020-07-06">
+    expires_after="never">
+<!-- expires-never: calibration metric for StartupTemperature, see
+     kColdStartHardFaultCountThreshold and WarmStartHardFaultCountThreshold in
+     startup_metric_utils.cc for details -->
+
   <owner>chrisha@chromium.org</owner>
   <owner>fdoray@chromium.org</owner>
   <summary>
     The number of hard faults incurred in the browser process from startup to
     start of the main thread's message loop, not including first runs of the
-    browser. This is only reported on Windows 7 and greater.
+    browser.
   </summary>
 </histogram>
 
@@ -148549,7 +148607,10 @@
 </histogram>
 
 <histogram name="Startup.FirstWebContents.FinishReason"
-    enum="StartupProfilingFinishReason" expires_after="2020-06-28">
+    enum="StartupProfilingFinishReason" expires_after="never">
+<!-- expires-never: used to understand user behavior shifts when Startup.FirstWebContents.NonEmptyPaint2 regresses -->
+
+  <owner>fdoray@chromium.org</owner>
   <owner>gab@chromium.org</owner>
   <summary>
     [Desktop] The reason for which startup profiling was deemed complete. Logged
@@ -148600,7 +148661,10 @@
 </histogram>
 
 <histogram name="Startup.FirstWebContents.MainNavigationStart" units="ms"
-    expires_after="2020-06-28">
+    expires_after="never">
+<!-- expires-never: used to diagnose regressions to Startup.FirstWebContents.NonEmptyPaint2 -->
+
+  <owner>fdoray@chromium.org</owner>
   <owner>gab@chromium.org</owner>
   <summary>
     [Desktop] Measure the elapsed time from process launch to the beginning of
@@ -149144,13 +149208,18 @@
 </histogram>
 
 <histogram name="Startup.Temperature" enum="StartupTemperature"
-    expires_after="2020-07-06">
+    expires_after="2021-01-31">
+<!-- expires-after: Diagnosis metric for changes in StartupTemperature suffix.
+     Shouldn't truly expire but kColdStartHardFaultCountThreshold should be
+     surveyed yearly. -->
+
   <owner>chrisha@chromium.org</owner>
   <owner>fdoray@chromium.org</owner>
   <summary>
     Indicates whether or not the given startup was warm, cold or unable to be
     determined. This is based off observing the number of hard faults that occur
-    during startup prior to Startup.BrowserMessageLoopStartTime.
+    during startup prior to Startup.BrowserMessageLoopStartTime. The threshold
+    for cold startup was updated Jan 2020, a bump in the metric is expected.
   </summary>
 </histogram>
 
@@ -166421,7 +166490,7 @@
     Blink.Canvas.ResourceProviderType and Blink.Canvas.2DLayerBridgeIsDeferred
   </obsolete>
   <owner>zmin@chromium.org</owner>
-  <owner>junov@chromium.org</owner>
+  <owner>fserb@chromium.org</owner>
   <summary>
     The usage of Canvas 2D Context API. Logged when the particular API is used.
   </summary>
@@ -168085,8 +168154,8 @@
 
 <histogram base="true" name="WebFont.HttpCacheStatus" enum="HttpCachePattern"
     expires_after="2020-07-06">
-  <owner>kenjibaheux@chromium.org</owner>
-  <owner>ksakamoto@chromium.org</owner>
+  <owner>yaoxia@chromium.org</owner>
+  <owner>shivanisha@chromium.org</owner>
   <summary>
     For each http cache transaction for a font in Google Fonts, record the cache
     status.
@@ -169868,7 +169937,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpComplexUsage.CreateAnswer"
-    enum="PeerConnectionSdpUsageCategory" expires_after="2020-04-19">
+    enum="PeerConnectionSdpUsageCategory" expires_after="2020-12-31">
   <owner>hbos@chromium.org</owner>
   <summary>
     The SDP usage category (&quot;safe&quot;, &quot;unsafe&quot; or
@@ -169879,7 +169948,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpComplexUsage.CreateOffer"
-    enum="PeerConnectionSdpUsageCategory" expires_after="2020-04-26">
+    enum="PeerConnectionSdpUsageCategory" expires_after="2020-12-31">
   <owner>hbos@chromium.org</owner>
   <summary>
     The SDP usage category (&quot;safe&quot;, &quot;unsafe&quot; or
@@ -169890,7 +169959,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpComplexUsage.SetLocalAnswer"
-    enum="PeerConnectionSdpUsageCategory" expires_after="M81">
+    enum="PeerConnectionSdpUsageCategory" expires_after="2020-12-31">
   <owner>hbos@chromium.org</owner>
   <summary>
     The SDP usage category (&quot;safe&quot;, &quot;unsafe&quot; or
@@ -169902,7 +169971,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpComplexUsage.SetLocalOffer"
-    enum="PeerConnectionSdpUsageCategory" expires_after="2020-02-23">
+    enum="PeerConnectionSdpUsageCategory" expires_after="2020-12-31">
   <owner>hbos@chromium.org</owner>
   <summary>
     The SDP usage category (&quot;safe&quot;, &quot;unsafe&quot; or
@@ -169914,7 +169983,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpComplexUsage.SetRemoteAnswer"
-    enum="PeerConnectionSdpUsageCategory" expires_after="M81">
+    enum="PeerConnectionSdpUsageCategory" expires_after="2020-12-31">
   <owner>hbos@chromium.org</owner>
   <summary>
     The SDP usage category (&quot;safe&quot;, &quot;unsafe&quot; or
@@ -169926,7 +169995,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpComplexUsage.SetRemoteOffer"
-    enum="PeerConnectionSdpUsageCategory" expires_after="M81">
+    enum="PeerConnectionSdpUsageCategory" expires_after="2020-12-31">
   <owner>hbos@chromium.org</owner>
   <summary>
     The SDP usage category (&quot;safe&quot;, &quot;unsafe&quot; or
@@ -169938,7 +170007,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpFormatReceived"
-    enum="PeerConnectionSdpFormatReceived" expires_after="2020-06-28">
+    enum="PeerConnectionSdpFormatReceived" expires_after="2020-12-31">
   <owner>steveanton@chromium.org</owner>
   <summary>
     What SDP format is received in the remote offer. The value &quot;no
@@ -169952,7 +170021,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpSemanticNegotiated"
-    enum="PeerConnectionSdpSemanticNegotiated" expires_after="2020-07-06">
+    enum="PeerConnectionSdpSemanticNegotiated" expires_after="2020-12-31">
   <owner>hta@chromium.org</owner>
   <summary>
     What SDP semantic (Unified Plan or Plan B) was detected when completing
@@ -169964,7 +170033,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.SdpSemanticRequested"
-    enum="PeerConnectionSdpSemanticRequested" expires_after="2020-06-21">
+    enum="PeerConnectionSdpSemanticRequested" expires_after="2020-12-31">
   <owner>hta@chromium.org</owner>
   <summary>
     What SDP semantic (Unified Plan, Plan B or &quot;use default&quot;) has been
@@ -173484,8 +173553,16 @@
 
 <histogram_suffixes name="AdsPageLoadMetricsCpuInteractivePeriod" separator=".">
   <suffix name="" label="Usage before and after the page is interactive."/>
-  <suffix name="PostInteractive" label="Usage after the page is interactive."/>
-  <suffix name="PreInteractive" label="Usage before the page is interactive."/>
+  <suffix name="PostInteractive" label="Usage after the page is interactive.">
+    <obsolete>
+      Removed 01/2020, did not provide useful information on ad CPU performance.
+    </obsolete>
+  </suffix>
+  <suffix name="PreInteractive" label="Usage before the page is interactive.">
+    <obsolete>
+      Removed 01/2020, did not provide useful information on ad CPU performance.
+    </obsolete>
+  </suffix>
   <affected-histogram
       name="PageLoad.Clients.Ads.Cpu.AdFrames.PerFrame.PercentUsage.Unactivated"/>
   <affected-histogram
@@ -177125,6 +177202,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="DefaultAppsExperiment" separator="_">
+  <obsolete>
+    Marked obsolete 01/2020. Hasn't been recorded for some time.
+  </obsolete>
   <suffix name="NoDefaultApps" label="User's without default apps installed"/>
   <suffix name="WithDefaultApps" label="User's with default apps installed"/>
   <affected-histogram name="Extensions.AppTabLaunchType"/>
@@ -188582,8 +188662,17 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="StartupTemperature" separator=".">
-  <suffix name="ColdStartup" label="Startup was cold (mostly hard faults)."/>
-  <suffix name="LukewarmStartup" label="Startup was neither warm nor cold."/>
+  <suffix name="ColdStartup"
+      label="Startup was cold (mostly hard faults). Threshold updated Jan
+             2020, metrics expected to bump."/>
+  <suffix name="LukewarmStartup" label="Startup was neither warm nor cold.">
+    <obsolete>
+      Obsolete as of Jan 2020. Was a diagnosis metric when warm/cold
+      distributions didn't look as expected but recording the suffix for every
+      startup metric was deemed overkill. Use Startup.Temperature and
+      Startup.BrowserMessageLoopStartHardFaultCount to diagnose discrepancies.
+    </obsolete>
+  </suffix>
   <suffix name="WarmStartup" label="Startup was warm (almost no hard faults)."/>
   <affected-histogram
       name="LibraryLoader.PercentageOfResidentCodeBeforePrefetch"/>
@@ -188608,9 +188697,17 @@
   <affected-histogram name="Startup.FirstWebContents.MainNavigationFinished"/>
   <affected-histogram name="Startup.FirstWebContents.MainNavigationStart"/>
   <affected-histogram
-      name="Startup.FirstWebContents.MainNavigationStart.MultiTabs"/>
+      name="Startup.FirstWebContents.MainNavigationStart.MultiTabs">
+    <obsolete>
+      Obsolete as of Jan 2020.
+    </obsolete>
+  </affected-histogram>
   <affected-histogram
-      name="Startup.FirstWebContents.MainNavigationStart.SingleTab"/>
+      name="Startup.FirstWebContents.MainNavigationStart.SingleTab">
+    <obsolete>
+      Obsolete as of Jan 2020.
+    </obsolete>
+  </affected-histogram>
   <affected-histogram name="Startup.FirstWebContents.NonEmptyPaint"/>
   <affected-histogram name="Startup.FirstWebContents.NonEmptyPaint2"/>
   <affected-histogram
@@ -188639,6 +188736,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="StartupWorkload" separator=".">
+  <obsolete>
+    Deprecated as of Jan 2020.
+  </obsolete>
   <suffix name="MultiTabs" label="Multiple tabs instantiated on startup."/>
   <suffix name="SingleTab" label="Single tab instantiated on startup."/>
   <affected-histogram name="Startup.FirstWebContents.MainNavigationStart"/>
@@ -189521,6 +189621,9 @@
 </histogram_suffixes>
 
 <histogram_suffixes name="ThisOrOtherUser" separator=".">
+  <obsolete>
+    Removed 2020 January.
+  </obsolete>
   <suffix name="OtherUser" label="Having a different EUID."/>
   <suffix name="ThisUser" label="Sharing the same EUID."/>
   <affected-histogram name="OSX.OtherInstances"/>
diff --git a/tools/perf/benchmark.csv b/tools/perf/benchmark.csv
index e4e8d67..1a44345 100644
--- a/tools/perf/benchmark.csv
+++ b/tools/perf/benchmark.csv
@@ -12,7 +12,7 @@
 blink_perf.dom,masonfreed@chromium.org,Blink>DOM,https://bit.ly/blink-perf-benchmarks,
 blink_perf.events,masonfreed@chromium.org,Blink>DOM,https://bit.ly/blink-perf-benchmarks,
 blink_perf.image_decoder,cblume@chromium.org,Internals>Images>Codecs,https://bit.ly/blink-perf-benchmarks,
-blink_perf.layout,eae@chromium.org,Blink>Layout,https://bit.ly/blink-perf-benchmarks,all
+blink_perf.layout,"ikilpatrick@chromium.org, kojii@chromium.org",Blink>Layout,https://bit.ly/blink-perf-benchmarks,all
 blink_perf.owp_storage,dmurph@chromium.org,Blink>Storage,https://bit.ly/blink-perf-benchmarks,
 blink_perf.paint,"pdr@chromium.org, wangxianzhu@chromium.org",Blink>Paint,https://bit.ly/blink-perf-benchmarks,all
 blink_perf.parser,"jbroman@chromium.org, yukishiino@chromium.org, haraken@chromium.org",Blink>Bindings,https://bit.ly/blink-perf-benchmarks,
diff --git a/tools/perf/benchmarks/blink_perf.py b/tools/perf/benchmarks/blink_perf.py
index 63aa2fc..37b493de 100644
--- a/tools/perf/benchmarks/blink_perf.py
+++ b/tools/perf/benchmarks/blink_perf.py
@@ -510,9 +510,10 @@
     ])
 
 
-@benchmark.Info(emails=['eae@chromium.org'],
-                component='Blink>Layout',
-                documentation_url='https://bit.ly/blink-perf-benchmarks')
+@benchmark.Info(
+    emails=['ikilpatrick@chromium.org', 'kojii@chromium.org'],
+    component='Blink>Layout',
+    documentation_url='https://bit.ly/blink-perf-benchmarks')
 class BlinkPerfLayout(_BlinkPerfBenchmark):
   SUBDIR = 'layout'
   TAGS = _BlinkPerfBenchmark.TAGS + ['all']
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config
index 69fefda..7aa8f04 100644
--- a/tools/perf/expectations.config
+++ b/tools/perf/expectations.config
@@ -331,7 +331,7 @@
 crbug.com/1014655 [ android-webview ] system_health.common_mobile/browse:social:instagram:2019 [ Skip ]
 crbug.com/1036141 [ android-webview ] system_health.common_mobile/browse:shopping:lazada:2019 [ Skip ]
 crbug.com/1036143 [ android-pixel-2 ] system_health.common_mobile/browse:chrome:omnibox:2019 [ Skip ]
-crbug.com/1039801 [ android-webview ] system_health.memory_mobile/browse:social:pinterest_infinite_scroll:2019 [ Skip ]
+crbug.com/1039801 [ android-webview ] system_health.common_mobile/browse:social:pinterest_infinite_scroll:2019 [ Skip ]
 
 # Benchmark: system_health.memory_desktop
 crbug.com/984599 [ linux ] system_health.memory_desktop/long_running:tools:gmail-foreground [ Skip ]
@@ -455,7 +455,8 @@
 crbug.com/1036143 [ android-pixel-2 ] v8.browsing_mobile/browse:chrome:omnibox:2019 [ Skip ]
 crbug.com/1037905 [ android-nexus-6 android-webview ] v8.browsing_mobile/browse:media:imgur:2019 [ Skip ]
 crbug.com/1039801 [ android-webview ] v8.browsing_mobile/browse:social:pinterest_infinite_scroll:2019 [ Skip ]
-
+crbug.com/1039295 [ android-go ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
+crbug.com/1039295 [ android-nexus-5 ] v8.browsing_mobile/browse:news:nytimes:2019 [ Skip ]
 
 # Benchmark: v8.browsing_mobile-future
 # Disabled v8.browsing_mobile-future for capacity reasons while we update
diff --git a/tools/perf/scripts_smoke_unittest.py b/tools/perf/scripts_smoke_unittest.py
index 007311aa..1b087db 100644
--- a/tools/perf/scripts_smoke_unittest.py
+++ b/tools/perf/scripts_smoke_unittest.py
@@ -251,7 +251,7 @@
           print fh.read()
       # pylint: disable=bare-except
       except:
-      # pylint: enable=bare-except
+        # pylint: enable=bare-except
         pass
       raise
     try:
@@ -351,16 +351,41 @@
     self.assertIn('--run-abridged-story-set', command)
 
   def testRunPerformanceTestsGtestArgsParser(self):
-     options = run_performance_tests.parse_arguments([
-        'media_perftests', '--non-telemetry=true', '--single-process-tests',
+    options = run_performance_tests.parse_arguments([
+        'media_perftests',
+        '--non-telemetry=true',
+        '--single-process-tests',
         '--test-launcher-retry-limit=0',
         '--isolated-script-test-filter=*::-*_unoptimized::*_unaligned::'
         '*unoptimized_aligned',
-        '--gtest-benchmark-name', 'media_perftests',
+        '--gtest-benchmark-name',
+        'media_perftests',
         '--isolated-script-test-output=/x/y/z/output.json',
-     ])
-     self.assertIn('--single-process-tests', options.passthrough_args)
-     self.assertIn('--test-launcher-retry-limit=0', options.passthrough_args)
-     self.assertEqual(options.executable, 'media_perftests')
-     self.assertEqual(options.isolated_script_test_output,
-                      r'/x/y/z/output.json')
+    ])
+    self.assertIn('--single-process-tests', options.passthrough_args)
+    self.assertIn('--test-launcher-retry-limit=0', options.passthrough_args)
+    self.assertEqual(options.executable, 'media_perftests')
+    self.assertEqual(options.isolated_script_test_output, r'/x/y/z/output.json')
+
+  def testRunPerformanceTestsExecuteGtest_OSError(self):
+    class FakeCommandGenerator(object):
+      def __init__(self):
+        self.executable_name = 'binary_that_doesnt_exist'
+
+      def generate(self, unused_path):
+        return [self.executable_name]
+
+    tempdir = tempfile.mkdtemp()
+    try:
+      fake_command_generator = FakeCommandGenerator()
+      output_paths = run_performance_tests.OutputFilePaths(
+          tempdir, 'fake_gtest')
+      output_paths.SetUp()
+      return_code = run_performance_tests.execute_gtest_perf_test(
+          fake_command_generator, output_paths)
+      self.assertEqual(return_code, 1)
+      with open(output_paths.test_results) as fh:
+        json_test_results = json.load(fh)
+      self.assertGreater(json_test_results['num_failures_by_type']['FAIL'], 0)
+    finally:
+      shutil.rmtree(tempdir)
diff --git a/tools/valgrind/asan/asan_symbolize.py b/tools/valgrind/asan/asan_symbolize.py
index ecf4276..0d1c8803 100755
--- a/tools/valgrind/asan/asan_symbolize.py
+++ b/tools/valgrind/asan/asan_symbolize.py
@@ -118,6 +118,12 @@
   return chrome_osx_binary_name_filter
 
 
+def make_sysroot_filter(sysroot):
+  """Creates a binary name filter for a symbol tree of a remote system."""
+
+  return lambda binary_path: sysroot + binary_path
+
+
 # Construct a path to the .dSYM bundle for the given binary.
 # There are three possible cases for binary location in Chromium:
 # 1. The binary is a standalone executable or dynamic library in the product
@@ -217,6 +223,7 @@
   parser.add_argument('--executable-path',
       help='Path to program executable. Used on OSX swarming bots to locate '
            'dSYM bundles for associated frameworks and bundles.')
+  parser.add_argument('--sysroot', help='Root directory for symbol files')
   args = parser.parse_args()
 
   disable_buffering()
@@ -227,7 +234,9 @@
   # /path/to/src/out/Release/../../
   asan_symbolize.fix_filename_patterns.append('Release/../../')
   binary_name_filter = None
-  if platform.uname()[0] == 'Darwin':
+  if args.sysroot:
+    binary_name_filter = make_sysroot_filter(args.sysroot)
+  elif platform.uname()[0] == 'Darwin':
     binary_name_filter = make_chrome_osx_binary_name_filter(
         chrome_product_dir_path(args.executable_path))
   loop = asan_symbolize.SymbolizationLoop(
diff --git a/tools/vim/ninja-build.vim b/tools/vim/ninja-build.vim
index df076b5..06141100 100644
--- a/tools/vim/ninja-build.vim
+++ b/tools/vim/ninja-build.vim
@@ -16,7 +16,7 @@
 " Add the following to your .vimrc file:
 "     so /path/to/src/tools/vim/ninja-build.vim
 
-python << endpython
+pythonx << endpython
 import os
 import vim
 
@@ -86,11 +86,11 @@
 endfun
 
 fun! s:NinjaCommandForCurrentBuffer()
-  python compute_ninja_command_for_current_buffer()
+  pythonx compute_ninja_command_for_current_buffer()
 endfun
 
 fun! s:NinjaCommandForTargets(targets)
-  python compute_ninja_command_for_targets(vim.eval('a:targets'))
+  pythonx compute_ninja_command_for_targets(vim.eval('a:targets'))
 endfun
 
 fun! CrCompileFile()
diff --git a/ui/chromeos/BUILD.gn b/ui/chromeos/BUILD.gn
index 2304b337..15899fa 100644
--- a/ui/chromeos/BUILD.gn
+++ b/ui/chromeos/BUILD.gn
@@ -76,12 +76,13 @@
     "//ui/events:test_support",
     "//ui/gl:test_support",
     "//ui/message_center",
-    "//ui/resources:ui_test_pak",
     "//ui/views",
     "//ui/views:test_support",
   ]
   data = [
     "$root_out_dir/locales/en-US.pak",
-    "$root_out_dir/ui_test.pak",
+  ]
+  data_deps = [
+    "//ui/resources:ui_test_pak_data",
   ]
 }
diff --git a/ui/compositor/overscroll/scroll_input_handler.cc b/ui/compositor/overscroll/scroll_input_handler.cc
index 92f33724..5279c0b 100644
--- a/ui/compositor/overscroll/scroll_input_handler.cc
+++ b/ui/compositor/overscroll/scroll_input_handler.cc
@@ -68,7 +68,8 @@
                                        cc::InputHandler::WHEEL);
 
   cc::ScrollState scroll_state = CreateScrollState(event, false);
-  input_handler_weak_ptr_->ScrollBy(&scroll_state);
+  input_handler_weak_ptr_->ScrollUpdate(&scroll_state, cc::InputHandler::WHEEL,
+                                        base::TimeDelta());
   input_handler_weak_ptr_->ScrollEnd(/*should_snap=*/false);
 
   return true;
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index be1284b..f278b402 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -186,7 +186,6 @@
 #endif
       gesture_scroll_on_impl_thread_(false),
       scroll_sequence_ignored_(false),
-      smooth_scroll_enabled_(false),
       touch_result_(kEventDispositionUndefined),
       mouse_wheel_result_(kEventDispositionUndefined),
       current_overscroll_params_(nullptr),
@@ -746,21 +745,6 @@
   }
 }
 
-bool InputHandlerProxy::ShouldAnimate(blink::WebGestureDevice device,
-                                      bool has_precise_scroll_deltas) const {
-  if (!smooth_scroll_enabled_)
-    return false;
-
-#if defined(OS_MACOSX)
-  // Mac does not smooth scroll wheel events (crbug.com/574283).
-  return device == blink::WebGestureDevice::kScrollbar
-             ? !has_precise_scroll_deltas
-             : false;
-#else
-  return !has_precise_scroll_deltas;
-#endif
-}
-
 InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel(
     const WebMouseWheelEvent& wheel_event) {
   InputHandlerProxy::EventDisposition result = DROP_EVENT;
@@ -837,13 +821,6 @@
   } else if (gesture_event.data.scroll_begin.target_viewport) {
     scroll_status = input_handler_->RootScrollBegin(
         &scroll_state, GestureScrollInputType(gesture_event.SourceDevice()));
-  } else if (ShouldAnimate(
-                 gesture_event.SourceDevice(),
-                 gesture_event.data.scroll_begin.delta_hint_units !=
-                     ui::input_types::ScrollGranularity::kScrollByPixel)) {
-    DCHECK(!scroll_state.is_in_inertial_phase());
-    scroll_status =
-        input_handler_->ScrollBegin(&scroll_state, cc::InputHandler::WHEEL);
   } else {
     scroll_status = input_handler_->ScrollBegin(
         &scroll_state, GestureScrollInputType(gesture_event.SourceDevice()));
@@ -887,15 +864,14 @@
 InputHandlerProxy::EventDisposition
 InputHandlerProxy::HandleGestureScrollUpdate(
     const WebGestureEvent& gesture_event) {
+  TRACE_EVENT2("input", "InputHandlerProxy::HandleGestureScrollUpdate", "dx",
+               -gesture_event.data.scroll_update.delta_x, "dy",
+               -gesture_event.data.scroll_update.delta_y);
+
 #if DCHECK_IS_ON()
   DCHECK(expect_scroll_update_end_);
 #endif
 
-  gfx::Vector2dF scroll_delta(-gesture_event.data.scroll_update.delta_x,
-                              -gesture_event.data.scroll_update.delta_y);
-  TRACE_EVENT2("input", "InputHandlerProxy::HandleGestureScrollUpdate", "dx",
-               scroll_delta.x(), "dy", scroll_delta.y());
-
   if (scroll_sequence_ignored_) {
     TRACE_EVENT_INSTANT0("input", "Scroll Sequence Ignored",
                          TRACE_EVENT_SCOPE_THREAD);
@@ -907,29 +883,10 @@
 
   cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event);
   in_inertial_scrolling_ = scroll_state.is_in_inertial_phase();
-  gfx::PointF scroll_point(gesture_event.PositionInWidget());
 
   TRACE_EVENT_INSTANT1(
       "input", "DeltaUnits", TRACE_EVENT_SCOPE_THREAD, "unit",
       static_cast<int>(gesture_event.data.scroll_update.delta_units));
-  if (ShouldAnimate(gesture_event.SourceDevice(),
-                    gesture_event.data.scroll_update.delta_units !=
-                        ui::input_types::ScrollGranularity::kScrollByPixel)) {
-    if (input_handler_->ScrollingShouldSwitchtoMainThread()) {
-      TRACE_EVENT_INSTANT0("input", "Move Scroll To Main Thread",
-                           TRACE_EVENT_SCOPE_THREAD);
-      gesture_scroll_on_impl_thread_ = false;
-      client_->GenerateScrollBeginAndSendToMainThread(gesture_event);
-      return DID_NOT_HANDLE;
-    } else {
-      DCHECK(!scroll_state.is_in_inertial_phase());
-      base::TimeTicks event_time = gesture_event.TimeStamp();
-      base::TimeDelta delay = base::TimeTicks::Now() - event_time;
-      input_handler_->ScrollAnimated(gfx::ToFlooredPoint(scroll_point),
-                                     scroll_delta, delay);
-      return DID_HANDLE;
-    }
-  }
 
   if (snap_fling_controller_->HandleGestureScrollUpdate(
           GetGestureScrollUpdateInfo(gesture_event))) {
@@ -940,19 +897,30 @@
     return DROP_EVENT;
   }
 
-  cc::InputHandlerScrollResult scroll_result =
-      input_handler_->ScrollBy(&scroll_state);
-
-  if (!scroll_result.did_scroll &&
-      input_handler_->ScrollingShouldSwitchtoMainThread()) {
+  if (input_handler_->ScrollingShouldSwitchtoMainThread()) {
+    TRACE_EVENT_INSTANT0("input", "Move Scroll To Main Thread",
+                         TRACE_EVENT_SCOPE_THREAD);
     gesture_scroll_on_impl_thread_ = false;
     client_->GenerateScrollBeginAndSendToMainThread(gesture_event);
 
+    // TODO(bokan): |!gesture_pinch_in_progress_| was put here by
+    // https://crrev.com/2720903005 but it's not clear to me how this is
+    // supposed to work - we already generated and sent a GSB to the main
+    // thread above so it's odd to continue handling on the compositor thread
+    // if a pinch was in progress. It probably makes more sense to bake this
+    // condition into ScrollingShouldSwitchToMainThread().
     if (!gesture_pinch_in_progress_)
       return DID_NOT_HANDLE;
   }
 
-  HandleOverscroll(scroll_point, scroll_result);
+  base::TimeTicks event_time = gesture_event.TimeStamp();
+  base::TimeDelta delay = base::TimeTicks::Now() - event_time;
+
+  cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollUpdate(
+      &scroll_state, GestureScrollInputType(gesture_event.SourceDevice()),
+      delay);
+
+  HandleOverscroll(gesture_event.PositionInWidget(), scroll_result);
 
   if (scroll_elasticity_controller_)
     HandleScrollElasticityOverscroll(gesture_event, scroll_result);
@@ -1203,8 +1171,11 @@
 gfx::Vector2dF InputHandlerProxy::ScrollByForSnapFling(
     const gfx::Vector2dF& delta) {
   cc::ScrollState scroll_state = CreateScrollStateForInertialUpdate(delta);
-  cc::InputHandlerScrollResult scroll_result =
-      input_handler_->ScrollBy(&scroll_state);
+
+  // TODO(bokan): We should be passing in the source device that was used to
+  // scroll during the gesture.
+  cc::InputHandlerScrollResult scroll_result = input_handler_->ScrollUpdate(
+      &scroll_state, cc::InputHandler::TOUCHSCREEN, base::TimeDelta());
   return scroll_result.current_visual_offset;
 }
 
diff --git a/ui/events/blink/input_handler_proxy.h b/ui/events/blink/input_handler_proxy.h
index 0e867027..25902776 100644
--- a/ui/events/blink/input_handler_proxy.h
+++ b/ui/events/blink/input_handler_proxy.h
@@ -61,8 +61,6 @@
     return scroll_elasticity_controller_.get();
   }
 
-  void set_smooth_scroll_enabled(bool value) { smooth_scroll_enabled_ = value; }
-
   enum EventDisposition {
     DID_HANDLE,
     DID_NOT_HANDLE,
@@ -164,10 +162,6 @@
   void HandleOverscroll(const gfx::PointF& causal_event_viewport_point,
                         const cc::InputHandlerScrollResult& scroll_result);
 
-  // Whether to use a smooth scroll animation for this event.
-  bool ShouldAnimate(blink::WebGestureDevice device,
-                     bool has_precise_scroll_deltas) const;
-
   // Update the elastic overscroll controller with |gesture_event|.
   void HandleScrollElasticityOverscroll(
       const blink::WebGestureEvent& gesture_event,
@@ -204,8 +198,6 @@
   std::unique_ptr<InputScrollElasticityController>
       scroll_elasticity_controller_;
 
-  bool smooth_scroll_enabled_;
-
   // The merged result of the last touch event with previous touch events.
   // This value will get returned for subsequent TouchMove events to allow
   // passive events not to block scrolling.
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index 254924b50..f3b36185 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -115,11 +115,11 @@
   MOCK_METHOD2(RootScrollBegin,
                ScrollStatus(cc::ScrollState*,
                             cc::InputHandler::ScrollInputType type));
-  MOCK_METHOD3(ScrollAnimated,
-               void(const gfx::Point& viewport_point,
-                    const gfx::Vector2dF& scroll_delta,
-                    base::TimeDelta));
-  MOCK_METHOD1(ScrollBy, cc::InputHandlerScrollResult(cc::ScrollState*));
+  MOCK_METHOD3(
+      ScrollUpdate,
+      cc::InputHandlerScrollResult(cc::ScrollState*,
+                                   cc::InputHandler::ScrollInputType type,
+                                   base::TimeDelta));
   MOCK_METHOD1(ScrollEnd, void(bool));
   MOCK_METHOD0(ScrollingShouldSwitchtoMainThread, bool());
 
@@ -370,7 +370,6 @@
   void Animate(base::TimeTicks time) { input_handler_->Animate(time); }
 
   void SetSmoothScrollEnabled(bool value) {
-    input_handler_->smooth_scroll_enabled_ = value;
   }
 
   base::HistogramTester& histogram_tester() { return histogram_tester_; }
@@ -635,7 +634,8 @@
       -40;  // -Y means scroll down - i.e. in the +Y direction.
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_not_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillOnce(testing::Return(false));
@@ -646,12 +646,15 @@
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   VERIFY_AND_RESET_MOCKS();
 
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
   gesture_.SetType(WebInputEvent::kGestureScrollUpdate);
   gesture_.data.scroll_update.delta_y =
       -40;  // -Y means scroll down - i.e. in the +Y direction.
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
   EXPECT_EQ(expected_disposition_,
             input_handler_->RouteToTypeSpecificHandler(gesture_));
@@ -727,72 +730,6 @@
   VERIFY_AND_RESET_MOCKS();
 }
 
-TEST_P(InputHandlerProxyTest, AnimatedScrollbarScroll) {
-  VERIFY_AND_RESET_MOCKS();
-  SetSmoothScrollEnabled(true);
-  expected_disposition_ = InputHandlerProxy::DID_HANDLE;
-  gesture_.SetSourceDevice(blink::WebGestureDevice::kScrollbar);
-  gesture_.data.scroll_begin.delta_hint_units =
-      gesture_.data.scroll_update.delta_units =
-          ui::input_types::ScrollGranularity::kScrollByPixel;
-
-  // Test setup for a kGestureScrollBegin.
-  gesture_.SetType(WebInputEvent::kGestureScrollBegin);
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(_, cc::InputHandler::WHEEL))
-      .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_EQ(expected_disposition_,
-            input_handler_->RouteToTypeSpecificHandler(gesture_));
-  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
-
-  VERIFY_AND_RESET_MOCKS();
-
-  // Test setup for a kGestureScrollUpdate.
-  gesture_.SetType(WebInputEvent::kGestureScrollUpdate);
-  EXPECT_CALL(mock_input_handler_, ScrollAnimated(_, _, _));
-  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
-      .WillOnce(testing::Return(false));
-  EXPECT_EQ(expected_disposition_,
-            input_handler_->RouteToTypeSpecificHandler(gesture_));
-  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
-
-  VERIFY_AND_RESET_MOCKS();
-}
-
-TEST_P(InputHandlerProxyTest, NonAnimatedScrollbarScroll) {
-  VERIFY_AND_RESET_MOCKS();
-  SetSmoothScrollEnabled(false);
-  expected_disposition_ = InputHandlerProxy::DID_HANDLE;
-  gesture_.SetSourceDevice(blink::WebGestureDevice::kScrollbar);
-  gesture_.data.scroll_begin.delta_hint_units =
-      gesture_.data.scroll_update.delta_units =
-          ui::input_types::ScrollGranularity::kScrollByPixel;
-
-  // Test setup for a kGestureScrollBegin.
-  gesture_.SetType(WebInputEvent::kGestureScrollBegin);
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
-      .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_EQ(expected_disposition_,
-            input_handler_->RouteToTypeSpecificHandler(gesture_));
-  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
-
-  VERIFY_AND_RESET_MOCKS();
-
-  // Test setup for a kGestureScrollUpdate.
-  gesture_.SetType(WebInputEvent::kGestureScrollUpdate);
-  gesture_.data.scroll_update.delta_y =
-      -40;  // -ve value implies "scrolling down".
-  EXPECT_CALL(
-      mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
-      .WillOnce(testing::Return(scroll_result_did_scroll_));
-  EXPECT_CALL(mock_input_handler_, ScrollAnimated(_, _, _)).Times(0);
-  EXPECT_EQ(expected_disposition_,
-            input_handler_->RouteToTypeSpecificHandler(gesture_));
-  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
-
-  VERIFY_AND_RESET_MOCKS();
-}
-
 TEST_P(InputHandlerProxyTest, GestureScrollByPage) {
   // We should send all events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
@@ -823,36 +760,6 @@
   VERIFY_AND_RESET_MOCKS();
 }
 
-// Mac does not smooth scroll wheel events (crbug.com/574283).
-#if !defined(OS_MACOSX)
-TEST_P(InputHandlerProxyTest, GestureScrollByCoarsePixels) {
-#else
-TEST_P(InputHandlerProxyTest, DISABLED_GestureScrollByCoarsePixels) {
-#endif
-  SetSmoothScrollEnabled(true);
-  expected_disposition_ = InputHandlerProxy::DID_HANDLE;
-
-  gesture_.SetType(WebInputEvent::kGestureScrollBegin);
-  gesture_.data.scroll_begin.delta_hint_units =
-      ui::input_types::ScrollGranularity::kScrollByPixel;
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(_, cc::InputHandler::WHEEL))
-      .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
-      .WillOnce(testing::Return(false));
-  EXPECT_EQ(expected_disposition_,
-            input_handler_->RouteToTypeSpecificHandler(gesture_));
-
-  gesture_.SetType(WebInputEvent::kGestureScrollUpdate);
-  gesture_.data.scroll_update.delta_units =
-      ui::input_types::ScrollGranularity::kScrollByPixel;
-
-  EXPECT_CALL(mock_input_handler_, ScrollAnimated(_, _, _));
-  EXPECT_EQ(expected_disposition_,
-            input_handler_->RouteToTypeSpecificHandler(gesture_));
-
-  VERIFY_AND_RESET_MOCKS();
-}
-
 TEST_P(InputHandlerProxyTest, GestureScrollBeginThatTargetViewport) {
   // We shouldn't send any events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
@@ -894,7 +801,7 @@
       .WillOnce(DoAll(testing::SetArgPointee<1>(gfx::Vector2dF(0, 0)),
                       testing::SetArgPointee<2>(gfx::Vector2dF(0, 100)),
                       testing::Return(true)));
-  EXPECT_CALL(mock_input_handler_, ScrollBy(_)).Times(1);
+  EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _, _)).Times(1);
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
   EXPECT_EQ(expected_disposition_,
             input_handler_->RouteToTypeSpecificHandler(gesture_));
@@ -910,7 +817,7 @@
   EXPECT_CALL(mock_input_handler_,
               GetSnapFlingInfoAndSetAnimatingSnapTarget(_, _, _))
       .Times(0);
-  EXPECT_CALL(mock_input_handler_, ScrollBy(_)).Times(0);
+  EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _, _)).Times(0);
   EXPECT_EQ(expected_disposition_,
             input_handler_->RouteToTypeSpecificHandler(gesture_));
   VERIFY_AND_RESET_MOCKS();
@@ -1004,12 +911,15 @@
 
   VERIFY_AND_RESET_MOCKS();
 
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
   gesture_.SetType(WebInputEvent::kGestureScrollUpdate);
   gesture_.data.scroll_update.delta_y =
       -40;  // -Y means scroll down - i.e. in the +Y direction.
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
   EXPECT_EQ(expected_disposition_,
             input_handler_->RouteToTypeSpecificHandler(gesture_));
@@ -1061,21 +971,22 @@
 
   gesture_.SetType(WebInputEvent::kGestureScrollUpdate);
   gesture_.data.scroll_update.delta_y = -40;
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
   EXPECT_EQ(expected_disposition_,
             input_handler_->RouteToTypeSpecificHandler(gesture_));
   EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+  VERIFY_AND_RESET_MOCKS();
 
   // The scroll handling switches to the main thread, a GSB is sent to the main
   // thread to initiate the hit testing and compute the scroll chain.
   expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
-  EXPECT_CALL(
-      mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
-      .WillOnce(testing::Return(scroll_result_did_not_scroll_));
+  EXPECT_CALL(mock_input_handler_, ScrollUpdate(_, _, _)).Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
       .WillOnce(testing::Return(true));
   EXPECT_CALL(mock_client_, GenerateScrollBeginAndSendToMainThread(_));
@@ -1584,9 +1495,12 @@
   EXPECT_EQ(1ul, event_disposition_recorder_.size());
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
 
@@ -1620,9 +1534,12 @@
   // Start scroll in the first frame.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
 
@@ -1641,9 +1558,12 @@
   // Continue scroll in the second frame, pinch, then start another scroll.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true)).Times(2);
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
@@ -1686,9 +1606,12 @@
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
 
@@ -1821,9 +1744,12 @@
       .WillRepeatedly(testing::Return(kImplThreadScrollState));
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput())
       .Times(::testing::AtLeast(1));
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true))
       .Times(::testing::AtLeast(1));
@@ -1898,9 +1824,12 @@
 
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillRepeatedly(testing::Return(kImplThreadScrollState));
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true))
       .Times(::testing::AtLeast(1));
@@ -1958,9 +1887,12 @@
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
   EXPECT_CALL(mock_input_handler_, ScrollEnd(true));
 
@@ -1996,7 +1928,8 @@
       .WillOnce(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_not_scroll_));
 
   HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
@@ -2057,9 +1990,12 @@
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(1);
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
 
   // No prediction when start with a GSB
@@ -2101,9 +2037,12 @@
   EXPECT_CALL(mock_input_handler_, ScrollBegin(_, _))
       .WillOnce(testing::Return(kImplThreadScrollState));
   EXPECT_CALL(mock_input_handler_, SetNeedsAnimateInput()).Times(2);
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillRepeatedly(testing::Return(false));
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll_));
 
   HandleGestureEvent(WebInputEvent::kGestureScrollBegin);
@@ -2704,7 +2643,8 @@
   scroll_result_did_scroll.did_scroll = true;
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll));
 
   base::HistogramTester histogram_tester;
@@ -2762,7 +2702,8 @@
   scroll_result_did_scroll.did_scroll = true;
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll));
 
   base::HistogramTester histogram_tester;
@@ -2802,7 +2743,8 @@
   scroll_result_did_scroll.did_scroll = true;
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll));
 
   base::HistogramTester histogram_tester;
@@ -2841,7 +2783,8 @@
   scroll_result_did_scroll.did_scroll = true;
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll));
 
   base::HistogramTester histogram_tester;
@@ -2879,7 +2822,8 @@
   scroll_result_did_scroll.did_scroll = true;
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll));
 
   base::HistogramTester histogram_tester;
@@ -2909,7 +2853,8 @@
   scroll_result_did_scroll.did_scroll = true;
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll));
 
   base::HistogramTester histogram_tester;
@@ -2945,7 +2890,8 @@
   scroll_result_did_scroll.did_scroll = true;
   EXPECT_CALL(
       mock_input_handler_,
-      ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+      ScrollUpdate(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0)),
+                   _, _))
       .WillRepeatedly(testing::Return(scroll_result_did_scroll));
 
   base::HistogramTester histogram_tester;
diff --git a/ui/gfx/BUILD.gn b/ui/gfx/BUILD.gn
index faf9f52..0f58185 100644
--- a/ui/gfx/BUILD.gn
+++ b/ui/gfx/BUILD.gn
@@ -779,7 +779,6 @@
       "transform_util_unittest.cc",
       "utf16_indexing_unittest.cc",
     ]
-    data += [ "$root_out_dir/ui_test.pak" ]
   }
 
   if (is_win) {
@@ -804,7 +803,6 @@
     "//ui/gfx/animation",
     "//ui/gfx/geometry",
     "//ui/gfx/range",
-    "//ui/resources:ui_test_pak",
   ]
 
   if (!is_ios) {
@@ -816,7 +814,7 @@
   }
 
   data_deps = [
-    "//ui/resources:ui_test_pak",
+    "//ui/resources:ui_test_pak_data",
   ]
 
   if (is_mac || is_ios) {
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index c29ce6b..d5c2bcc 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -905,6 +905,8 @@
     "animation/test/test_ink_drop_ripple_observer.h",
     "controls/textfield/textfield_test_api.cc",
     "controls/textfield/textfield_test_api.h",
+    "layout/animating_layout_manager_test_util.cc",
+    "layout/animating_layout_manager_test_util.h",
     "test/button_test_api.cc",
     "test/button_test_api.h",
     "test/capture_tracking_view.cc",
diff --git a/ui/views/examples/dialog_example.cc b/ui/views/examples/dialog_example.cc
index a0c50d8e..e3b6ede2 100644
--- a/ui/views/examples/dialog_example.cc
+++ b/ui/views/examples/dialog_example.cc
@@ -36,7 +36,13 @@
 template <class DialogType>
 class DialogExample::Delegate : public virtual DialogType {
  public:
-  explicit Delegate(DialogExample* parent) : parent_(parent) {}
+  explicit Delegate(DialogExample* parent) : parent_(parent) {
+    DialogDelegate::set_buttons(parent_->GetDialogButtons());
+    DialogDelegate::set_button_label(ui::DIALOG_BUTTON_OK,
+                                     parent_->ok_button_label_->GetText());
+    DialogDelegate::set_button_label(ui::DIALOG_BUTTON_CANCEL,
+                                     parent_->cancel_button_label_->GetText());
+  }
 
   void InitDelegate() {
     this->SetLayoutManager(std::make_unique<FillLayout>());
@@ -67,14 +73,6 @@
 
   bool Cancel() override { return parent_->AllowDialogClose(false); }
   bool Accept() override { return parent_->AllowDialogClose(true); }
-  int GetDialogButtons() const override { return parent_->GetDialogButtons(); }
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override {
-    if (button == ui::DIALOG_BUTTON_OK)
-      return parent_->ok_button_label_->GetText();
-    if (button == ui::DIALOG_BUTTON_CANCEL)
-      return parent_->cancel_button_label_->GetText();
-    return base::string16();
-  }
 
  private:
   DialogExample* parent_;
diff --git a/ui/views/layout/animating_layout_manager_test_util.cc b/ui/views/layout/animating_layout_manager_test_util.cc
new file mode 100644
index 0000000..20f3eb8
--- /dev/null
+++ b/ui/views/layout/animating_layout_manager_test_util.cc
@@ -0,0 +1,29 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ui/views/layout/animating_layout_manager_test_util.h"
+
+#include "base/run_loop.h"
+#include "ui/views/layout/animating_layout_manager.h"
+#include "ui/views/view.h"
+
+namespace views {
+namespace test {
+
+AnimatingLayoutManager* GetAnimatingLayoutManager(View* view) {
+  return static_cast<AnimatingLayoutManager*>(view->GetLayoutManager());
+}
+
+void WaitForAnimatingLayoutManager(AnimatingLayoutManager* layout_manager) {
+  base::RunLoop loop;
+  layout_manager->PostOrQueueAction(loop.QuitClosure());
+  loop.Run();
+}
+
+void WaitForAnimatingLayoutManager(View* view) {
+  return WaitForAnimatingLayoutManager(GetAnimatingLayoutManager(view));
+}
+
+}  // namespace test
+}  // namespace views
diff --git a/ui/views/layout/animating_layout_manager_test_util.h b/ui/views/layout/animating_layout_manager_test_util.h
new file mode 100644
index 0000000..a9cc641
--- /dev/null
+++ b/ui/views/layout/animating_layout_manager_test_util.h
@@ -0,0 +1,26 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_VIEWS_LAYOUT_ANIMATING_LAYOUT_MANAGER_TEST_UTIL_H_
+#define UI_VIEWS_LAYOUT_ANIMATING_LAYOUT_MANAGER_TEST_UTIL_H_
+
+namespace views {
+
+class AnimatingLayoutManager;
+class View;
+
+namespace test {
+
+// Gets the AnimatingLayoutManager for a View. This assumes that one exists, no
+// type checks are performed.
+AnimatingLayoutManager* GetAnimatingLayoutManager(View* view);
+
+// Waits for animations to finish and pending tasks to run.
+void WaitForAnimatingLayoutManager(AnimatingLayoutManager* layout_manager);
+void WaitForAnimatingLayoutManager(View* view);
+
+}  // namespace test
+}  // namespace views
+
+#endif  // UI_VIEWS_LAYOUT_ANIMATING_LAYOUT_MANAGER_TEST_UTIL_H_
diff --git a/ui/views/window/dialog_client_view_unittest.cc b/ui/views/window/dialog_client_view_unittest.cc
index 1d83d74..a6b3f7e 100644
--- a/ui/views/window/dialog_client_view_unittest.cc
+++ b/ui/views/window/dialog_client_view_unittest.cc
@@ -40,6 +40,7 @@
     WidgetTest::SetUp();
 
     DialogDelegate::set_use_custom_frame(false);
+    DialogDelegate::set_buttons(ui::DIALOG_BUTTON_NONE);
 
     // Note: not using DialogDelegate::CreateDialogWidget(..), since that can
     // alter the frame type according to the platform.
@@ -64,13 +65,6 @@
     // DialogDelegateView would delete this, but |this| is owned by the test.
   }
 
-  int GetDialogButtons() const override { return dialog_buttons_; }
-  base::string16 GetDialogButtonLabel(ui::DialogButton button) const override {
-    return button == ui::DIALOG_BUTTON_CANCEL && !cancel_label_.empty()
-               ? cancel_label_
-               : DialogDelegate::GetDialogButtonLabel(button);
-  }
-
  protected:
   gfx::Rect GetUpdatedClientBounds() {
     client_view()->SizeToPreferredSize();
@@ -91,7 +85,7 @@
 
   // Sets the buttons to show in the dialog and refreshes the dialog.
   void SetDialogButtons(int dialog_buttons) {
-    dialog_buttons_ = dialog_buttons;
+    DialogDelegate::set_buttons(dialog_buttons);
     DialogModelChanged();
   }
 
@@ -124,7 +118,9 @@
   // exceeded. The resulting width is around 160 pixels, but depends on system
   // fonts.
   void SetLongCancelLabel() {
-    cancel_label_ = base::ASCIIToUTF16("Cancel Cancel Cancel");
+    DialogDelegate::set_button_label(
+        ui::DIALOG_BUTTON_CANCEL, base::ASCIIToUTF16("Cancel Cancel Cancel"));
+    DialogModelChanged();
   }
 
   DialogClientView* client_view() {
@@ -137,15 +133,10 @@
   // The dialog Widget.
   Widget* widget_ = nullptr;
 
-  // The bitmask of buttons to show in the dialog.
-  int dialog_buttons_ = ui::DIALOG_BUTTON_NONE;
-
   gfx::Size preferred_size_;
   gfx::Size min_size_;
   gfx::Size max_size_;
 
-  base::string16 cancel_label_;  // If set, the label for the Cancel button.
-
   DISALLOW_COPY_AND_ASSIGN(DialogClientViewTest);
 };
 
diff --git a/ui/views/window/dialog_delegate.h b/ui/views/window/dialog_delegate.h
index a5e8d29..07e4eee 100644
--- a/ui/views/window/dialog_delegate.h
+++ b/ui/views/window/dialog_delegate.h
@@ -103,7 +103,7 @@
   int GetDefaultDialogButton() const;
 
   // Returns the label of the specified dialog button.
-  virtual base::string16 GetDialogButtonLabel(ui::DialogButton button) const;
+  base::string16 GetDialogButtonLabel(ui::DialogButton button) const;
 
   // Returns whether the specified dialog button is enabled.
   virtual bool IsDialogButtonEnabled(ui::DialogButton button) const;
diff --git a/ui/webui/resources/cr_elements/shared_vars_css.html b/ui/webui/resources/cr_elements/shared_vars_css.html
index d5d8517..6f9945fc 100644
--- a/ui/webui/resources/cr_elements/shared_vars_css.html
+++ b/ui/webui/resources/cr_elements/shared_vars_css.html
@@ -34,6 +34,8 @@
     --google-blue-refresh-300: rgb(var(--google-blue-refresh-300-rgb));
     --google-blue-refresh-500-rgb: 66, 133, 244;  /* #4285f4 */
     --google-blue-refresh-500: rgb(var(--google-blue-refresh-500-rgb));
+    --google-blue-refresh-700-rgb: 25, 103, 210;  /* #1966d2 */
+    --google-blue-refresh-700: rgb(var(--google-blue-refresh-700-rgb));
 
     --google-green-refresh-300-rgb: 129, 201, 149;  /* #81c995 */
     --google-green-refresh-300: rgb(var(--google-green-refresh-300-rgb));
diff --git a/weblayer/shell/BUILD.gn b/weblayer/shell/BUILD.gn
index cfc670cb4..d93ea68f 100644
--- a/weblayer/shell/BUILD.gn
+++ b/weblayer/shell/BUILD.gn
@@ -250,7 +250,6 @@
     defines = []
 
     deps = [
-      ":pak",
       ":weblayer_shell_lib",
       "//base",
       "//build/win:default_exe_manifest",