diff --git a/DEPS b/DEPS
index 98253ef..a2e1652 100644
--- a/DEPS
+++ b/DEPS
@@ -209,11 +209,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '12eb0ee65c328f9fc98baf56528d134338025da0',
+  'skia_revision': 'f4f9c3b6cc625d5b6f32cec72fa8b67b7a8f37d5',
   # 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': '0fca2670052f229eb7f634d53cb0f0daa042bcbb',
+  'v8_revision': '6a639438a6015ce6c0e204807e18fb15b43e3cb0',
   # 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.
@@ -221,7 +221,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'c072daec5ffb06580e4bf45500a8b2d802d09ee2',
+  'angle_revision': 'c12f594a19b18231e5cdc5ccea09a52cdf52500c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -229,7 +229,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '5f4cf5d3fa39d8e4308feaed923a9dfcfe1e4fac',
+  'pdfium_revision': '5790eb518cf476b473e52b0a03784e699580abe5',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -280,7 +280,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': '26af1f63b6cbcb76d5a7045d6c1ea71f651cbdae',
+  'catapult_revision': '053e386fd4017d799b5b9130fa7a0b00b0fb0229',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -288,7 +288,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling devtools-frontend
   # and whatever else without interference from each other.
-  'devtools_frontend_revision': '287be69dde16b717a964ddac62fec207c0b85548',
+  'devtools_frontend_revision': '62b16561e433f4aa1645826923222699ac4bad38',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libprotobuf-mutator
   # and whatever else without interference from each other.
@@ -328,11 +328,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '7f7f8130415a8a6ad950b9dd987a89cdaa2f88a1',
+  'dawn_revision': '39633e2da2d0cc0d11b496f5bf4e75f8da44bba7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'quiche_revision': '96b92aa7a50beb09678c3ba786a0ff7ce335d3b9',
+  'quiche_revision': '9c1a2f0b79044aa9061f0eb2b12b4ace2b275045',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ios_webkit
   # and whatever else without interference from each other.
@@ -356,7 +356,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling nearby
   # and whatever else without interference from each other.
-  'nearby_revision': '93eec2c07b588985da41af77a4589dd0de37c677',
+  'nearby_revision': '258403118d3bd98ec6c52eeb6db0d626af05249d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling securemessage
   # and whatever else without interference from each other.
@@ -573,7 +573,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '640f555df438950bbca7a380d9099ca7a5167870',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '2ae9094c8b917273b1c8bdbc16d53e0f9362eb88',
       'condition': 'checkout_ios',
   },
 
@@ -966,7 +966,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '30cde45e06283ef80e0b5ce970cd5da9509cc0e8',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'cd3696cf7ee983de86583b898785cea56ffd07fe',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -1018,7 +1018,7 @@
     Var('chromium_git') + '/external/github.com/google/gemmlowp.git' + '@' + 'fda83bdc38b118cc6b56753bd540caa49e570745',
 
   'src/third_party/grpc/src': {
-      'url': Var('chromium_git') + '/external/github.com/grpc/grpc.git' + '@' + '3ca079faadfcc1f111b6c9a3f3fb10f4b5c794ea',
+      'url': Var('chromium_git') + '/external/github.com/grpc/grpc.git' + '@' + '54dc182082db941aa67c7c3f93ad858c99a16d7d',
   },
 
   'src/third_party/freetype/src':
@@ -1332,7 +1332,7 @@
     Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '3dd5b80bc4f172dd82925bb259cb7c82348409c5',
 
   'src/third_party/openscreen/src':
-    Var('chromium_git') + '/openscreen' + '@' + 'cacde3327295076c1e93d772ca38b115cf93bbd4',
+    Var('chromium_git') + '/openscreen' + '@' + 'dec3ae516ec228e649869988229b0e48ee8bc269',
 
   'src/third_party/openxr/src': {
     'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '97cfe495bb7a3853266b646d1c79e169387f9c7a',
@@ -1349,7 +1349,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'd53f42ae593851c3d4762dd4dd8ba49be17c52ed',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '31cfc47480a7a226c3ff06143620509cbd2aa29c',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1516,7 +1516,7 @@
     Var('swiftshader_git') + '/SwiftShader.git' + '@' +  Var('swiftshader_revision'),
 
   'src/third_party/text-fragments-polyfill/src': {
-    'url': Var('chromium_git') + '/external/github.com/GoogleChromeLabs/text-fragments-polyfill.git' + '@' + 'c2c70ad99a04d381b72286d9e70c780cdd27ebca',
+    'url': Var('chromium_git') + '/external/github.com/GoogleChromeLabs/text-fragments-polyfill.git' + '@' + '21c264b9ae719c09aacd822fa0ac5066732c4780',
     'condition': 'checkout_ios',
   },
 
@@ -1547,7 +1547,7 @@
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '22ba62ffe79c3881581ab430368bf3764d9533eb',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@83d509240cca255b5917bcb869f1d03e6325eb14',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@dd2bfc6287f294dd75f38fc6ff5e70278712f245',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '732a76d9d3c70d6aa487216495eeb28518349c3a',
@@ -1635,7 +1635,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9cfcb47ec4fa1b1c4af7d77dc8ed485f7dd1a0a9',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6b3b342e18488c8b1092a9bbaf2ad3bed7a342cc',
     'condition': 'checkout_src_internal',
   },
 
@@ -1665,7 +1665,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'IsKyc6RHdE3rG9XwwvccCgPaqzdbp1DJYilZy0LTCBMC',
+        'version': 'vU4A4sFU1mlyfYrlVQnoZyVzXFycdSz6Sar5TP_ThaoC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 432fa3b..8d1f1ab8 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -133,18 +133,6 @@
 constexpr gfx::Tween::Type kCardifiedStateTweenType =
     gfx::Tween::LINEAR_OUT_SLOW_IN;
 
-// Presentation time histogram for apps grid scroll by dragging.
-constexpr char kPageDragScrollInClamshellHistogram[] =
-    "Apps.PaginationTransition.DragScroll.PresentationTime.ClamshellMode";
-constexpr char kPageDragScrollInClamshellMaxLatencyHistogram[] =
-    "Apps.PaginationTransition.DragScroll.PresentationTime.MaxLatency."
-    "ClamshellMode";
-constexpr char kPageDragScrollInTabletHistogram[] =
-    "Apps.PaginationTransition.DragScroll.PresentationTime.TabletMode";
-constexpr char kPageDragScrollInTabletMaxLatencyHistogram[] =
-    "Apps.PaginationTransition.DragScroll.PresentationTime.MaxLatency."
-    "TabletMode";
-
 // Returns the size of a tile view excluding its padding.
 gfx::Size GetTileViewSize(const AppListConfig& config, bool cardified_state) {
   return gfx::ScaleToRoundedSize(
@@ -429,7 +417,6 @@
       GetAppListConfig().page_transition_duration(),
       GetAppListConfig().overscroll_page_transition_duration());
 
-  pagination_model_.AddObserver(this);
   pagination_controller_ = std::make_unique<PaginationController>(
       &pagination_model_,
       folder_delegate_ ? PaginationController::SCROLL_AXIS_HORIZONTAL
@@ -452,7 +439,6 @@
 
   if (model_)
     model_->RemoveObserver(this);
-  pagination_model_.RemoveObserver(this);
 
   if (item_list_)
     item_list_->RemoveObserver(this);
@@ -608,7 +594,7 @@
   return selected_view_ == view;
 }
 
-views::View* AppsGridView::GetSelectedView() const {
+AppListItemView* AppsGridView::GetSelectedView() const {
   if (selected_view_)
     return selected_view_;
   return nullptr;
@@ -2876,151 +2862,6 @@
   background_cards_.clear();
 }
 
-void AppsGridView::TotalPagesChanged(int previous_page_count,
-                                     int new_page_count) {
-  // Don't record from folder.
-  if (folder_delegate_)
-    return;
-
-  // Initial setup for the AppList starts with -1 pages. Ignore the page count
-  // change resulting from the initialization of the view.
-  if (previous_page_count == -1)
-    return;
-
-  if (previous_page_count < new_page_count) {
-    AppListPageCreationType type = AppListPageCreationType::kSyncOrInstall;
-    if (handling_keyboard_move_)
-      type = AppListPageCreationType::kMovingAppWithKeyboard;
-    else if (dragging())
-      type = AppListPageCreationType::kDraggingApp;
-    UMA_HISTOGRAM_ENUMERATION("Apps.AppList.AppsGridAddPage", type);
-  }
-}
-
-void AppsGridView::SelectedPageChanged(int old_selected, int new_selected) {
-  items_container_->layer()->SetTransform(gfx::Transform());
-  if (dragging()) {
-    drag_view_->layer()->SetTransform(gfx::Transform());
-
-    // Sets the transform to locate the scrolled content.
-    gfx::Size grid_size = GetTileGridSize();
-    gfx::Vector2d update;
-    if (pagination_controller_->scroll_axis() ==
-        PaginationController::SCROLL_AXIS_HORIZONTAL) {
-      const int page_width = grid_size.width() + GetPaddingBetweenPages();
-      update.set_x(page_width * (new_selected - old_selected));
-    } else {
-      const int page_height = grid_size.height() + GetPaddingBetweenPages();
-      update.set_y(page_height * (new_selected - old_selected));
-    }
-    drag_view_start_ += update;
-    drag_view_->SetPosition(drag_view_->origin() + update);
-    UpdateDropTargetRegion();
-    Layout();
-    MaybeStartPageFlipTimer(last_drag_point_);
-  } else {
-    // If |selected_view_| is no longer on the page, select the first item in
-    // the page relative to the page swap in order to keep keyboard focus
-    // movement predictable.
-    if (selected_view_ && GetIndexOfView(selected_view_).page != new_selected) {
-      GetViewAtIndex(
-          GridIndex(new_selected, (old_selected < new_selected)
-                                      ? 0
-                                      : (GetItemsNumOfPage(new_selected) - 1)))
-          ->RequestFocus();
-    } else {
-      ClearSelectedView(selected_view_);
-    }
-    Layout();
-  }
-}
-
-void AppsGridView::TransitionStarting() {
-  // Drag ends and animation starts.
-  presentation_time_recorder_.reset();
-
-  MaybeCreateGradientMask();
-  CancelContextMenusOnCurrentPage();
-}
-
-void AppsGridView::TransitionStarted() {
-  if (abs(pagination_model_.transition().target_page -
-          pagination_model_.selected_page()) > 1) {
-    Layout();
-  }
-
-  pagination_metrics_tracker_ =
-      GetWidget()->GetCompositor()->RequestNewThroughputTracker();
-  pagination_metrics_tracker_->Start(metrics_util::ForSmoothness(
-      base::BindRepeating(&ReportPaginationSmoothness, IsTabletMode())));
-}
-
-void AppsGridView::TransitionChanged() {
-  const PaginationModel::Transition& transition =
-      pagination_model_.transition();
-  if (!pagination_model_.is_valid_page(transition.target_page))
-    return;
-
-  // Sets the transform to locate the scrolled content.
-  gfx::Size grid_size = GetTileGridSize();
-  gfx::Vector2dF translate;
-  const int dir =
-      transition.target_page > pagination_model_.selected_page() ? -1 : 1;
-  if (pagination_controller_->scroll_axis() ==
-      PaginationController::SCROLL_AXIS_HORIZONTAL) {
-    const int page_width = grid_size.width() + GetPaddingBetweenPages();
-    translate.set_x(page_width * transition.progress * dir);
-  } else {
-    const int page_height = grid_size.height() + GetPaddingBetweenPages();
-    translate.set_y(page_height * transition.progress * dir);
-  }
-  gfx::Transform transform;
-  transform.Translate(translate);
-  items_container_->layer()->SetTransform(transform);
-
-  // |drag_view_| should stay in the same location in the screen, so makes
-  // the opposite effect of the transform.
-  if (drag_view_) {
-    gfx::Transform drag_view_transform;
-    drag_view_transform.Translate(-translate);
-    drag_view_->layer()->SetTransform(drag_view_transform);
-  }
-
-  if (presentation_time_recorder_)
-    presentation_time_recorder_->RequestNext();
-}
-
-void AppsGridView::TransitionEnded() {
-  pagination_metrics_tracker_->Stop();
-
-  // Gradient mask is no longer necessary once transition is finished.
-  if (layer()->layer_mask_layer())
-    layer()->SetMaskLayer(nullptr);
-}
-
-void AppsGridView::ScrollStarted() {
-  DCHECK(!presentation_time_recorder_);
-
-  MaybeCreateGradientMask();
-  if (IsTabletMode()) {
-    presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
-        GetWidget()->GetCompositor(), kPageDragScrollInTabletHistogram,
-        kPageDragScrollInTabletMaxLatencyHistogram);
-  } else {
-    presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
-        GetWidget()->GetCompositor(), kPageDragScrollInClamshellHistogram,
-        kPageDragScrollInClamshellMaxLatencyHistogram);
-  }
-}
-
-void AppsGridView::ScrollEnded() {
-  // Scroll can end without triggering state animation.
-  presentation_time_recorder_.reset();
-  // Need to reset the mask because transition will not happen in some
-  // cases. (See https://crbug.com/1049275)
-  layer()->SetMaskLayer(nullptr);
-}
-
 void AppsGridView::OnAppListModelStatusChanged() {
   UpdatePulsingBlockViews();
   Layout();
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index 79fb79f6..85cf3b7 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -20,17 +20,14 @@
 #include "ash/app_list/views/app_list_view.h"
 #include "ash/ash_export.h"
 #include "ash/public/cpp/pagination/pagination_model.h"
-#include "ash/public/cpp/pagination/pagination_model_observer.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "build/build_config.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/base/models/list_model_observer.h"
 #include "ui/compositor/layer_animation_observer.h"
-#include "ui/compositor/throughput_tracker.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/views/animation/bounds_animator.h"
@@ -82,7 +79,6 @@
 // - The grid of apps in a folder
 class ASH_EXPORT AppsGridView : public views::View,
                                 public AppListItemListObserver,
-                                public PaginationModelObserver,
                                 public AppListModelObserver,
                                 public ui::ImplicitAnimationObserver,
                                 public views::BoundsAnimatorObserver {
@@ -151,7 +147,7 @@
   void ClearAnySelectedView();
   bool IsSelectedView(const AppListItemView* view) const;
   bool has_selected_view() const { return selected_view_ != nullptr; }
-  views::View* GetSelectedView() const;
+  AppListItemView* GetSelectedView() const;
 
   void InitiateDrag(AppListItemView* view,
                     Pointer pointer,
@@ -363,6 +359,8 @@
   int BackgroundCardCountForTesting() const { return background_cards_.size(); }
 
  protected:
+  class FadeoutLayerDelegate;
+
   // Returns the size of the entire tile grid.
   virtual gfx::Size GetTileGridSize() const = 0;
 
@@ -376,6 +374,29 @@
   // page/slot info.
   gfx::Rect GetExpectedTileBounds(const GridIndex& index) const;
 
+  GridIndex GetIndexOfView(const AppListItemView* view) const;
+  AppListItemView* GetViewAtIndex(const GridIndex& index) const;
+
+  // Returns the number of existing items in specified page. Returns 0 if |page|
+  // is out of range.
+  int GetItemsNumOfPage(int page) const;
+
+  // Updates |drop_target_| and |drop_target_region_| based on |drag_view_|'s
+  // position.
+  void UpdateDropTargetRegion();
+
+  // Cancels any context menus showing for app items on the current page.
+  void CancelContextMenusOnCurrentPage();
+
+  // Starts the page flip timer if |drag_point| is in left/right side page flip
+  // zone or is over page switcher.
+  void MaybeStartPageFlipTimer(const gfx::Point& drag_point);
+
+  // Creates a layer mask for gradient alpha when the feature is enabled.
+  // TODO(crbug.com/1211608): Move to PagedAppsGridView when
+  // `fadeout_layer_delegate_` moves.
+  void MaybeCreateGradientMask();
+
   bool ignore_layout() const { return ignore_layout_; }
   views::BoundsAnimator* bounds_animator() { return bounds_animator_.get(); }
   views::View* items_container() { return items_container_; }
@@ -385,6 +406,8 @@
   int reorder_placeholder_slot() const { return reorder_placeholder_.slot; }
   int vertical_tile_padding() const { return vertical_tile_padding_; }
   int horizontal_tile_padding() const { return horizontal_tile_padding_; }
+  const gfx::Point& last_drag_point() const { return last_drag_point_; }
+  bool handling_keyboard_move() const { return handling_keyboard_move_; }
 
   // TODO(crbug.com/1211608): Move these member variables to PagedAppsGridView.
   PaginationModel pagination_model_{this};
@@ -400,8 +423,15 @@
   // drag state is cleared complete.
   bool items_need_layer_for_drag_ = false;
 
+  // Subclasses need non-const access.
+  AppListItemView* drag_view_ = nullptr;
+
+  // The location of |drag_view_| when the drag started.
+  gfx::Point drag_view_start_;
+
+  std::unique_ptr<FadeoutLayerDelegate> fadeout_layer_delegate_;
+
  private:
-  class FadeoutLayerDelegate;
   friend class test::AppsGridViewTestApi;
   friend class test::AppsGridViewTest;
   friend class PagedViewStructure;
@@ -438,9 +468,6 @@
 
   void SetSelectedItemByIndex(const GridIndex& index);
 
-  GridIndex GetIndexOfView(const AppListItemView* view) const;
-  AppListItemView* GetViewAtIndex(const GridIndex& index) const;
-
   // Calculates the offset for |page_of_view| based on current page and
   // transition target page.
   const gfx::Vector2d CalculateTransitionOffset(int page_of_view) const;
@@ -464,10 +491,6 @@
   void ExtractDragLocation(const gfx::Point& root_location,
                            gfx::Point* drag_point);
 
-  // Updates |drop_target_| and |drop_target_region_|
-  // based on |drag_view_|'s position.
-  void UpdateDropTargetRegion();
-
   bool DropTargetIsValidFolder();
 
   // Updates |drop_target_| as a location for potential reordering after the
@@ -488,10 +511,6 @@
   void DispatchDragEventToDragAndDropHost(
       const gfx::Point& location_in_screen_coordinates);
 
-  // Starts the page flip timer if |drag_point| is in left/right side page flip
-  // zone or is over page switcher.
-  void MaybeStartPageFlipTimer(const gfx::Point& drag_point);
-
   // Invoked when |page_flip_timer_| fires.
   void OnPageFlipTimer();
 
@@ -529,9 +548,6 @@
   void RemoveLastItemFromReparentItemFolderIfNecessary(
       const std::string& source_folder_id);
 
-  // Cancels any context menus showing for app items on the current page.
-  void CancelContextMenusOnCurrentPage();
-
   // Removes the AppListItemView at |index| in |view_model_|, removes it from
   // view structure as well and deletes it. Sanitizes the view structure if
   // |sanitize| if true. (|sanitize| should be true when moving an item outside
@@ -563,16 +579,6 @@
                        size_t to_index,
                        AppListItem* item) override;
 
-  // Overridden from PaginationModelObserver:
-  void TotalPagesChanged(int previous_page_count, int new_page_count) override;
-  void SelectedPageChanged(int old_selected, int new_selected) override;
-  void TransitionStarting() override;
-  void TransitionStarted() override;
-  void TransitionChanged() override;
-  void TransitionEnded() override;
-  void ScrollStarted() override;
-  void ScrollEnded() override;
-
   // Overridden from AppListModelObserver:
   void OnAppListModelStatusChanged() override;
 
@@ -727,10 +733,6 @@
   // Update the padding of tile view based on the contents bounds.
   void UpdateTilePadding();
 
-  // Returns the number of existing items in specified page. Returns 0 if |page|
-  // is out of range.
-  int GetItemsNumOfPage(int page) const;
-
   // Starts the animation to transition the |drag_item| from |source_bounds| to
   // the target bounds in the |folder_item_view|. Note that this animation
   // should run only after |drag_item| is added to the folder.
@@ -780,9 +782,6 @@
   // Invoked when |host_drag_start_timer_| fires.
   void OnHostDragStartTimerFired();
 
-  // Create a layer mask for graident alpha when the feature is enabled.
-  void MaybeCreateGradientMask();
-
   // Obtains the target page to flip for |drag_point|.
   int GetPageFlipTargetForDrag(const gfx::Point& drag_point);
 
@@ -815,8 +814,6 @@
 
   AppListItemView* selected_view_ = nullptr;
 
-  AppListItemView* drag_view_ = nullptr;
-
   // The index of the drag_view_ when the drag starts.
   GridIndex drag_view_init_index_;
 
@@ -826,9 +823,6 @@
   // The point where the drag started in GridView coordinates.
   gfx::Point drag_start_grid_view_;
 
-  // The location of |drag_view_| when the drag started.
-  gfx::Point drag_view_start_;
-
   // Page the drag started on.
   int drag_start_page_ = -1;
 
@@ -892,8 +886,6 @@
   // True if the drag_view_ item is a folder item being dragged for reparenting.
   bool dragging_for_reparent_item_ = false;
 
-  std::unique_ptr<FadeoutLayerDelegate> fadeout_layer_delegate_;
-
   // Delay for when |page_flip_timer_| should fire after user drags an item near
   // the edge.
   base::TimeDelta page_flip_delay_;
@@ -928,24 +920,6 @@
   // True if the AppList is in cardified state.
   bool cardified_state_ = false;
 
-  // Records smoothness of pagination animation.
-  absl::optional<ui::ThroughputTracker> pagination_metrics_tracker_;
-
-  // Records the presentation time for apps grid dragging.
-  std::unique_ptr<PresentationTimeRecorder> presentation_time_recorder_;
-
-  // Indicates whether the AppsGridView is in mouse drag.
-  bool is_in_mouse_drag_ = false;
-
-  // The initial mouse drag location in root window coordinate. Updates when
-  // drag on AppsGridView starts.
-  gfx::PointF mouse_drag_start_point_;
-
-  // The last mouse drag location in root window coordinate. Different from
-  // |last_drag_point_|, |last_mouse_drag_point_| is the location of the most
-  // recent drag on AppsGridView instead of the app icon.
-  gfx::PointF last_mouse_drag_point_;
-
   // The highlighted page during cardified state.
   int highlighted_page_ = -1;
 
diff --git a/ash/app_list/views/paged_apps_grid_view.cc b/ash/app_list/views/paged_apps_grid_view.cc
index 3b7bf53..61517620 100644
--- a/ash/app_list/views/paged_apps_grid_view.cc
+++ b/ash/app_list/views/paged_apps_grid_view.cc
@@ -13,16 +13,35 @@
 #include "ash/public/cpp/pagination/pagination_controller.h"
 #include "ash/public/cpp/pagination/pagination_model.h"
 #include "base/check.h"
+#include "base/metrics/histogram_macros.h"
 #include "ui/compositor/layer.h"
 #include "ui/events/event.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/insets.h"
 #include "ui/gfx/geometry/point_f.h"
 #include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d.h"
+#include "ui/gfx/geometry/vector2d_f.h"
+#include "ui/gfx/transform.h"
 #include "ui/views/view.h"
 
 namespace ash {
+namespace {
+
+// Presentation time histogram for apps grid scroll by dragging.
+constexpr char kPageDragScrollInClamshellHistogram[] =
+    "Apps.PaginationTransition.DragScroll.PresentationTime.ClamshellMode";
+constexpr char kPageDragScrollInClamshellMaxLatencyHistogram[] =
+    "Apps.PaginationTransition.DragScroll.PresentationTime.MaxLatency."
+    "ClamshellMode";
+constexpr char kPageDragScrollInTabletHistogram[] =
+    "Apps.PaginationTransition.DragScroll.PresentationTime.TabletMode";
+constexpr char kPageDragScrollInTabletMaxLatencyHistogram[] =
+    "Apps.PaginationTransition.DragScroll.PresentationTime.MaxLatency."
+    "TabletMode";
+
+}  // namespace
 
 PagedAppsGridView::PagedAppsGridView(
     ContentsView* contents_view,
@@ -32,9 +51,12 @@
                    folder_delegate),
       contents_view_(contents_view) {
   DCHECK(contents_view_);
+  pagination_model_.AddObserver(this);
 }
 
-PagedAppsGridView::~PagedAppsGridView() = default;
+PagedAppsGridView::~PagedAppsGridView() {
+  pagination_model_.RemoveObserver(this);
+}
 
 void PagedAppsGridView::HandleScrollFromAppListView(const gfx::Vector2d& offset,
                                                     ui::EventType type) {
@@ -261,6 +283,156 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
+// PaginationModelObserver:
+
+void PagedAppsGridView::TotalPagesChanged(int previous_page_count,
+                                          int new_page_count) {
+  // Don't record from folder.
+  if (is_in_folder())
+    return;
+
+  // Initial setup for the AppList starts with -1 pages. Ignore the page count
+  // change resulting from the initialization of the view.
+  if (previous_page_count == -1)
+    return;
+
+  if (previous_page_count < new_page_count) {
+    AppListPageCreationType type = AppListPageCreationType::kSyncOrInstall;
+    if (handling_keyboard_move())
+      type = AppListPageCreationType::kMovingAppWithKeyboard;
+    else if (dragging())
+      type = AppListPageCreationType::kDraggingApp;
+    UMA_HISTOGRAM_ENUMERATION("Apps.AppList.AppsGridAddPage", type);
+  }
+}
+
+void PagedAppsGridView::SelectedPageChanged(int old_selected,
+                                            int new_selected) {
+  items_container()->layer()->SetTransform(gfx::Transform());
+  if (dragging()) {
+    drag_view_->layer()->SetTransform(gfx::Transform());
+
+    // Sets the transform to locate the scrolled content.
+    gfx::Size grid_size = GetTileGridSize();
+    gfx::Vector2d update;
+    if (pagination_controller_->scroll_axis() ==
+        PaginationController::SCROLL_AXIS_HORIZONTAL) {
+      const int page_width = grid_size.width() + GetPaddingBetweenPages();
+      update.set_x(page_width * (new_selected - old_selected));
+    } else {
+      const int page_height = grid_size.height() + GetPaddingBetweenPages();
+      update.set_y(page_height * (new_selected - old_selected));
+    }
+    drag_view_start_ += update;
+    drag_view_->SetPosition(drag_view_->origin() + update);
+    UpdateDropTargetRegion();
+    Layout();
+    MaybeStartPageFlipTimer(last_drag_point());
+  } else {
+    AppListItemView* selected_view = GetSelectedView();
+    // If |selected_view| is no longer on the page, select the first item in
+    // the page relative to the page swap in order to keep keyboard focus
+    // movement predictable.
+    if (selected_view && GetIndexOfView(selected_view).page != new_selected) {
+      GetViewAtIndex(
+          GridIndex(new_selected, (old_selected < new_selected)
+                                      ? 0
+                                      : (GetItemsNumOfPage(new_selected) - 1)))
+          ->RequestFocus();
+    } else {
+      ClearSelectedView(selected_view);
+    }
+    Layout();
+  }
+}
+
+void PagedAppsGridView::TransitionStarting() {
+  // Drag ends and animation starts.
+  presentation_time_recorder_.reset();
+
+  MaybeCreateGradientMask();
+  CancelContextMenusOnCurrentPage();
+}
+
+void PagedAppsGridView::TransitionStarted() {
+  if (abs(pagination_model_.transition().target_page -
+          pagination_model_.selected_page()) > 1) {
+    Layout();
+  }
+
+  pagination_metrics_tracker_ =
+      GetWidget()->GetCompositor()->RequestNewThroughputTracker();
+  pagination_metrics_tracker_->Start(metrics_util::ForSmoothness(
+      base::BindRepeating(&ReportPaginationSmoothness, IsTabletMode())));
+}
+
+void PagedAppsGridView::TransitionChanged() {
+  const PaginationModel::Transition& transition =
+      pagination_model_.transition();
+  if (!pagination_model_.is_valid_page(transition.target_page))
+    return;
+
+  // Sets the transform to locate the scrolled content.
+  gfx::Size grid_size = GetTileGridSize();
+  gfx::Vector2dF translate;
+  const int dir =
+      transition.target_page > pagination_model_.selected_page() ? -1 : 1;
+  if (pagination_controller_->scroll_axis() ==
+      PaginationController::SCROLL_AXIS_HORIZONTAL) {
+    const int page_width = grid_size.width() + GetPaddingBetweenPages();
+    translate.set_x(page_width * transition.progress * dir);
+  } else {
+    const int page_height = grid_size.height() + GetPaddingBetweenPages();
+    translate.set_y(page_height * transition.progress * dir);
+  }
+  gfx::Transform transform;
+  transform.Translate(translate);
+  items_container()->layer()->SetTransform(transform);
+
+  // |drag_view_| should stay in the same location in the screen, so makes
+  // the opposite effect of the transform.
+  if (drag_view_) {
+    gfx::Transform drag_view_transform;
+    drag_view_transform.Translate(-translate);
+    drag_view_->layer()->SetTransform(drag_view_transform);
+  }
+
+  if (presentation_time_recorder_)
+    presentation_time_recorder_->RequestNext();
+}
+
+void PagedAppsGridView::TransitionEnded() {
+  pagination_metrics_tracker_->Stop();
+
+  // Gradient mask is no longer necessary once transition is finished.
+  if (layer()->layer_mask_layer())
+    layer()->SetMaskLayer(nullptr);
+}
+
+void PagedAppsGridView::ScrollStarted() {
+  DCHECK(!presentation_time_recorder_);
+
+  MaybeCreateGradientMask();
+  if (IsTabletMode()) {
+    presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
+        GetWidget()->GetCompositor(), kPageDragScrollInTabletHistogram,
+        kPageDragScrollInTabletMaxLatencyHistogram);
+  } else {
+    presentation_time_recorder_ = CreatePresentationTimeHistogramRecorder(
+        GetWidget()->GetCompositor(), kPageDragScrollInClamshellHistogram,
+        kPageDragScrollInClamshellMaxLatencyHistogram);
+  }
+}
+
+void PagedAppsGridView::ScrollEnded() {
+  // Scroll can end without triggering state animation.
+  presentation_time_recorder_.reset();
+  // Need to reset the mask because transition will not happen in some
+  // cases. (See https://crbug.com/1049275)
+  layer()->SetMaskLayer(nullptr);
+}
+
+////////////////////////////////////////////////////////////////////////////////
 // private:
 
 bool PagedAppsGridView::ShouldHandleDragEvent(const ui::LocatedEvent& event) {
diff --git a/ash/app_list/views/paged_apps_grid_view.h b/ash/app_list/views/paged_apps_grid_view.h
index 09de675..1db3fe51 100644
--- a/ash/app_list/views/paged_apps_grid_view.h
+++ b/ash/app_list/views/paged_apps_grid_view.h
@@ -5,8 +5,13 @@
 #ifndef ASH_APP_LIST_VIEWS_PAGED_APPS_GRID_VIEW_H_
 #define ASH_APP_LIST_VIEWS_PAGED_APPS_GRID_VIEW_H_
 
+#include <memory>
+
 #include "ash/app_list/views/apps_grid_view.h"
 #include "ash/ash_export.h"
+#include "ash/public/cpp/pagination/pagination_model_observer.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "ui/compositor/throughput_tracker.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/point_f.h"
 
@@ -21,7 +26,8 @@
 // An apps grid that shows the apps on a series of fixed-size pages.
 // Used for the peeking/fullscreen launcher, home launcher and folders.
 // Created by and is a child of AppsContainerView.
-class ASH_EXPORT PagedAppsGridView : public AppsGridView {
+class ASH_EXPORT PagedAppsGridView : public AppsGridView,
+                                     public PaginationModelObserver {
  public:
   PagedAppsGridView(ContentsView* contents_view,
                     AppsGridViewFolderDelegate* folder_delegate);
@@ -47,6 +53,16 @@
   gfx::Insets GetTilePadding() const override;
   gfx::Size GetTileGridSize() const override;
 
+  // PaginationModelObserver:
+  void TotalPagesChanged(int previous_page_count, int new_page_count) override;
+  void SelectedPageChanged(int old_selected, int new_selected) override;
+  void TransitionStarting() override;
+  void TransitionStarted() override;
+  void TransitionChanged() override;
+  void TransitionEnded() override;
+  void ScrollStarted() override;
+  void ScrollEnded() override;
+
  private:
   // Indicates whether the drag event (from the gesture or mouse) should be
   // handled by PagedAppsGridView.
@@ -67,6 +83,12 @@
   // The last mouse drag location in root window coordinate. Used for
   // between-item drags that move the entire grid, not for app icon drags.
   gfx::PointF last_mouse_drag_point_;
+
+  // Records smoothness of pagination animation.
+  absl::optional<ui::ThroughputTracker> pagination_metrics_tracker_;
+
+  // Records the presentation time for apps grid dragging.
+  std::unique_ptr<PresentationTimeRecorder> presentation_time_recorder_;
 };
 
 }  // namespace ash
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index 331e399..a3188530 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -441,7 +441,7 @@
 
 // Enable or disable IME decoder via Mojo connection on Chrome OS.
 const base::Feature kImeMojoDecoder{"ImeMojoDecoder",
-                                    base::FEATURE_DISABLED_BY_DEFAULT};
+                                    base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable or disable system emoji picker.
 const base::Feature kImeSystemEmojiPicker{"SystemEmojiPicker",
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 3154839..fad1f1b0 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -3844,10 +3844,16 @@
       "android/java/src/org/chromium/base/compat/ApiHelperForQ.java",
       "android/java/src/org/chromium/base/compat/ApiHelperForR.java",
       "android/java/src/org/chromium/base/compat/ApiHelperForS.java",
+      "android/java/src/org/chromium/base/jank_tracker/FrameMetrics.java",
+      "android/java/src/org/chromium/base/jank_tracker/FrameMetricsListener.java",
+      "android/java/src/org/chromium/base/jank_tracker/FrameMetricsStore.java",
       "android/java/src/org/chromium/base/jank_tracker/JankActivityTracker.java",
-      "android/java/src/org/chromium/base/jank_tracker/JankFrameMetricsListener.java",
-      "android/java/src/org/chromium/base/jank_tracker/JankMetricMeasurement.java",
+      "android/java/src/org/chromium/base/jank_tracker/JankMetricCalculator.java",
       "android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java",
+      "android/java/src/org/chromium/base/jank_tracker/JankMetrics.java",
+      "android/java/src/org/chromium/base/jank_tracker/JankReportingRunnable.java",
+      "android/java/src/org/chromium/base/jank_tracker/JankReportingScheduler.java",
+      "android/java/src/org/chromium/base/jank_tracker/JankScenario.java",
       "android/java/src/org/chromium/base/jank_tracker/JankTracker.java",
       "android/java/src/org/chromium/base/library_loader/LegacyLinker.java",
       "android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
@@ -4128,9 +4134,13 @@
       "android/junit/src/org/chromium/base/PromiseTest.java",
       "android/junit/src/org/chromium/base/UnownedUserDataHostTest.java",
       "android/junit/src/org/chromium/base/UnownedUserDataKeyTest.java",
+      "android/junit/src/org/chromium/base/jank_tracker/FrameMetricsListenerTest.java",
+      "android/junit/src/org/chromium/base/jank_tracker/FrameMetricsStoreTest.java",
       "android/junit/src/org/chromium/base/jank_tracker/JankActivityTrackerTest.java",
-      "android/junit/src/org/chromium/base/jank_tracker/JankFrameMetricsListenerTest.java",
-      "android/junit/src/org/chromium/base/jank_tracker/JankMetricMeasurementTest.java",
+      "android/junit/src/org/chromium/base/jank_tracker/JankMetricCalculatorTest.java",
+      "android/junit/src/org/chromium/base/jank_tracker/JankMetricUMARecorderTest.java",
+      "android/junit/src/org/chromium/base/jank_tracker/JankReportingRunnableTest.java",
+      "android/junit/src/org/chromium/base/jank_tracker/JankReportingSchedulerTest.java",
       "android/junit/src/org/chromium/base/memory/MemoryPressureMonitorTest.java",
       "android/junit/src/org/chromium/base/metrics/CachingUmaRecorderTest.java",
       "android/junit/src/org/chromium/base/metrics/test/ShadowRecordHistogramTest.java",
diff --git a/base/allocator/allocator_shim.h b/base/allocator/allocator_shim.h
index 080029d..65a1668 100644
--- a/base/allocator/allocator_shim.h
+++ b/base/allocator/allocator_shim.h
@@ -166,7 +166,7 @@
 BASE_EXPORT void ConfigurePartitionRefCountSupport(bool enable_ref_count);
 #endif
 
-#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && PA_ALLOW_PCSCAN
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
 BASE_EXPORT void EnablePCScan(bool dcscan);
 #endif
 
diff --git a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
index 0c097b1..4250b730 100644
--- a/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
+++ b/base/allocator/allocator_shim_default_dispatch_to_partition_alloc.cc
@@ -482,7 +482,7 @@
 }
 #endif  // BUILDFLAG(ENABLE_RUNTIME_BACKUP_REF_PTR_CONTROL)
 
-#if PA_ALLOW_PCSCAN
+#if defined(PA_ALLOW_PCSCAN)
 void EnablePCScan(bool dcscan) {
   internal::PCScan::Initialize(
       dcscan ? internal::PCScan::WantedWriteProtectionMode::kEnabled
@@ -492,7 +492,7 @@
     internal::PCScan::RegisterScannableRoot(AlignedAllocator());
   internal::NonScannableAllocator::Instance().EnablePCScan();
 }
-#endif
+#endif  // defined(PA_ALLOW_PCSCAN)
 
 #if defined(OS_WIN)
 // Call this as soon as possible during startup.
diff --git a/base/allocator/allocator_shim_unittest.cc b/base/allocator/allocator_shim_unittest.cc
index 4408c6a..5ffe5f8 100644
--- a/base/allocator/allocator_shim_unittest.cc
+++ b/base/allocator/allocator_shim_unittest.cc
@@ -451,7 +451,8 @@
   free(non_hooked_ptr);
 }
 
-#if defined(OS_APPLE)
+// PartitionAlloc-Everywhere does not support batch_malloc / batch_free.
+#if defined(OS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 TEST_F(AllocatorShimTest, InterceptLibcSymbolsBatchMallocFree) {
   InsertAllocatorDispatch(&g_mock_dispatch);
 
@@ -490,7 +491,7 @@
   ASSERT_GE(free_definite_sizes_intercepted_by_size[19], 1u);
   RemoveAllocatorDispatchForTesting(&g_mock_dispatch);
 }
-#endif  // defined(OS_APPLE)
+#endif  // defined(OS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
 #if defined(OS_WIN)
 TEST_F(AllocatorShimTest, InterceptUcrtAlignedAllocationSymbols) {
diff --git a/base/allocator/partition_allocator/partition_address_space.cc b/base/allocator/partition_allocator/partition_address_space.cc
index b79483a4..2fc7ecf 100644
--- a/base/allocator/partition_allocator/partition_address_space.cc
+++ b/base/allocator/partition_allocator/partition_address_space.cc
@@ -71,7 +71,7 @@
   PA_DCHECK(IsInBRPPool(reinterpret_cast<void*>(current - 1)));
   PA_DCHECK(!IsInBRPPool(reinterpret_cast<void*>(current)));
 
-#if PA_ALLOW_PCSCAN
+#if defined(PA_ALLOW_PCSCAN)
   // Reserve memory for PCScan quarantine card table.
   void* requested_address = reinterpret_cast<void*>(brp_pool_base_address_);
   char* actual_address = internal::AddressPoolManager::GetInstance()->Reserve(
@@ -80,7 +80,7 @@
       << "QuarantineCardTable is required to be allocated in the beginning of "
          "the BRPPool";
   SetSystemPagesAccess(actual_address, kSuperPageSize, PageInaccessible);
-#endif
+#endif  // defined(PA_ALLOW_PCSCAN)
 
 #if BUILDFLAG(ENABLE_BRP_DIRECTMAP_SUPPORT)
   // Allocate the BRP pool offset table in the BRP pool.
diff --git a/base/allocator/partition_allocator/partition_alloc_config.h b/base/allocator/partition_allocator/partition_alloc_config.h
index cec5ff2..c5ccd81 100644
--- a/base/allocator/partition_allocator/partition_alloc_config.h
+++ b/base/allocator/partition_allocator/partition_alloc_config.h
@@ -23,9 +23,7 @@
 // BackupRefPtr and PCScan are incompatible, and due to its conservative nature,
 // it is 64 bits only.
 #if defined(PA_HAS_64_BITS_POINTERS) && !BUILDFLAG(USE_BACKUP_REF_PTR)
-#define PA_ALLOW_PCSCAN 1
-#else
-#define PA_ALLOW_PCSCAN 0
+#define PA_ALLOW_PCSCAN
 #endif
 
 #if defined(PA_HAS_64_BITS_POINTERS) && \
@@ -55,7 +53,9 @@
 // This applies only to normal buckets, as direct-map allocations are always
 // decommitted.
 // TODO(bartekn): Re-enable once PartitionAlloc-Everywhere evaluation is done.
-#define PA_ZERO_RANDOMLY_ON_FREE 0
+#if 0
+#define PA_ZERO_RANDOMLY_ON_FREE
+#endif
 
 // Need TLS support.
 #if defined(OS_POSIX) || defined(OS_WIN)
@@ -87,6 +87,8 @@
 #endif
 
 // Specifies whether allocation extras need to be added.
-#define PA_EXTRAS_REQUIRED (DCHECK_IS_ON() || BUILDFLAG(USE_BACKUP_REF_PTR))
+#if DCHECK_IS_ON() || BUILDFLAG(USE_BACKUP_REF_PTR)
+#define PA_EXTRAS_REQUIRED
+#endif
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_CONFIG_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_features.cc b/base/allocator/partition_allocator/partition_alloc_features.cc
index 00c093a34..6b9473f 100644
--- a/base/allocator/partition_allocator/partition_alloc_features.cc
+++ b/base/allocator/partition_allocator/partition_alloc_features.cc
@@ -9,12 +9,12 @@
 namespace base {
 namespace features {
 
-#if PA_ALLOW_PCSCAN
+#if defined(PA_ALLOW_PCSCAN)
 // If enabled, PCScan is turned on by default for all partitions that don't
 // disable it explicitly.
 const Feature kPartitionAllocPCScan{"PartitionAllocPCScan",
                                     FEATURE_DISABLED_BY_DEFAULT};
-#endif  // PA_ALLOW_PCSCAN
+#endif  // defined(PA_ALLOW_PCSCAN)
 
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 // If enabled, PCScan is turned on only for the browser's malloc partition.
diff --git a/base/allocator/partition_allocator/partition_alloc_features.h b/base/allocator/partition_allocator/partition_alloc_features.h
index baff1f55..7171ebf 100644
--- a/base/allocator/partition_allocator/partition_alloc_features.h
+++ b/base/allocator/partition_allocator/partition_alloc_features.h
@@ -16,9 +16,9 @@
 
 namespace features {
 
-#if PA_ALLOW_PCSCAN
+#if defined(PA_ALLOW_PCSCAN)
 extern const BASE_EXPORT Feature kPartitionAllocPCScan;
-#endif  // PA_ALLOW_PCSCAN
+#endif  // defined(PA_ALLOW_PCSCAN)
 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 extern const BASE_EXPORT Feature kPartitionAllocPCScanBrowserOnly;
 extern const BASE_EXPORT Feature kPartitionAllocBackupRefPtrControl;
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index 84736ab..5377e0e 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -494,7 +494,7 @@
     PA_DCHECK(!allow_aligned_alloc || !allow_ref_count);
 #endif
 
-#if PA_EXTRAS_REQUIRED
+#if defined(PA_EXTRAS_REQUIRED)
     extras_size = 0;
     extras_offset = 0;
 
@@ -510,7 +510,7 @@
       extras_size += internal::kPartitionRefCountSizeAdjustment;
       extras_offset += internal::kPartitionRefCountOffsetAdjustment;
     }
-#endif
+#endif  //  defined(PA_EXTRAS_REQUIRED)
 
     // Re-confirm the above PA_CHECKs, by making sure there are no
     // pre-allocation extras when AlignedAlloc is allowed. Post-allocation
@@ -518,13 +518,13 @@
     PA_CHECK(!allow_aligned_alloc || !extras_offset);
 
     quarantine_mode =
-#if PA_ALLOW_PCSCAN
+#if defined(PA_ALLOW_PCSCAN)
         (opts.quarantine == PartitionOptions::Quarantine::kDisallowed
              ? QuarantineMode::kAlwaysDisabled
              : QuarantineMode::kDisabledByDefault);
 #else
         QuarantineMode::kAlwaysDisabled;
-#endif
+#endif  // defined(PA_ALLOW_PCSCAN)
 
     // We mark the sentinel slot span as free to make sure it is skipped by our
     // logic to find a new active slot span.
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index b210eb7..e6ae008 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -191,14 +191,14 @@
   static constexpr bool never_used_lazy_commit = true;
 #endif
 
-#if !PA_EXTRAS_REQUIRED
+#if !defined(PA_EXTRAS_REQUIRED)
   // Teach the compiler that code can be optimized in builds that use no extras.
   static constexpr uint32_t extras_size = 0;
   static constexpr uint32_t extras_offset = 0;
 #else
   uint32_t extras_size;
   uint32_t extras_offset;
-#endif
+#endif  // !defined(PA_EXTRAS_REQUIRED)
 
   // Not used on the fastest path (thread cache allocations), but on the fast
   // path of the central allocator.
@@ -1094,7 +1094,7 @@
   // Note: ref-count and cookies can be 0-sized.
   //
   // For more context, see the other "Layout inside the slot" comment below.
-#if EXPENSIVE_DCHECKS_ARE_ON() || PA_ZERO_RANDOMLY_ON_FREE
+#if EXPENSIVE_DCHECKS_ARE_ON() || defined(PA_ZERO_RANDOMLY_ON_FREE)
   const size_t utilized_slot_size = slot_span->GetUtilizedSlotSize();
 #endif
 #if BUILDFLAG(USE_BACKUP_REF_PTR) || DCHECK_IS_ON()
@@ -1140,7 +1140,7 @@
              - sizeof(internal::PartitionRefCount)
 #endif
   );
-#elif PA_ZERO_RANDOMLY_ON_FREE
+#elif defined(PA_ZERO_RANDOMLY_ON_FREE)
   // `memset` only once in a while: we're trading off safety for time
   // efficiency.
   if (UNLIKELY(internal::RandomPeriod()) &&
@@ -1152,7 +1152,7 @@
 #endif
     );
   }
-#endif
+#endif  // defined(PA_ZERO_RANDOMLY_ON_FREE)
 
   RawFreeWithThreadCache(slot_start, slot_span);
 }
diff --git a/base/allocator/partition_allocator/starscan/pcscan_unittest.cc b/base/allocator/partition_allocator/starscan/pcscan_unittest.cc
index 9b788f6..a196271b 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_unittest.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_unittest.cc
@@ -2,20 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/allocator/partition_allocator/partition_alloc_constants.h"
-#include "base/allocator/partition_allocator/partition_root.h"
 #if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
 
 #include "base/allocator/partition_allocator/starscan/pcscan.h"
 
 #include "base/allocator/partition_allocator/partition_alloc.h"
+#include "base/allocator/partition_allocator/partition_alloc_constants.h"
 #include "base/allocator/partition_allocator/partition_alloc_features.h"
+#include "base/allocator/partition_allocator/partition_root.h"
 #include "base/allocator/partition_allocator/starscan/stack/stack.h"
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if PA_ALLOW_PCSCAN
+#if defined(PA_ALLOW_PCSCAN)
 
 namespace base {
 namespace internal {
@@ -608,5 +608,5 @@
 }  // namespace internal
 }  // namespace base
 
-#endif  // PA_ALLOW_PCSCAN
+#endif  // defined(PA_ALLOW_PCSCAN)
 #endif  // defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
diff --git a/base/android/jank_metric_uma_recorder.cc b/base/android/jank_metric_uma_recorder.cc
index bc300c2..32035307 100644
--- a/base/android/jank_metric_uma_recorder.cc
+++ b/base/android/jank_metric_uma_recorder.cc
@@ -8,9 +8,11 @@
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
 #include "base/base_jni_headers/JankMetricUMARecorder_jni.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/strcat.h"
 #include "base/time/time.h"
 
 namespace base {
@@ -22,36 +24,47 @@
 // testing.
 void JNI_JankMetricUMARecorder_RecordJankMetrics(
     JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& java_scenario_name,
     const base::android::JavaParamRef<jlongArray>& java_durations_ns,
     const base::android::JavaParamRef<jlongArray>& java_jank_bursts_ns,
     jint java_missed_frames) {
-  RecordJankMetrics(env, java_durations_ns, java_jank_bursts_ns,
-                    java_missed_frames);
+  RecordJankMetrics(env, java_scenario_name, java_durations_ns,
+                    java_jank_bursts_ns, java_missed_frames);
 }
 
 void RecordJankMetrics(
     JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& java_scenario_name,
     const base::android::JavaParamRef<jlongArray>& java_durations_ns,
     const base::android::JavaParamRef<jlongArray>& java_jank_bursts_ns,
     jint java_missed_frames) {
+  std::string scenario_name = ConvertJavaStringToUTF8(env, java_scenario_name);
   std::vector<int64_t> durations_ns;
   std::vector<int64_t> jank_bursts_ns;
 
   JavaLongArrayToInt64Vector(env, java_durations_ns, &durations_ns);
   JavaLongArrayToInt64Vector(env, java_jank_bursts_ns, &jank_bursts_ns);
 
+  std::string frame_duration_histogram_name =
+      base::StrCat({"Android.Jank.FrameDuration.", scenario_name});
+  std::string jank_burst_histogram_name =
+      base::StrCat({"Android.Jank.JankBursts.", scenario_name});
+  std::string missed_frames_histogram_name =
+      base::StrCat({"Android.Jank.MissedFrames.", scenario_name});
+
   for (const int64_t frame_duration_ns : durations_ns) {
-    UMA_HISTOGRAM_TIMES("Android.Jank.FrameDuration",
+    UMA_HISTOGRAM_TIMES(frame_duration_histogram_name,
                         base::TimeDelta::FromNanoseconds(frame_duration_ns));
   }
 
   for (const int64_t jank_burst_duration_ns : jank_bursts_ns) {
     UMA_HISTOGRAM_TIMES(
-        "Android.Jank.JankBursts",
+        jank_burst_histogram_name,
         base::TimeDelta::FromNanoseconds(jank_burst_duration_ns));
   }
 
-  base::UmaHistogramCounts1000("Android.Jank.MissedFrames", java_missed_frames);
+  base::UmaHistogramCounts1000(missed_frames_histogram_name,
+                               java_missed_frames);
 }
 
 }  // namespace android
diff --git a/base/android/jank_metric_uma_recorder.h b/base/android/jank_metric_uma_recorder.h
index d2e0f2a2..71d87956 100644
--- a/base/android/jank_metric_uma_recorder.h
+++ b/base/android/jank_metric_uma_recorder.h
@@ -5,10 +5,6 @@
 #ifndef BASE_ANDROID_JANK_METRIC_UMA_RECORDER_H_
 #define BASE_ANDROID_JANK_METRIC_UMA_RECORDER_H_
 
-#include <stdint.h>
-
-#include <memory>
-
 #include "base/android/jni_android.h"
 #include "base/base_export.h"
 
@@ -17,6 +13,7 @@
 
 BASE_EXPORT void RecordJankMetrics(
     JNIEnv* env,
+    const base::android::JavaParamRef<jstring>& java_scenario_name,
     const base::android::JavaParamRef<jlongArray>& java_durations_ns,
     const base::android::JavaParamRef<jlongArray>& java_jank_bursts_ns,
     jint java_missed_frames);
diff --git a/base/android/jank_metric_uma_recorder_unittest.cc b/base/android/jank_metric_uma_recorder_unittest.cc
index 2a57cab5..92a9e65 100644
--- a/base/android/jank_metric_uma_recorder_unittest.cc
+++ b/base/android/jank_metric_uma_recorder_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
 #include "base/metrics/histogram.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -22,9 +23,9 @@
 namespace android {
 namespace {
 
-jlongArray GenerateJavaLongArrayParamRef(JNIEnv* env,
-                                         const int64_t long_array[],
-                                         const size_t array_length) {
+jlongArray GenerateJavaLongArray(JNIEnv* env,
+                                 const int64_t long_array[],
+                                 const size_t array_length) {
   ScopedJavaLocalRef<jlongArray> java_long_array =
       ToJavaLongArray(env, long_array, array_length);
 
@@ -59,27 +60,34 @@
 
   JNIEnv* env = AttachCurrentThread();
 
+  jstring java_scenario_name =
+      ConvertUTF8ToJavaString(env, "PeriodicReporting").Release();
   jlongArray java_durations =
-      GenerateJavaLongArrayParamRef(env, kDurations, kDurationsLen);
+      GenerateJavaLongArray(env, kDurations, kDurationsLen);
   jlongArray java_jank_bursts =
-      GenerateJavaLongArrayParamRef(env, kJankBursts, kJankBurstsLen);
+      GenerateJavaLongArray(env, kJankBursts, kJankBurstsLen);
 
   RecordJankMetrics(
       env,
+      /* java_scenario_name= */
+      base::android::JavaParamRef<jstring>(env, java_scenario_name),
       /* java_durations_ns= */
       base::android::JavaParamRef<jlongArray>(env, java_durations),
       /* java_jank_bursts_ns=*/
       base::android::JavaParamRef<jlongArray>(env, java_jank_bursts),
       /* java_missed_frames = */ 2);
 
-  EXPECT_THAT(histogram_tester.GetAllSamples("Android.Jank.FrameDuration"),
+  EXPECT_THAT(histogram_tester.GetAllSamples(
+                  "Android.Jank.FrameDuration.PeriodicReporting"),
               ElementsAre(Bucket(1, 3), Bucket(2, 1), Bucket(10, 1),
                           Bucket(20, 1), Bucket(29, 1), Bucket(57, 1)));
 
-  EXPECT_THAT(histogram_tester.GetAllSamples("Android.Jank.JankBursts"),
+  EXPECT_THAT(histogram_tester.GetAllSamples(
+                  "Android.Jank.JankBursts.PeriodicReporting"),
               ElementsAre(Bucket(20, 1), Bucket(96, 1)));
 
-  histogram_tester.ExpectUniqueSample("Android.Jank.MissedFrames", 2, 1);
+  histogram_tester.ExpectUniqueSample(
+      "Android.Jank.MissedFrames.PeriodicReporting", 2, 1);
 }
 
 }  // namespace android
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/FrameMetrics.java b/base/android/java/src/org/chromium/base/jank_tracker/FrameMetrics.java
new file mode 100644
index 0000000..3f111a9
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/jank_tracker/FrameMetrics.java
@@ -0,0 +1,26 @@
+// Copyright 2021 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.jank_tracker;
+
+/**
+ * Container for individual frame metrics as reported by Android's FrameMetrics API.
+ */
+class FrameMetrics {
+    public final Long[] timestampsNs;
+    public final Long[] durationsNs;
+    public final Integer[] skippedFrames;
+
+    public FrameMetrics(Long[] timestampsNs, Long[] durationsNs, Integer[] skippedFrames) {
+        this.timestampsNs = timestampsNs;
+        this.durationsNs = durationsNs;
+        this.skippedFrames = skippedFrames;
+    }
+
+    public FrameMetrics() {
+        this.timestampsNs = new Long[0];
+        this.durationsNs = new Long[0];
+        this.skippedFrames = new Integer[0];
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/JankFrameMetricsListener.java b/base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsListener.java
similarity index 61%
rename from base/android/java/src/org/chromium/base/jank_tracker/JankFrameMetricsListener.java
rename to base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsListener.java
index 579b9e14..0576f5f1 100644
--- a/base/android/java/src/org/chromium/base/jank_tracker/JankFrameMetricsListener.java
+++ b/base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsListener.java
@@ -14,24 +14,24 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * This class receives FrameMetrics callbacks and records frame durations in a JankMetricMeasurement
- * instance. Recording can be toggled from any thread, but the actual recording occurs in the thread
- * specified when this is attached to a window with addOnFrameMetricsAvailableListener(). This class
- * only adds data to the provided JankMetricMeasurement instance, its owner must clear it to avoid
- * OOMs.
+ * This class receives  OnFrameMetricsAvailableListener.onFrameMetricsAvailable() callbacks and
+ * records frame durations in a FrameMetricsStore instance. Recording can be toggled from any
+ * thread, but the actual recording occurs in the thread specified when this is attached to a window
+ * with addOnFrameMetricsAvailableListener(). This class only adds data to the provided
+ * FrameMetricsStore instance, its owner must clear it to avoid OOMs.
  */
 @RequiresApi(api = VERSION_CODES.N)
-class JankFrameMetricsListener implements OnFrameMetricsAvailableListener {
-    private final JankMetricMeasurement mMeasurement;
+class FrameMetricsListener implements OnFrameMetricsAvailableListener {
+    private final FrameMetricsStore mFrameMetricsStore;
     private final AtomicBoolean mIsRecording;
 
-    JankFrameMetricsListener(JankMetricMeasurement measurement) {
-        mMeasurement = measurement;
+    FrameMetricsListener(FrameMetricsStore frameMetricsStore) {
+        mFrameMetricsStore = frameMetricsStore;
         mIsRecording = new AtomicBoolean(false);
     }
 
     /**
-     * Toggles recording into JankMetricMeasurement, can be invoked from any thread.
+     * Toggles recording into JankMetricsStore, can be invoked from any thread.
      * @param isRecording
      */
     public void setIsListenerRecording(boolean isRecording) {
@@ -50,7 +50,7 @@
 
         long timestampNs = frameMetrics.getMetric(FrameMetrics.VSYNC_TIMESTAMP);
 
-        mMeasurement.addFrameMeasurement(
+        mFrameMetricsStore.addFrameMeasurement(
                 timestampNs, frameTotalDurationNs, dropCountSinceLastInvocation);
     }
 }
\ No newline at end of file
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsStore.java b/base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsStore.java
new file mode 100644
index 0000000..a9f8f28
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/jank_tracker/FrameMetricsStore.java
@@ -0,0 +1,192 @@
+// Copyright 2021 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.jank_tracker;
+
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.build.BuildConfig;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * This class stores timestamps and frame durations related to different jank scenarios (e.g.
+ * Opening omnibox, Opening tab switcher). Scenarios are tracked by calling {@Link
+ * #startTrackingScenario} and {@Link #stopTrackingScenario}. {@Link #stopTrackingScenario} returns
+ * a FrameMetrics object with the timestamp and duration of all frames drawn between the start and
+ * end of the specified scenario.
+ *
+ * Multiple scenarios can be tracked simultaneously without duplicating data, and frame data is
+ * cleared as needed when scenarios end.
+ */
+class FrameMetricsStore {
+    // Guards access to the fields below.
+    private final Object mLock = new Object();
+
+    // Array of timestamps stored in nanoseconds, they represent the moment when each frame
+    // finished drawing, must always be the same size as mTotalDurationsNs.
+    @GuardedBy("mLock")
+    private final ArrayList<Long> mTimestampsNs = new ArrayList<>();
+
+    // Array of total durations stored in nanoseconds, they represent how long each frame took to
+    // draw, must always be the same size as mTimestampsNs.
+    @GuardedBy("mLock")
+    private final ArrayList<Long> mTotalDurationsNs = new ArrayList<>();
+
+    // Number of frames that FrameMetrics was unable to report on, due to excessive activity on its
+    // handler thread.
+    @GuardedBy("mLock")
+    private final ArrayList<Integer> mSkippedFrames = new ArrayList<>();
+
+    // Map of jank scenarios being currently tracked and the timestamp of the frame before tracking
+    // began. Each key corresponds to a JankScenario and each value corresponds to a timestamp
+    // present in mTimestampsNs, or 0 in case the scenario tracking started before any frames were
+    // recorded. If empty then no scenarios are being tracked, so calls to addFrameMeasurement won't
+    // store anything.
+    @GuardedBy("mLock")
+    private final HashMap<Integer, Long> mScenarioPreviousFrameTimestampNs = new HashMap<>();
+
+    /**
+     * Records a timestamp and total draw duration for a single frame.
+     */
+    void addFrameMeasurement(long timestampNs, long totalDurationNs, int skippedFrames) {
+        synchronized (mLock) {
+            if (mScenarioPreviousFrameTimestampNs.isEmpty()) {
+                return;
+            }
+
+            mTimestampsNs.add(timestampNs);
+            mTotalDurationsNs.add(totalDurationNs);
+            mSkippedFrames.add(skippedFrames);
+        }
+    }
+
+    void startTrackingScenario(@JankScenario int scenario) {
+        synchronized (mLock) {
+            // Ignore multiple calls to startTrackingScenario without corresponding
+            // stopTrackingScenario calls.
+            if (mScenarioPreviousFrameTimestampNs.containsKey(scenario)) {
+                return;
+            }
+
+            // Scenarios are tracked based on the latest stored timestamp to allow fast lookups
+            // (find index of [timestamp] vs find first index that's >= [timestamp]). In case there
+            // are no stored timestamps then we hardcode the scenario's starting timestamp to 0L,
+            // this is handled as a special case in stopTrackingScenario by returning all stored
+            // frames.
+            Long startingTimestamp = 0L;
+            if (!mTimestampsNs.isEmpty()) {
+                startingTimestamp = mTimestampsNs.get(mTimestampsNs.size() - 1);
+            }
+
+            mScenarioPreviousFrameTimestampNs.put(scenario, startingTimestamp);
+        }
+    }
+
+    FrameMetrics stopTrackingScenario(@JankScenario int scenario) {
+        synchronized (mLock) {
+            // Get the timestamp of the latest frame before startTrackingScenario was called. This
+            // can be null if tracking never started for scenario, or 0L if tracking started when no
+            // frames were stored.
+            Long previousFrameTimestamp = mScenarioPreviousFrameTimestampNs.remove(scenario);
+
+            // If stopTrackingScenario is called without a corresponding startTrackingScenario then
+            // return an empty FrameMetrics object.
+            if (previousFrameTimestamp == null) {
+                return new FrameMetrics();
+            }
+
+            int startingIndex;
+            // Starting timestamp may be 0 if a scenario starts without any frames stored, in this
+            // case return all frames.
+            if (previousFrameTimestamp == 0) {
+                startingIndex = 0;
+            } else {
+                startingIndex = mTimestampsNs.indexOf(previousFrameTimestamp);
+                // The scenario starts with the frame after the tracking timestamp.
+                startingIndex++;
+
+                // If startingIndex is out of bounds then we haven't recorded any frames since
+                // tracking started, return an empty FrameMetrics object.
+                if (startingIndex >= mTimestampsNs.size()) {
+                    return new FrameMetrics();
+                }
+            }
+
+            // Ending index is exclusive, so this is not out of bounds.
+            int endingIndex = mTimestampsNs.size();
+            int scenarioFrameCount = endingIndex - startingIndex;
+
+            Long[] timestamps = mTimestampsNs.subList(startingIndex, endingIndex)
+                                        .toArray(new Long[scenarioFrameCount]);
+            Long[] durations = mTotalDurationsNs.subList(startingIndex, endingIndex)
+                                       .toArray(new Long[scenarioFrameCount]);
+            Integer[] skippedFrames = mSkippedFrames.subList(startingIndex, endingIndex)
+                                              .toArray(new Integer[scenarioFrameCount]);
+
+            FrameMetrics frameMetrics = new FrameMetrics(timestamps, durations, skippedFrames);
+            removeUnusedFrames();
+
+            return frameMetrics;
+        }
+    }
+
+    @VisibleForTesting
+    FrameMetrics getAllStoredMetricsForTesting() {
+        synchronized (mLock) {
+            Long[] timestamps = mTimestampsNs.toArray(new Long[mTimestampsNs.size()]);
+            Long[] durations = mTotalDurationsNs.toArray(new Long[mTotalDurationsNs.size()]);
+            Integer[] skippedFrames = mSkippedFrames.toArray(new Integer[mSkippedFrames.size()]);
+
+            FrameMetrics frameMetrics = new FrameMetrics(timestamps, durations, skippedFrames);
+
+            return frameMetrics;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void removeUnusedFrames() {
+        if (mScenarioPreviousFrameTimestampNs.isEmpty()) {
+            mTimestampsNs.clear();
+            mTotalDurationsNs.clear();
+            mSkippedFrames.clear();
+            return;
+        }
+
+        long firstUsedTimestamp = findFirstUsedTimestamp();
+        // If the earliest timestamp tracked is 0 then that scenario contains every frame
+        // stored, so we shouldn't delete anything.
+        if (firstUsedTimestamp == 0L) {
+            return;
+        }
+
+        int firstUsedIndex = mTimestampsNs.indexOf(firstUsedTimestamp);
+        if (firstUsedIndex == -1) {
+            if (BuildConfig.ENABLE_ASSERTS) {
+                throw new IllegalStateException("Timestamp for tracked scenario not found");
+            }
+            // This shouldn't happen.
+            return;
+        }
+
+        mTimestampsNs.subList(0, firstUsedIndex).clear();
+        mTotalDurationsNs.subList(0, firstUsedIndex).clear();
+        mSkippedFrames.subList(0, firstUsedIndex).clear();
+    }
+
+    @GuardedBy("mLock")
+    private long findFirstUsedTimestamp() {
+        long firstTimestamp = Long.MAX_VALUE;
+        for (long timestamp : mScenarioPreviousFrameTimestampNs.values()) {
+            if (timestamp < firstTimestamp) {
+                firstTimestamp = timestamp;
+            }
+        }
+
+        return firstTimestamp;
+    }
+}
\ No newline at end of file
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/JankActivityTracker.java b/base/android/java/src/org/chromium/base/jank_tracker/JankActivityTracker.java
index 5c77f90..c4f988d 100644
--- a/base/android/java/src/org/chromium/base/jank_tracker/JankActivityTracker.java
+++ b/base/android/java/src/org/chromium/base/jank_tracker/JankActivityTracker.java
@@ -6,74 +6,40 @@
 
 import android.app.Activity;
 import android.os.Build.VERSION_CODES;
-import android.os.Handler;
-import android.os.HandlerThread;
 
-import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ApplicationStatus.ActivityStateListener;
 import org.chromium.base.ThreadUtils.ThreadChecker;
-import org.chromium.base.library_loader.LibraryLoader;
 import org.chromium.base.lifetime.DestroyChecker;
 
 import java.lang.ref.WeakReference;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * This class takes an activity, listens and records FrameMetrics in a JankMetricMeasurement
- * instance. A new HandlerThread is started to listen to FrameMetric events.
- * In addition it starts/stops recording based on activity lifecycle.
+ * This class takes an Activity and attaches a FrameMetricsListener to it, in addition it controls
+ * periodic jank metric reporting and frame metric recording based on the Activity's lifecycle
+ * events.
  */
 @RequiresApi(api = VERSION_CODES.N)
 class JankActivityTracker implements ActivityStateListener {
-    private static final long METRIC_DELAY_MS = 30_000;
-
-    static JankActivityTracker create(Activity context) {
-        JankMetricMeasurement measurement = new JankMetricMeasurement();
-        JankFrameMetricsListener listener = new JankFrameMetricsListener(measurement);
-        return new JankActivityTracker(context, listener, measurement);
-    }
-
-    private final JankFrameMetricsListener mFrameMetricsListener;
-    private final JankMetricMeasurement mMeasurement;
-    private final AtomicBoolean mIsMetricReporterLooping = new AtomicBoolean(false);
+    private final FrameMetricsListener mFrameMetricsListener;
+    private final JankReportingScheduler mReportingScheduler;
     private final ThreadChecker mThreadChecker = new ThreadChecker();
     private final DestroyChecker mDestroyChecker = new DestroyChecker();
 
-    private final Runnable mMetricReporter = new Runnable() {
-        @Override
-        public void run() {
-            if (LibraryLoader.getInstance().isInitialized()) {
-                JankMetricUMARecorder.recordJankMetricsToUMA(mMeasurement.getMetrics());
-            }
-            // TODO(salg@): Cache metrics in case native takes >30s to initialize.
-            mMeasurement.clear();
-
-            if (mIsMetricReporterLooping.get()) {
-                getOrCreateHandler().postDelayed(mMetricReporter, METRIC_DELAY_MS);
-            }
-        }
-    };
-
-    @Nullable
-    protected HandlerThread mHandlerThread;
-    @Nullable
-    private Handler mHandler;
     private WeakReference<Activity> mActivityReference;
 
-    JankActivityTracker(Activity context, JankFrameMetricsListener listener,
-            JankMetricMeasurement measurement) {
+    JankActivityTracker(Activity context, FrameMetricsListener listener,
+            JankReportingScheduler reportingScheduler) {
         mActivityReference = new WeakReference<>(context);
         mFrameMetricsListener = listener;
-        mMeasurement = measurement;
+        mReportingScheduler = reportingScheduler;
     }
 
     void initialize() {
-        mThreadChecker.assertOnValidThread();
-        mDestroyChecker.checkNotDestroyed();
+        assertValidState();
         Activity activity = mActivityReference.get();
         if (activity != null) {
             ApplicationStatus.registerStateListenerForActivity(this, activity);
@@ -81,13 +47,12 @@
             int activityState = ApplicationStatus.getStateForActivity(activity);
             onActivityStateChange(activity, activityState);
             activity.getWindow().addOnFrameMetricsAvailableListener(
-                    mFrameMetricsListener, getOrCreateHandler());
+                    mFrameMetricsListener, mReportingScheduler.getOrCreateHandler());
         }
     }
 
     void destroy() {
         mThreadChecker.assertOnValidThread();
-        mDestroyChecker.destroy();
         ApplicationStatus.unregisterActivityStateListener(this);
         stopMetricRecording();
         stopReportingTimer();
@@ -95,51 +60,37 @@
         if (activity != null) {
             activity.getWindow().removeOnFrameMetricsAvailableListener(mFrameMetricsListener);
         }
-    }
-
-    protected Handler getOrCreateHandler() {
-        // TODO(salg): Sort out whether thread assertion should be happening here as well.
-        mDestroyChecker.checkNotDestroyed();
-        if (mHandler == null) {
-            mHandlerThread = new HandlerThread("Jank-Tracker");
-            mHandlerThread.start();
-            mHandler = new Handler(mHandlerThread.getLooper());
-        }
-        return mHandler;
+        mDestroyChecker.destroy();
     }
 
     private void startReportingTimer() {
-        // If mIsMetricReporterLooping was already true then there's no need to post another task.
-        if (mIsMetricReporterLooping.getAndSet(true)) {
-            return;
-        }
-        getOrCreateHandler().postDelayed(mMetricReporter, METRIC_DELAY_MS);
+        assertValidState();
+        mReportingScheduler.startReportingPeriodicMetrics();
     }
 
     private void stopReportingTimer() {
-        if (!mIsMetricReporterLooping.get()) {
-            return;
-        }
-        // Remove any existing mMetricReporter delayed tasks.
-        getOrCreateHandler().removeCallbacks(mMetricReporter);
-        // Disable mMetricReporter looping.
-        mIsMetricReporterLooping.set(false);
-        // Run mMetricReporter one last time immediately.
-        getOrCreateHandler().post(mMetricReporter);
+        assertValidState();
+        mReportingScheduler.stopReportingPeriodicMetrics();
     }
 
     private void startMetricRecording() {
+        assertValidState();
         mFrameMetricsListener.setIsListenerRecording(true);
     }
 
     private void stopMetricRecording() {
+        assertValidState();
         mFrameMetricsListener.setIsListenerRecording(false);
     }
 
+    private void assertValidState() {
+        mThreadChecker.assertOnValidThread();
+        mDestroyChecker.checkNotDestroyed();
+    }
+
     @Override
     public void onActivityStateChange(Activity activity, @ActivityState int newState) {
-        mThreadChecker.assertOnValidThread();
-        mDestroyChecker.checkNotDestroyed();
+        assertValidState();
         switch (newState) {
             case ActivityState.STARTED: // Intentional fallthrough.
             case ActivityState.RESUMED:
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/JankMetricMeasurement.java b/base/android/java/src/org/chromium/base/jank_tracker/JankMetricCalculator.java
similarity index 66%
rename from base/android/java/src/org/chromium/base/jank_tracker/JankMetricMeasurement.java
rename to base/android/java/src/org/chromium/base/jank_tracker/JankMetricCalculator.java
index 5340d3a..3c4577f9 100644
--- a/base/android/java/src/org/chromium/base/jank_tracker/JankMetricMeasurement.java
+++ b/base/android/java/src/org/chromium/base/jank_tracker/JankMetricCalculator.java
@@ -6,10 +6,8 @@
 
 import java.util.ArrayList;
 
-import javax.annotation.concurrent.GuardedBy;
-
 /**
- * This class records frame durations and timestamps. And it calculates a jank burst metric.
+ * This class calculates a jank burst metric.
  *
  * Jank bursts are periods with janky frames in a quick succession. A non-janky frame may be
  * included in this measurement if it's preceded and succeeded by janky frames, as long as the three
@@ -39,56 +37,7 @@
  *  In this example the jank burst metric would report 3 values, which are the sum of the frame
  *  duration of all frames inside a jank burst, including the first and last frames.
  */
-class JankMetricMeasurement {
-    protected static class JankMetric {
-        private final long[] mTimestampsNs;
-        private final long[] mDurationsNs;
-        private final long[] mJankBurstsNs;
-        private final int mSkippedFrames;
-
-        public JankMetric(
-                long[] timestampsNs, long[] durationsNs, long[] jankBurstsNs, int skippedFrames) {
-            mTimestampsNs = timestampsNs;
-            mDurationsNs = durationsNs;
-            mJankBurstsNs = jankBurstsNs;
-            mSkippedFrames = skippedFrames;
-        }
-
-        public long[] getTimestamps() {
-            return mTimestampsNs;
-        }
-
-        public long[] getDurations() {
-            return mDurationsNs;
-        }
-
-        public long[] getJankBursts() {
-            return mJankBurstsNs;
-        }
-
-        public int getSkippedFrames() {
-            return mSkippedFrames;
-        }
-    }
-
-    // Guards access to the fields below.
-    private final Object mLock = new Object();
-
-    // Array of timestamps stored in nanoseconds, they represent the moment when each frame
-    // finished drawing, must always be the same size as mTotalDurationsNs.
-    @GuardedBy("mLock")
-    private final ArrayList<Long> mTimestampsNs;
-
-    // Array of total durations stored in nanoseconds, they represent how long each frame took to
-    // draw, must always be the same size as mTimestampsNs.
-    @GuardedBy("mLock")
-    private final ArrayList<Long> mTotalDurationsNs;
-
-    // Number of frames that FrameMetrics was unable to report on, due to excessive activity on its
-    // handler thread.
-    @GuardedBy("mLock")
-    private int mSkippedFrames;
-
+class JankMetricCalculator {
     private static final long NANOSECONDS_PER_MILLISECOND = 1_000_000;
 
     // Threshold in milliseconds to distinguish janky and non-janky frames. Any frames whose
@@ -101,24 +50,6 @@
     private static final long JANK_BURST_CONSECUTIVE_FRAME_THRESHOLD_NS =
             50 * NANOSECONDS_PER_MILLISECOND;
 
-    JankMetricMeasurement() {
-        mTimestampsNs = new ArrayList<>();
-        mTotalDurationsNs = new ArrayList<>();
-        mSkippedFrames = 0;
-    }
-
-    /**
-     *  Records a timestamp and total draw duration for a single frame, both values are in
-     * milliseconds.
-     */
-    void addFrameMeasurement(long timestampNs, long totalDurationNs, int skippedFrames) {
-        synchronized (mLock) {
-            mTimestampsNs.add(timestampNs);
-            mTotalDurationsNs.add(totalDurationNs);
-            mSkippedFrames += skippedFrames;
-        }
-    }
-
     /**
      *  Returns an array of all recorded jank bursts measured in nanoseconds (see class doc for
      * details).
@@ -166,47 +97,26 @@
             jankBurstDurationsNs.add(currentJankBurstDurationNs);
         }
 
-        return longArrayListToPrimitiveArray(jankBurstDurationsNs);
+        return longArrayToPrimitiveArray(
+                jankBurstDurationsNs.toArray(new Long[jankBurstDurationsNs.size()]));
     }
 
     /**
      *  Returns a new object with metrics for all frames recorded since started or clear() was
      * called.
      */
-    JankMetric getMetrics() {
+    static JankMetrics calculateJankMetrics(FrameMetrics frameMetrics) {
         long[] timestampsNs;
         long[] totalDurationsNs;
         int skippedFrames;
 
-        synchronized (mLock) {
-            timestampsNs = longArrayListToPrimitiveArray(mTimestampsNs);
-            totalDurationsNs = longArrayListToPrimitiveArray(mTotalDurationsNs);
-            skippedFrames = mSkippedFrames;
-        }
+        timestampsNs = longArrayToPrimitiveArray(frameMetrics.timestampsNs);
+        totalDurationsNs = longArrayToPrimitiveArray(frameMetrics.durationsNs);
+        skippedFrames = sumArray(frameMetrics.skippedFrames);
 
         long[] jankBursts = calculateJankBurstDurationsNs(timestampsNs, totalDurationsNs);
 
-        return new JankMetric(timestampsNs, totalDurationsNs, jankBursts, skippedFrames);
-    }
-
-    private static long[] longArrayListToPrimitiveArray(ArrayList<Long> longArrayList) {
-        long[] longArray = new long[longArrayList.size()];
-        for (int i = 0; i < longArrayList.size(); i++) {
-            longArray[i] = longArrayList.get(i).longValue();
-        }
-
-        return longArray;
-    }
-
-    /**
-     * Clears this measurement.
-     */
-    void clear() {
-        synchronized (mLock) {
-            mTimestampsNs.clear();
-            mTotalDurationsNs.clear();
-            mSkippedFrames = 0;
-        }
+        return new JankMetrics(timestampsNs, totalDurationsNs, jankBursts, skippedFrames);
     }
 
     /**
@@ -248,4 +158,22 @@
 
         return (timeBetweenFramesNs < JANK_BURST_CONSECUTIVE_FRAME_THRESHOLD_NS);
     }
+
+    private static long[] longArrayToPrimitiveArray(Long[] longArray) {
+        long[] primitiveArray = new long[longArray.length];
+        for (int i = 0; i < longArray.length; i++) {
+            primitiveArray[i] = longArray[i].longValue();
+        }
+
+        return primitiveArray;
+    }
+
+    private static int sumArray(Integer[] input) {
+        int sum = 0;
+        for (Integer i : input) {
+            sum += i;
+        }
+
+        return sum;
+    }
 }
\ No newline at end of file
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java b/base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java
index 9dcf66d2..d541b8d4 100644
--- a/base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java
+++ b/base/android/java/src/org/chromium/base/jank_tracker/JankMetricUMARecorder.java
@@ -6,24 +6,45 @@
 
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.base.jank_tracker.JankMetricMeasurement.JankMetric;
 
 /**
  * Sends Android jank metrics to native to be recorded using UMA.
  */
 @JNINamespace("base::android")
 public class JankMetricUMARecorder {
-    public static void recordJankMetricsToUMA(JankMetric metric) {
+    public static void recordJankMetricsToUMA(JankMetrics metric, @JankScenario int scenario) {
         if (metric == null) {
             return;
         }
 
-        JankMetricUMARecorderJni.get().recordJankMetrics(
-                metric.getDurations(), metric.getJankBursts(), metric.getSkippedFrames());
+        JankMetricUMARecorderJni.get().recordJankMetrics(scenarioToString(scenario),
+                metric.durationsNs, metric.jankBurstsNs, metric.skippedFrames);
+    }
+
+    // Convert an enum value to string to use as an UMA histogram name, changes to strings should be
+    // reflected in android/histograms.xml.
+    private static String scenarioToString(@JankScenario int scenario) {
+        switch (scenario) {
+            case JankScenario.PERIODIC_REPORTING:
+                return "Total";
+            case JankScenario.OMNIBOX:
+                return "Omnibox";
+            case JankScenario.NEW_TAB_PAGE:
+                return "NewTabPage";
+            case JankScenario.STARTUP:
+                return "Startup";
+            case JankScenario.TAB_SWITCHER:
+                return "TabSwitcher";
+            case JankScenario.OPEN_LINK_IN_NEW_TAB:
+                return "OpenLinkInNewTab";
+            default:
+                throw new IllegalArgumentException("Invalid scenario value");
+        }
     }
 
     @NativeMethods
     interface Natives {
-        void recordJankMetrics(long[] durationsNs, long[] jankBurstsNs, int missedFrames);
+        void recordJankMetrics(
+                String scenarioName, long[] durationsNs, long[] jankBurstsNs, int missedFrames);
     }
 }
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/JankMetrics.java b/base/android/java/src/org/chromium/base/jank_tracker/JankMetrics.java
new file mode 100644
index 0000000..8eebbea
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/jank_tracker/JankMetrics.java
@@ -0,0 +1,24 @@
+// Copyright 2021 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.jank_tracker;
+
+/**
+ * This class is a container for jank metrics, which are processed FrameMetrics ready to be uploaded
+ * to UMA.
+ */
+class JankMetrics {
+    public final long[] timestampsNs;
+    public final long[] durationsNs;
+    public final long[] jankBurstsNs;
+    public final int skippedFrames;
+
+    public JankMetrics(
+            long[] timestampsNs, long[] durationsNs, long[] jankBurstsNs, int skippedFrames) {
+        this.timestampsNs = timestampsNs;
+        this.durationsNs = durationsNs;
+        this.jankBurstsNs = jankBurstsNs;
+        this.skippedFrames = skippedFrames;
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/JankReportingRunnable.java b/base/android/java/src/org/chromium/base/jank_tracker/JankReportingRunnable.java
new file mode 100644
index 0000000..f108237
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/jank_tracker/JankReportingRunnable.java
@@ -0,0 +1,45 @@
+// Copyright 2021 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.jank_tracker;
+
+import org.chromium.base.library_loader.LibraryLoader;
+
+/**
+ * This runnable receives a FrameMetricsStore instance and starts/stops tracking a given scenario.
+ * When a scenario stops it takes its metrics and sends them to native to be recorded in UMA.
+ * This is executed by JankReportingScheduler on its own thread.
+ */
+class JankReportingRunnable implements Runnable {
+    private final FrameMetricsStore mMetricsStore;
+    private final @JankScenario int mScenario;
+    private final boolean mIsStartingTracking;
+
+    JankReportingRunnable(FrameMetricsStore metricsStore, @JankScenario int scenario,
+            boolean isStartingTracking) {
+        mMetricsStore = metricsStore;
+        mScenario = scenario;
+        mIsStartingTracking = isStartingTracking;
+    }
+
+    @Override
+    public void run() {
+        if (mIsStartingTracking) {
+            mMetricsStore.startTrackingScenario(mScenario);
+        } else {
+            FrameMetrics frames = mMetricsStore.stopTrackingScenario(mScenario);
+            if (frames == null) {
+                return;
+            }
+
+            if (!LibraryLoader.getInstance().isInitialized()) {
+                return;
+            }
+
+            JankMetrics metrics = JankMetricCalculator.calculateJankMetrics(frames);
+            // TODO(salg@): Cache metrics in case native takes >30s to initialize.
+            JankMetricUMARecorder.recordJankMetricsToUMA(metrics, mScenario);
+        }
+    }
+}
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/JankReportingScheduler.java b/base/android/java/src/org/chromium/base/jank_tracker/JankReportingScheduler.java
new file mode 100644
index 0000000..8f98570
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/jank_tracker/JankReportingScheduler.java
@@ -0,0 +1,82 @@
+// Copyright 2021 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.jank_tracker;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This class receives requests to start and stop jank scenario tracking and runs them in a
+ * HandlerThread it creates. In addition it handles the recording of periodic jank metrics.
+ */
+class JankReportingScheduler {
+    private static final long PERIODIC_METRIC_DELAY_MS = 30_000;
+    private final FrameMetricsStore mFrameMetricsStore;
+
+    JankReportingScheduler(FrameMetricsStore frameMetricsStore) {
+        mFrameMetricsStore = frameMetricsStore;
+    }
+
+    private final Runnable mPeriodicMetricReporter = new Runnable() {
+        @Override
+        public void run() {
+            finishTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+            if (mIsPeriodicReporterLooping.get()) {
+                startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+                getOrCreateHandler().postDelayed(mPeriodicMetricReporter, PERIODIC_METRIC_DELAY_MS);
+            }
+        }
+    };
+
+    @Nullable
+    protected HandlerThread mHandlerThread;
+    @Nullable
+    private Handler mHandler;
+    private final AtomicBoolean mIsPeriodicReporterLooping = new AtomicBoolean(false);
+
+    void startTrackingScenario(@JankScenario int scenario) {
+        getOrCreateHandler().post(new JankReportingRunnable(
+                mFrameMetricsStore, scenario, /* isStartingTracking= */ true));
+    }
+
+    void finishTrackingScenario(@JankScenario int scenario) {
+        getOrCreateHandler().post(new JankReportingRunnable(
+                mFrameMetricsStore, scenario, /* isStartingTracking= */ false));
+    }
+
+    protected Handler getOrCreateHandler() {
+        if (mHandler == null) {
+            mHandlerThread = new HandlerThread("Jank-Tracker");
+            mHandlerThread.start();
+            mHandler = new Handler(mHandlerThread.getLooper());
+        }
+        return mHandler;
+    }
+
+    void startReportingPeriodicMetrics() {
+        // If mIsPeriodicReporterLooping was already true then there's no need to post another task.
+        if (mIsPeriodicReporterLooping.getAndSet(true)) {
+            return;
+        }
+        startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        getOrCreateHandler().postDelayed(mPeriodicMetricReporter, PERIODIC_METRIC_DELAY_MS);
+    }
+
+    void stopReportingPeriodicMetrics() {
+        // Disable mPeriodicMetricReporter looping, and return early if it was already disabled.
+        if (!mIsPeriodicReporterLooping.getAndSet(false)) {
+            return;
+        }
+        // Remove any existing mPeriodicMetricReporter delayed tasks.
+        getOrCreateHandler().removeCallbacks(mPeriodicMetricReporter);
+        // Run mPeriodicMetricReporter one last time immediately.
+        getOrCreateHandler().post(mPeriodicMetricReporter);
+    }
+}
\ No newline at end of file
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/JankScenario.java b/base/android/java/src/org/chromium/base/jank_tracker/JankScenario.java
new file mode 100644
index 0000000..9ca5867
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/jank_tracker/JankScenario.java
@@ -0,0 +1,26 @@
+// Copyright 2021 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.jank_tracker;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A list of jank scenarios to be tracked, each scenario corresponds to a specific user journey
+ * except by PERIODIC_REPORTING, which runs constantly and is uploaded every 30s.
+ */
+@IntDef({JankScenario.PERIODIC_REPORTING, JankScenario.OMNIBOX, JankScenario.NEW_TAB_PAGE,
+        JankScenario.STARTUP, JankScenario.TAB_SWITCHER, JankScenario.OPEN_LINK_IN_NEW_TAB})
+@Retention(RetentionPolicy.SOURCE)
+public @interface JankScenario {
+    int PERIODIC_REPORTING = 1;
+    int OMNIBOX = 2;
+    int NEW_TAB_PAGE = 3;
+    int STARTUP = 4;
+    int TAB_SWITCHER = 5;
+    int OPEN_LINK_IN_NEW_TAB = 6;
+}
diff --git a/base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java b/base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java
index b8284c16..9ce17ce8 100644
--- a/base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java
+++ b/base/android/java/src/org/chromium/base/jank_tracker/JankTracker.java
@@ -19,16 +19,28 @@
 @RequiresApi(api = VERSION_CODES.N)
 public final class JankTracker {
     private final JankActivityTracker mActivityTracker;
+    private final JankReportingScheduler mReportingScheduler;
 
     /**
      * Creates a new JankTracker instance tracking UI rendering of an activity. Metric recording
      * starts when the activity starts, and it's paused when the activity stops.
      */
     public JankTracker(Activity activity) {
-        mActivityTracker = JankActivityTracker.create(activity);
+        FrameMetricsStore metricsStore = new FrameMetricsStore();
+        FrameMetricsListener metricsListener = new FrameMetricsListener(metricsStore);
+        mReportingScheduler = new JankReportingScheduler(metricsStore);
+        mActivityTracker = new JankActivityTracker(activity, metricsListener, mReportingScheduler);
         mActivityTracker.initialize();
     }
 
+    public void startTrackingScenario(@JankScenario int scenario) {
+        mReportingScheduler.startTrackingScenario(scenario);
+    }
+
+    public void finishTrackingScenario(@JankScenario int scenario) {
+        mReportingScheduler.finishTrackingScenario(scenario);
+    }
+
     /**
      * Stops listening for Activity state changes.
      */
diff --git a/base/android/junit/src/org/chromium/base/jank_tracker/JankFrameMetricsListenerTest.java b/base/android/junit/src/org/chromium/base/jank_tracker/FrameMetricsListenerTest.java
similarity index 64%
rename from base/android/junit/src/org/chromium/base/jank_tracker/JankFrameMetricsListenerTest.java
rename to base/android/junit/src/org/chromium/base/jank_tracker/FrameMetricsListenerTest.java
index 56899b2..3f89b8fc 100644
--- a/base/android/junit/src/org/chromium/base/jank_tracker/JankFrameMetricsListenerTest.java
+++ b/base/android/junit/src/org/chromium/base/jank_tracker/FrameMetricsListenerTest.java
@@ -18,37 +18,42 @@
 import org.chromium.base.test.BaseRobolectricTestRunner;
 
 /**
- *  Tests for JankFrameMetricsListener.
+ *  Tests for FrameMetricsListener.
  */
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
-public class JankFrameMetricsListenerTest {
+public class FrameMetricsListenerTest {
     @Test
     public void testMetricRecording_OffByDefault() {
-        JankMetricMeasurement measurement = new JankMetricMeasurement();
-        JankFrameMetricsListener metricsListener = new JankFrameMetricsListener(measurement);
+        FrameMetricsStore store = new FrameMetricsStore();
+        FrameMetricsListener metricsListener = new FrameMetricsListener(store);
         FrameMetrics frameMetrics = mock(FrameMetrics.class);
 
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
         when(frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION)).thenReturn(10_000_000L);
 
         metricsListener.onFrameMetricsAvailable(null, frameMetrics, 0);
 
         // By default metrics shouldn't be logged.
-        Assert.assertEquals(0, measurement.getMetrics().getDurations().length);
+        Assert.assertEquals(
+                0, store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING).durationsNs.length);
         verifyZeroInteractions(frameMetrics);
     }
 
     @Test
     public void testMetricRecording_EnableRecording() {
-        JankMetricMeasurement measurement = new JankMetricMeasurement();
-        JankFrameMetricsListener metricsListener = new JankFrameMetricsListener(measurement);
+        FrameMetricsStore store = new FrameMetricsStore();
+
+        FrameMetricsListener metricsListener = new FrameMetricsListener(store);
         FrameMetrics frameMetrics = mock(FrameMetrics.class);
 
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
         when(frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION)).thenReturn(10_000_000L);
 
         metricsListener.setIsListenerRecording(true);
         metricsListener.onFrameMetricsAvailable(null, frameMetrics, 0);
 
-        Assert.assertArrayEquals(new long[] {10_000_000L}, measurement.getMetrics().getDurations());
+        Assert.assertArrayEquals(new Long[] {10_000_000L},
+                store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING).durationsNs);
     }
 }
diff --git a/base/android/junit/src/org/chromium/base/jank_tracker/FrameMetricsStoreTest.java b/base/android/junit/src/org/chromium/base/jank_tracker/FrameMetricsStoreTest.java
new file mode 100644
index 0000000..2bd9b63
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/jank_tracker/FrameMetricsStoreTest.java
@@ -0,0 +1,217 @@
+// Copyright 2021 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.jank_tracker;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/**
+ *  Tests for FrameMetricsStore.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class FrameMetricsStoreTest {
+    @Test
+    public void addFrameMeasurementTest() {
+        FrameMetricsStore store = new FrameMetricsStore();
+
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_000_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_020_000_000L, 12_000_000L, 1);
+        store.addFrameMeasurement(1_040_000_000L, 20_000_000L, 2);
+        store.addFrameMeasurement(1_060_000_000L, 8_000_000L, 0);
+
+        FrameMetrics scenarioMetrics = store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        assertArrayEquals(
+                new Long[] {1_000_000_000L, 1_020_000_000L, 1_040_000_000L, 1_060_000_000L},
+                scenarioMetrics.timestampsNs);
+        assertArrayEquals(new Long[] {10_000_000L, 12_000_000L, 20_000_000L, 8_000_000L},
+                scenarioMetrics.durationsNs);
+        assertArrayEquals(new Integer[] {0, 1, 2, 0}, scenarioMetrics.skippedFrames);
+    }
+
+    @Test
+    public void addFrameMeasurementTest_MultipleScenarios() {
+        JankMetricCalculator measurement = new JankMetricCalculator();
+
+        FrameMetricsStore store = new FrameMetricsStore();
+
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_000_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_020_000_000L, 12_000_000L, 0);
+
+        FrameMetrics periodicReportingMetrics =
+                store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        store.startTrackingScenario(JankScenario.OMNIBOX);
+
+        store.addFrameMeasurement(1_040_000_000L, 20_000_000L, 0);
+        store.addFrameMeasurement(1_060_000_000L, 8_000_000L, 0);
+
+        FrameMetrics omniboxMetrics = store.stopTrackingScenario(JankScenario.OMNIBOX);
+
+        assertArrayEquals(
+                new Long[] {10_000_000L, 12_000_000L}, periodicReportingMetrics.durationsNs);
+        assertArrayEquals(new Long[] {20_000_000L, 8_000_000L}, omniboxMetrics.durationsNs);
+    }
+
+    @Test
+    public void addFrameMeasurement_MultipleOverlappingScenarios() {
+        JankMetricCalculator measurement = new JankMetricCalculator();
+
+        FrameMetricsStore store = new FrameMetricsStore();
+
+        store.addFrameMeasurement(1_000_000_000L, 15_000_000L, 0);
+
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_020_000_000L, 15_000_000L, 0);
+        store.addFrameMeasurement(1_040_000_000L, 50_000_000L, 0);
+        store.addFrameMeasurement(1_060_000_000L, 30_000_000L, 0);
+
+        store.startTrackingScenario(JankScenario.OMNIBOX);
+
+        store.addFrameMeasurement(1_080_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_100_000_000L, 30_000_000L, 0);
+
+        FrameMetrics periodicReportingMetrics =
+                store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_120_000_000L, 10_000_000L, 0);
+
+        FrameMetrics omniboxMetrics = store.stopTrackingScenario(JankScenario.OMNIBOX);
+
+        assertArrayEquals(
+                new Long[] {15_000_000L, 50_000_000L, 30_000_000L, 10_000_000L, 30_000_000L},
+                periodicReportingMetrics.durationsNs);
+        assertArrayEquals(
+                new Long[] {10_000_000L, 30_000_000L, 10_000_000L}, omniboxMetrics.durationsNs);
+    }
+
+    @Test
+    public void addFrameMeasurementTest_MultipleStartCallsAreIgnored() {
+        JankMetricCalculator measurement = new JankMetricCalculator();
+
+        FrameMetricsStore store = new FrameMetricsStore();
+
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_000_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_020_000_000L, 12_000_000L, 0);
+
+        // Any duplicate calls to startTracking should be ignored.
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_040_000_000L, 20_000_000L, 0);
+
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_060_000_000L, 8_000_000L, 0);
+
+        FrameMetrics periodicReportingMetrics =
+                store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        // The returned metrics should begin at the first call to startTrackingScenario.
+        assertArrayEquals(new Long[] {10_000_000L, 12_000_000L, 20_000_000L, 8_000_000L},
+                periodicReportingMetrics.durationsNs);
+    }
+
+    @Test
+    public void stopTrackingScenario_StopWithoutAnyFrames() {
+        JankMetricCalculator measurement = new JankMetricCalculator();
+
+        FrameMetricsStore store = new FrameMetricsStore();
+
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        FrameMetrics periodicReportingMetrics =
+                store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.startTrackingScenario(JankScenario.OMNIBOX);
+        FrameMetrics omniboxMetrics = store.stopTrackingScenario(JankScenario.OMNIBOX);
+
+        assertEquals(0, periodicReportingMetrics.durationsNs.length);
+        assertEquals(0, omniboxMetrics.durationsNs.length);
+    }
+
+    @Test
+    public void stopTrackingScenario_EmptyScenarioAfterOneFrame() {
+        JankMetricCalculator measurement = new JankMetricCalculator();
+
+        FrameMetricsStore store = new FrameMetricsStore();
+
+        // Start a scenario just to start recording.
+        store.startTrackingScenario(JankScenario.OMNIBOX);
+
+        // Add a frame measurement.
+        store.addFrameMeasurement(1_060_000_000L, 8_000_000L, 0);
+
+        // Start and stop tracking scenario.
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        FrameMetrics periodicReportingMetrics =
+                store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        // The resulting metrics should be empty.
+        assertEquals(0, periodicReportingMetrics.durationsNs.length);
+    }
+
+    @Test
+    public void stopTrackingScenario_StopWithoutStartingReturnsEmptyMetrics() {
+        JankMetricCalculator measurement = new JankMetricCalculator();
+
+        FrameMetricsStore store = new FrameMetricsStore();
+
+        FrameMetrics periodicReportingMetrics =
+                store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        FrameMetrics omniboxMetrics = store.stopTrackingScenario(JankScenario.OMNIBOX);
+
+        assertEquals(0, periodicReportingMetrics.durationsNs.length);
+        assertEquals(0, omniboxMetrics.durationsNs.length);
+    }
+
+    @Test
+    public void stopTrackingScenario_ClearsUnneededFrames() {
+        FrameMetricsStore store = new FrameMetricsStore();
+
+        store.addFrameMeasurement(1_000_000_000L, 15_000_000L, 0);
+
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_020_000_000L, 15_000_000L, 0);
+        store.addFrameMeasurement(1_040_000_000L, 50_000_000L, 0);
+        // This frame should be kept after PERIODIC_REPORTING ends because OMNIBOX uses its
+        // timestamp to track the scenario's start.
+        store.addFrameMeasurement(1_060_000_000L, 33_333_333L, 0);
+
+        store.startTrackingScenario(JankScenario.OMNIBOX);
+
+        store.addFrameMeasurement(1_080_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_100_000_000L, 30_000_000L, 0);
+
+        store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        FrameMetrics storedMetricsAfterPeriodic = store.getAllStoredMetricsForTesting();
+
+        store.addFrameMeasurement(1_120_000_000L, 10_000_000L, 0);
+
+        store.stopTrackingScenario(JankScenario.OMNIBOX);
+        FrameMetrics storedMetricsAfterOmnibox = store.getAllStoredMetricsForTesting();
+
+        // When we stop tracking periodic reporting we should remove all frames that aren't used by
+        // any other scenarios.
+        assertArrayEquals(new Long[] {33_333_333L, 10_000_000L, 30_000_000L},
+                storedMetricsAfterPeriodic.durationsNs);
+        // When we stop tracking omnibox there are no other scenarios being tracked, so we clear all
+        // frame data.
+        assertEquals(0, storedMetricsAfterOmnibox.durationsNs.length);
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/jank_tracker/JankActivityTrackerTest.java b/base/android/junit/src/org/chromium/base/jank_tracker/JankActivityTrackerTest.java
index 927f2bd..cb14861 100644
--- a/base/android/junit/src/org/chromium/base/jank_tracker/JankActivityTrackerTest.java
+++ b/base/android/junit/src/org/chromium/base/jank_tracker/JankActivityTrackerTest.java
@@ -6,13 +6,13 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
 import android.view.Window;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -20,8 +20,6 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.ActivityState;
 import org.chromium.base.ApplicationStatus;
@@ -32,7 +30,6 @@
  */
 @RunWith(BaseRobolectricTestRunner.class)
 public class JankActivityTrackerTest {
-    ShadowLooper mShadowLooper;
 
     @Mock
     private Activity mActivity;
@@ -41,15 +38,14 @@
     private Window mWindow;
 
     @Mock
-    private JankFrameMetricsListener mJankFrameMetricsListener;
+    private FrameMetricsListener mFrameMetricsListener;
 
     @Mock
-    private JankMetricMeasurement mJankMetricMeasurement;
+    private JankReportingScheduler mJankReportingScheduler;
 
     JankActivityTracker createJankActivityTracker(Activity activity) {
-        JankActivityTracker tracker = new JankActivityTracker(
-                activity, mJankFrameMetricsListener, mJankMetricMeasurement);
-        mShadowLooper = Shadow.extract(tracker.getOrCreateHandler().getLooper());
+        JankActivityTracker tracker =
+                new JankActivityTracker(activity, mFrameMetricsListener, mJankReportingScheduler);
 
         return tracker;
     }
@@ -82,18 +78,12 @@
         ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
         ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
 
-        // When an activity resumes we schedule a repeating task to report metrics on the tracker's
-        // handler thread.
-        mShadowLooper.runOneTask();
-
-        // The reporting task should clear the jank measurement.
-        verify(mJankMetricMeasurement).clear();
-
-        // The reporting task should be looping, so another task should be posted.
-        Assert.assertTrue(mShadowLooper.getScheduler().areAnyRunnable());
+        // When an activity resumes we start reporting periodic metrics.
+        verify(mJankReportingScheduler, atLeastOnce()).startReportingPeriodicMetrics();
+        verify(mJankReportingScheduler, never()).stopReportingPeriodicMetrics();
 
         // When an activity resumes we start recording metrics.
-        verify(mJankFrameMetricsListener, atLeastOnce()).setIsListenerRecording(true);
+        verify(mFrameMetricsListener, atLeastOnce()).setIsListenerRecording(true);
     }
 
     @Test
@@ -105,16 +95,15 @@
         ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
         ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
 
-        mShadowLooper.runOneTask();
-
         // When an activity pauses the reporting task should still be looping.
-        Assert.assertTrue(mShadowLooper.getScheduler().areAnyRunnable());
+        verify(mJankReportingScheduler, atLeastOnce()).startReportingPeriodicMetrics();
+        verify(mJankReportingScheduler, never()).stopReportingPeriodicMetrics();
 
-        InOrder orderVerifier = Mockito.inOrder(mJankFrameMetricsListener);
+        InOrder orderVerifier = Mockito.inOrder(mFrameMetricsListener);
 
-        orderVerifier.verify(mJankFrameMetricsListener, atLeastOnce()).setIsListenerRecording(true);
+        orderVerifier.verify(mFrameMetricsListener, atLeastOnce()).setIsListenerRecording(true);
         // When an activity pauses we stop recording metrics.
-        orderVerifier.verify(mJankFrameMetricsListener).setIsListenerRecording(false);
+        orderVerifier.verify(mFrameMetricsListener).setIsListenerRecording(false);
     }
 
     @Test
@@ -127,10 +116,11 @@
         ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
         ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STOPPED);
 
-        // When an activity stops we run the reporting task one last time and stop it.
-        mShadowLooper.runOneTask();
-
-        Assert.assertFalse(mShadowLooper.getScheduler().areAnyRunnable());
+        // When an activity stops we stop reporting periodic metrics.
+        InOrder schedulerOrderVerifier = Mockito.inOrder(mJankReportingScheduler);
+        schedulerOrderVerifier.verify(mJankReportingScheduler, atLeastOnce())
+                .startReportingPeriodicMetrics();
+        schedulerOrderVerifier.verify(mJankReportingScheduler).stopReportingPeriodicMetrics();
     }
 
     @Test
@@ -143,12 +133,10 @@
         jankActivityTracker.initialize();
 
         // Verify that JankActivityTracker is running as expected for the Resumed state.
-        // Reporting task should be running and looping.
-        mShadowLooper.runOneTask();
-        verify(mJankMetricMeasurement).clear();
-        Assert.assertTrue(mShadowLooper.getScheduler().areAnyRunnable());
+        // Periodic metric reporting should be enabled.
+        verify(mJankReportingScheduler).startReportingPeriodicMetrics();
         // Metric recording should be enabled.
-        verify(mJankFrameMetricsListener).setIsListenerRecording(true);
+        verify(mFrameMetricsListener).setIsListenerRecording(true);
     }
 
     @Test
@@ -162,10 +150,8 @@
 
         // Verify that JankActivityTracker is running as expected for the Resumed state.
         // Reporting task should be running and looping.
-        mShadowLooper.runOneTask();
-        verify(mJankMetricMeasurement).clear();
-        Assert.assertTrue(mShadowLooper.getScheduler().areAnyRunnable());
+        verify(mJankReportingScheduler).startReportingPeriodicMetrics();
         // Metric recording should be enabled.
-        verify(mJankFrameMetricsListener).setIsListenerRecording(true);
+        verify(mFrameMetricsListener).setIsListenerRecording(true);
     }
 }
diff --git a/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricCalculatorTest.java b/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricCalculatorTest.java
new file mode 100644
index 0000000..5d28a42f
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricCalculatorTest.java
@@ -0,0 +1,107 @@
+// Copyright 2021 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.jank_tracker;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/**
+ *  Tests for JankMetricCalculator.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class JankMetricCalculatorTest {
+    @Test
+    public void getJankBurstDurationsTest() {
+        FrameMetricsStore store = new FrameMetricsStore();
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_000_000_000L, 15_000_000L, 1);
+        store.addFrameMeasurement(1_020_000_000L, 15_000_000L, 0);
+        store.addFrameMeasurement(1_040_000_000L, 50_000_000L, 1); // Burst starts here.
+        store.addFrameMeasurement(1_060_000_000L, 30_000_000L, 1);
+        store.addFrameMeasurement(1_080_000_000L, 10_000_000L, 2);
+        store.addFrameMeasurement(1_120_000_000L, 30_000_000L, 0); // Burst ends here.
+        store.addFrameMeasurement(1_100_000_000L, 10_000_000L, 0);
+
+        FrameMetrics frameMetrics = store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        JankMetrics jankMetrics = JankMetricCalculator.calculateJankMetrics(frameMetrics);
+
+        assertArrayEquals(new long[] {120_000_000L}, jankMetrics.jankBurstsNs);
+        assertEquals(5, jankMetrics.skippedFrames);
+    }
+
+    @Test
+    public void getJankBurstDurationsTest_TwoBursts() {
+        FrameMetricsStore store = new FrameMetricsStore();
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_000_000_000L, 50_000_000L, 0); // Burst starts here.
+        store.addFrameMeasurement(1_020_000_000L, 50_000_000L, 0);
+        store.addFrameMeasurement(1_040_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_060_000_000L, 50_000_000L, 0); // Burst ends here.
+        store.addFrameMeasurement(1_080_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_100_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_120_000_000L, 50_000_000L, 0); // Burst starts here.
+        store.addFrameMeasurement(1_140_000_000L, 50_000_000L, 0);
+        store.addFrameMeasurement(1_160_000_000L, 50_000_000L, 0); // Burst ends here.
+
+        FrameMetrics frameMetrics = store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        JankMetrics jankMetrics = JankMetricCalculator.calculateJankMetrics(frameMetrics);
+
+        assertArrayEquals(new long[] {160_000_000L, 150_000_000L}, jankMetrics.jankBurstsNs);
+    }
+
+    @Test
+    public void getJankBurstDurationsTest_OneLongBurst() {
+        FrameMetricsStore store = new FrameMetricsStore();
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_000_000_000L, 50_000_000L, 0); // Burst starts here.
+        store.addFrameMeasurement(1_020_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_040_000_000L, 50_000_000L, 0);
+        store.addFrameMeasurement(1_060_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_080_000_000L, 50_000_000L, 0);
+        store.addFrameMeasurement(1_100_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_120_000_000L, 50_000_000L, 0);
+        store.addFrameMeasurement(1_140_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(1_160_000_000L, 50_000_000L, 0); // Burst ends here.
+
+        FrameMetrics frameMetrics = store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        JankMetrics jankMetrics = JankMetricCalculator.calculateJankMetrics(frameMetrics);
+
+        assertArrayEquals(new long[] {290_000_000L}, jankMetrics.jankBurstsNs);
+    }
+
+    @Test
+    public void getJankBurstDurationsTest_ThreeBursts_WithNonConsecutiveFrames() {
+        FrameMetricsStore store = new FrameMetricsStore();
+        store.startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        store.addFrameMeasurement(1_000_000_000L, 50_000_000L, 0); // Burst starts here.
+        store.addFrameMeasurement(1_020_000_000L, 50_000_000L, 0);
+        store.addFrameMeasurement(1_040_000_000L, 50_000_000L, 0); // Burst ends here.
+        store.addFrameMeasurement(
+                2_000_000_000L, 50_000_000L, 0); // Burst starts here (~1000ms passed).
+        store.addFrameMeasurement(2_020_000_000L, 50_000_000L, 0);
+        store.addFrameMeasurement(2_040_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(2_060_000_000L, 50_000_000L, 0); // Burst ends here.
+        store.addFrameMeasurement(3_000_000_000L, 10_000_000L, 0);
+        store.addFrameMeasurement(3_020_000_000L, 50_000_000L, 0); // Burst starts and ends here.
+        store.addFrameMeasurement(3_040_000_000L, 10_000_000L, 0);
+
+        FrameMetrics frameMetrics = store.stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        JankMetrics jankMetrics = JankMetricCalculator.calculateJankMetrics(frameMetrics);
+
+        assertArrayEquals(
+                new long[] {150_000_000L, 160_000_000L, 50_000_000L}, jankMetrics.jankBurstsNs);
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricMeasurementTest.java b/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricMeasurementTest.java
deleted file mode 100644
index 48ad065..0000000
--- a/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricMeasurementTest.java
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2021 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.jank_tracker;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
-import org.chromium.base.test.BaseRobolectricTestRunner;
-
-/**
- *  Tests for JankMetricMeasurement.
- */
-@RunWith(BaseRobolectricTestRunner.class)
-@Config(manifest = Config.NONE)
-public class JankMetricMeasurementTest {
-    @Test
-    public void addFrameMeasurementTest() {
-        JankMetricMeasurement measurement = new JankMetricMeasurement();
-
-        measurement.addFrameMeasurement(1_000_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(1_020_000_000L, 12_000_000L, 0);
-        measurement.addFrameMeasurement(1_040_000_000L, 20_000_000L, 0);
-        measurement.addFrameMeasurement(1_060_000_000L, 8_000_000L, 0);
-
-        assertArrayEquals(new long[] {10_000_000L, 12_000_000L, 20_000_000L, 8_000_000L},
-                measurement.getMetrics().getDurations());
-    }
-
-    @Test
-    public void clearTest() {
-        JankMetricMeasurement measurement = new JankMetricMeasurement();
-
-        measurement.addFrameMeasurement(1_000_000_000L, 10_000_000L, 0);
-
-        measurement.clear();
-
-        assertEquals(0, measurement.getMetrics().getDurations().length);
-    }
-
-    @Test
-    public void getJankBurstDurationsTest() {
-        JankMetricMeasurement measurement = new JankMetricMeasurement();
-
-        measurement.addFrameMeasurement(1_000_000_000L, 15_000_000L, 0);
-        measurement.addFrameMeasurement(1_020_000_000L, 15_000_000L, 0);
-        measurement.addFrameMeasurement(1_040_000_000L, 50_000_000L, 0); // Burst starts here.
-        measurement.addFrameMeasurement(1_060_000_000L, 30_000_000L, 0);
-        measurement.addFrameMeasurement(1_080_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(1_100_000_000L, 30_000_000L, 0); // Burst ends here.
-        measurement.addFrameMeasurement(1_120_000_000L, 10_000_000L, 0);
-
-        assertArrayEquals(new long[] {120_000_000L}, measurement.getMetrics().getJankBursts());
-    }
-
-    @Test
-    public void getJankBurstDurationsTest_TwoBursts() {
-        JankMetricMeasurement measurement = new JankMetricMeasurement();
-
-        measurement.addFrameMeasurement(1_000_000_000L, 50_000_000L, 0); // Burst starts here.
-        measurement.addFrameMeasurement(1_020_000_000L, 50_000_000L, 0);
-        measurement.addFrameMeasurement(1_040_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(1_060_000_000L, 50_000_000L, 0); // Burst ends here.
-        measurement.addFrameMeasurement(1_080_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(1_100_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(1_120_000_000L, 50_000_000L, 0); // Burst starts here.
-        measurement.addFrameMeasurement(1_140_000_000L, 50_000_000L, 0);
-        measurement.addFrameMeasurement(1_160_000_000L, 50_000_000L, 0); // Burst ends here.
-
-        assertArrayEquals(
-                new long[] {160_000_000L, 150_000_000L}, measurement.getMetrics().getJankBursts());
-    }
-
-    @Test
-    public void getJankBurstDurationsTest_OneLongBurst() {
-        JankMetricMeasurement measurement = new JankMetricMeasurement();
-
-        measurement.addFrameMeasurement(1_000_000_000L, 50_000_000L, 0); // Burst starts here.
-        measurement.addFrameMeasurement(1_020_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(1_040_000_000L, 50_000_000L, 0);
-        measurement.addFrameMeasurement(1_060_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(1_080_000_000L, 50_000_000L, 0);
-        measurement.addFrameMeasurement(1_100_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(1_120_000_000L, 50_000_000L, 0);
-        measurement.addFrameMeasurement(1_140_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(1_160_000_000L, 50_000_000L, 0); // Burst ends here.
-
-        assertArrayEquals(new long[] {290_000_000L}, measurement.getMetrics().getJankBursts());
-    }
-
-    @Test
-    public void getJankBurstDurationsTest_ThreeBursts_WithNonConsecutiveFrames() {
-        JankMetricMeasurement measurement = new JankMetricMeasurement();
-
-        measurement.addFrameMeasurement(1_000_000_000L, 50_000_000L, 0); // Burst starts here.
-        measurement.addFrameMeasurement(1_020_000_000L, 50_000_000L, 0);
-        measurement.addFrameMeasurement(1_040_000_000L, 50_000_000L, 0); // Burst ends here.
-        measurement.addFrameMeasurement(
-                2_000_000_000L, 50_000_000L, 0); // Burst starts here (~1000ms passed).
-        measurement.addFrameMeasurement(2_020_000_000L, 50_000_000L, 0);
-        measurement.addFrameMeasurement(2_040_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(2_060_000_000L, 50_000_000L, 0); // Burst ends here.
-        measurement.addFrameMeasurement(3_000_000_000L, 10_000_000L, 0);
-        measurement.addFrameMeasurement(
-                3_020_000_000L, 50_000_000L, 0); // Burst starts and ends here.
-        measurement.addFrameMeasurement(3_040_000_000L, 10_000_000L, 0);
-
-        assertArrayEquals(new long[] {150_000_000L, 160_000_000L, 50_000_000L},
-                measurement.getMetrics().getJankBursts());
-    }
-}
diff --git a/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricUMARecorderTest.java b/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricUMARecorderTest.java
index 4b71a71..134d16cd 100644
--- a/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricUMARecorderTest.java
+++ b/base/android/junit/src/org/chromium/base/jank_tracker/JankMetricUMARecorderTest.java
@@ -14,7 +14,6 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
 
-import org.chromium.base.jank_tracker.JankMetricMeasurement.JankMetric;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.JniMocker;
 
@@ -43,11 +42,11 @@
         long[] jankBurstsNs = new long[] {30_000L};
         int missedFrames = 3;
 
-        JankMetric metric = new JankMetric(timestampsNs, durationsNs, jankBurstsNs, missedFrames);
+        JankMetrics metric = new JankMetrics(timestampsNs, durationsNs, jankBurstsNs, missedFrames);
 
-        JankMetricUMARecorder.recordJankMetricsToUMA(metric);
+        JankMetricUMARecorder.recordJankMetricsToUMA(metric, JankScenario.OMNIBOX);
 
         // Ensure that the relevant fields are sent down to native.
-        verify(mNativeMock).recordJankMetrics(durationsNs, jankBurstsNs, missedFrames);
+        verify(mNativeMock).recordJankMetrics("Omnibox", durationsNs, jankBurstsNs, missedFrames);
     }
 }
diff --git a/base/android/junit/src/org/chromium/base/jank_tracker/JankReportingRunnableTest.java b/base/android/junit/src/org/chromium/base/jank_tracker/JankReportingRunnableTest.java
new file mode 100644
index 0000000..05a24aa
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/jank_tracker/JankReportingRunnableTest.java
@@ -0,0 +1,71 @@
+// Copyright 2021 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.jank_tracker;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import org.chromium.base.library_loader.LibraryLoader;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.JniMocker;
+
+/**
+ *  Tests for JankReportingRunnable.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class JankReportingRunnableTest {
+    @Rule
+    public JniMocker mocker = new JniMocker();
+
+    @Mock
+    JankMetricUMARecorder.Natives mNativeMock;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mocker.mock(JankMetricUMARecorderJni.TEST_HOOKS, mNativeMock);
+    }
+
+    @Test
+    public void testStartTracking() {
+        FrameMetricsStore metricsStore = Mockito.spy(new FrameMetricsStore());
+
+        JankReportingRunnable reportingRunnable = new JankReportingRunnable(
+                metricsStore, JankScenario.TAB_SWITCHER, /* isStartingTracking= */ true);
+        reportingRunnable.run();
+
+        verify(metricsStore).startTrackingScenario(JankScenario.TAB_SWITCHER);
+        verifyNoMoreInteractions(metricsStore);
+    }
+
+    @Test
+    public void testStopTracking() {
+        FrameMetricsStore metricsStore = Mockito.spy(new FrameMetricsStore());
+
+        JankReportingRunnable startReportingRunnable = new JankReportingRunnable(
+                metricsStore, JankScenario.TAB_SWITCHER, /* isStartingTracking= */ true);
+        startReportingRunnable.run();
+
+        metricsStore.addFrameMeasurement(1_000_000L, 1_000L, 1);
+        LibraryLoader.getInstance().setLibrariesLoadedForNativeTests();
+
+        JankReportingRunnable stopReportingRunnable = new JankReportingRunnable(
+                metricsStore, JankScenario.TAB_SWITCHER, /* isStartingTracking= */ false);
+        stopReportingRunnable.run();
+
+        verify(metricsStore).startTrackingScenario(JankScenario.TAB_SWITCHER);
+        verify(metricsStore).stopTrackingScenario(JankScenario.TAB_SWITCHER);
+
+        verify(mNativeMock).recordJankMetrics("TabSwitcher", new long[] {1_000L}, new long[0], 1);
+    }
+}
diff --git a/base/android/junit/src/org/chromium/base/jank_tracker/JankReportingSchedulerTest.java b/base/android/junit/src/org/chromium/base/jank_tracker/JankReportingSchedulerTest.java
new file mode 100644
index 0000000..d6f0c55
--- /dev/null
+++ b/base/android/junit/src/org/chromium/base/jank_tracker/JankReportingSchedulerTest.java
@@ -0,0 +1,144 @@
+// Copyright 2021 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.jank_tracker;
+
+import static org.mockito.Mockito.verify;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowLooper;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/**
+ *  Tests for JankReportingScheduler.
+ */
+@RunWith(BaseRobolectricTestRunner.class)
+public class JankReportingSchedulerTest {
+    ShadowLooper mShadowLooper;
+
+    @Mock
+    private FrameMetricsStore mFrameMetricsStore;
+
+    JankReportingScheduler createJankReportingScheduler() {
+        JankReportingScheduler scheduler = new JankReportingScheduler(mFrameMetricsStore);
+        mShadowLooper = Shadow.extract(scheduler.getOrCreateHandler().getLooper());
+
+        return scheduler;
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void jankScenarioTracking_startTracking() {
+        JankReportingScheduler jankReportingScheduler = createJankReportingScheduler();
+
+        jankReportingScheduler.startTrackingScenario(JankScenario.NEW_TAB_PAGE);
+
+        // Starting tracking posts a task to begin recording metrics in FrameMetricsStore.
+        mShadowLooper.runOneTask();
+
+        verify(mFrameMetricsStore).startTrackingScenario(JankScenario.NEW_TAB_PAGE);
+    }
+
+    @Test
+    public void jankScenarioTracking_startAndStopTracking() {
+        JankReportingScheduler jankReportingScheduler = createJankReportingScheduler();
+
+        jankReportingScheduler.startTrackingScenario(JankScenario.NEW_TAB_PAGE);
+        jankReportingScheduler.finishTrackingScenario(JankScenario.NEW_TAB_PAGE);
+
+        // Starting tracking posts a task to begin recording metrics in FrameMetricsStore.
+        mShadowLooper.runOneTask();
+        // Stopping tracking posts a task to finish tracking and upload the calculated metrics.
+        mShadowLooper.runOneTask();
+
+        InOrder orderVerifier = Mockito.inOrder(mFrameMetricsStore);
+
+        // After both tasks we should have started and stopped tracking the periodic reporting
+        // scenario.
+        orderVerifier.verify(mFrameMetricsStore).startTrackingScenario(JankScenario.NEW_TAB_PAGE);
+        orderVerifier.verify(mFrameMetricsStore).stopTrackingScenario(JankScenario.NEW_TAB_PAGE);
+
+        Assert.assertFalse(mShadowLooper.getScheduler().areAnyRunnable());
+    }
+
+    @Test
+    public void jankReportingSchedulerTest_StartPeriodicReporting() {
+        JankReportingScheduler jankReportingScheduler = createJankReportingScheduler();
+
+        jankReportingScheduler.startReportingPeriodicMetrics();
+
+        // When periodic reporting is enabled a task is immediately posted to begin tracking.
+        mShadowLooper.runOneTask();
+        // Then a delayed task is posted for the reporting loop.
+        mShadowLooper.runOneTask();
+        // The reporting loop task posts an immediate task to stop tracking and record the data.
+        mShadowLooper.runOneTask();
+
+        InOrder orderVerifier = Mockito.inOrder(mFrameMetricsStore);
+
+        // After both tasks we should have started and stopped tracking the periodic reporting
+        // scenario.
+        orderVerifier.verify(mFrameMetricsStore)
+                .startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        orderVerifier.verify(mFrameMetricsStore)
+                .stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        // There should be another task posted to continue the loop.
+        Assert.assertTrue(mShadowLooper.getScheduler().areAnyRunnable());
+    }
+
+    @Test
+    public void jankReportingSchedulerTest_StopPeriodicReporting() {
+        JankReportingScheduler jankReportingScheduler = createJankReportingScheduler();
+
+        jankReportingScheduler.startReportingPeriodicMetrics();
+
+        // Run tracking initialization task.
+        mShadowLooper.runOneTask();
+        // Run the first reporting loop (delayed 30s).
+        mShadowLooper.runOneTask();
+        // Run task to stop tracking 1st loop and record data.
+        mShadowLooper.runOneTask();
+        // Run task to start tracking the 2nd reporting loop.
+        mShadowLooper.runOneTask();
+
+        jankReportingScheduler.stopReportingPeriodicMetrics();
+
+        // Stopping periodic metric recording posts a reporting loop task immediately to stop
+        // tracking and record results.
+        mShadowLooper.runOneTask();
+        // The reporting loop task posts another immediate task to stop tracking and report data.
+        mShadowLooper.runOneTask();
+
+        InOrder orderVerifier = Mockito.inOrder(mFrameMetricsStore);
+
+        // This start/stop pair corresponds to the first reporting period.
+        orderVerifier.verify(mFrameMetricsStore)
+                .startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        orderVerifier.verify(mFrameMetricsStore)
+                .stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        // Stopping reporting forces an immediate report of recorded frames, if any.
+        orderVerifier.verify(mFrameMetricsStore)
+                .startTrackingScenario(JankScenario.PERIODIC_REPORTING);
+        orderVerifier.verify(mFrameMetricsStore)
+                .stopTrackingScenario(JankScenario.PERIODIC_REPORTING);
+
+        // There should not be another task posted to continue the loop.
+        Assert.assertFalse(mShadowLooper.getScheduler().areAnyRunnable());
+    }
+}
diff --git a/base/message_loop/message_pump.h b/base/message_loop/message_pump.h
index f1f5df7f..7f67cb8 100644
--- a/base/message_loop/message_pump.h
+++ b/base/message_loop/message_pump.h
@@ -60,6 +60,11 @@
       // isn't null nor max. MessagePump impls should use remaining_delay()
       // instead of resampling Now() if they wish to sleep for a TimeDelta.
       TimeTicks recent_now;
+
+      // If true, native messages should be processed before executing more work
+      // from the Delegate. This is an optional hint; not all message pumpls
+      // implement this.
+      bool yield_to_native = false;
     };
 
     // Executes an immediate task or a ripe delayed task. Returns information
diff --git a/base/message_loop/message_pump_android.cc b/base/message_loop/message_pump_android.cc
index bb8aadd..d7024583 100644
--- a/base/message_loop/message_pump_android.cc
+++ b/base/message_loop/message_pump_android.cc
@@ -215,6 +215,14 @@
       return;
 
     next_work_info = delegate_->DoWork();
+    // If we are prioritizing native, and the next work would normally run
+    // immediately, skip the next work and let the native tasks have a chance to
+    // run. This is useful when user input is waiting for native to have a
+    // chance to run.
+    if (next_work_info.is_immediate() && next_work_info.yield_to_native) {
+      ScheduleWork();
+      return;
+    }
   } while (next_work_info.is_immediate());
 
   // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
diff --git a/base/message_loop/message_pump_unittest.cc b/base/message_loop/message_pump_unittest.cc
index e09e813f..d84b237f 100644
--- a/base/message_loop/message_pump_unittest.cc
+++ b/base/message_loop/message_pump_unittest.cc
@@ -160,6 +160,36 @@
   message_pump_->Run(&delegate);
 }
 
+TEST_P(MessagePumpTest, YieldToNativeRequestedSmokeTest) {
+  // The handling of the "yield_to_native" boolean in the NextWorkInfo is only
+  // implemented on the MessagePumpForUI on android. However since we inject a
+  // fake one for testing this is hard to test. This test ensures that setting
+  // this boolean doesn't cause any MessagePump to explode.
+  testing::InSequence sequence;
+  testing::StrictMock<MockMessagePumpDelegate> delegate;
+
+  // Return an immediate task with |yield_to_native| set.
+  AddPreDoWorkExpectations(delegate);
+  EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([] {
+    return MessagePump::Delegate::NextWorkInfo{TimeTicks(), TimeTicks(),
+                                               /* yield_to_native = */ true};
+  }));
+  AddPostDoWorkExpectations(delegate);
+
+  // Return a delayed task with |yield_to_native| set, and exit.
+  AddPreDoWorkExpectations(delegate);
+  EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
+    message_pump_->Quit();
+    auto now = TimeTicks::Now();
+    return MessagePump::Delegate::NextWorkInfo{
+        now + TimeDelta::FromMilliseconds(1), now, true};
+  }));
+  EXPECT_CALL(delegate, DoIdleWork()).Times(AnyNumber());
+
+  message_pump_->ScheduleWork();
+  message_pump_->Run(&delegate);
+}
+
 namespace {
 
 class TimerSlackTestDelegate : public MessagePump::Delegate {
diff --git a/base/task/sequence_manager/sequence_manager.h b/base/task/sequence_manager/sequence_manager.h
index a9f6c09..b5e5b0b6 100644
--- a/base/task/sequence_manager/sequence_manager.h
+++ b/base/task/sequence_manager/sequence_manager.h
@@ -241,6 +241,11 @@
   virtual std::unique_ptr<NativeWorkHandle> OnNativeWorkPending(
       TaskQueue::QueuePriority priority) = 0;
 
+  // While Now() is less than |prioritize_until| we will alternate between a
+  // SequenceManager task and a yielding to the underlying sequence (e.g., the
+  // message pump).
+  virtual void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) = 0;
+
   // Adds an observer which reports task execution. Can only be called on the
   // same thread that |this| is running on.
   virtual void AddTaskObserver(TaskObserver* task_observer) = 0;
diff --git a/base/task/sequence_manager/sequence_manager_impl.cc b/base/task/sequence_manager/sequence_manager_impl.cc
index e5265e6..a59b2b1 100644
--- a/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/base/task/sequence_manager/sequence_manager_impl.cc
@@ -1125,6 +1125,11 @@
   return std::make_unique<NativeWorkHandleImpl>(this, priority);
 }
 
+void SequenceManagerImpl::PrioritizeYieldingToNative(
+    base::TimeTicks prioritize_until) {
+  controller_->PrioritizeYieldingToNative(prioritize_until);
+}
+
 void SequenceManagerImpl::AddDestructionObserver(
     CurrentThread::DestructionObserver* destruction_observer) {
   main_thread_only().destruction_observers.AddObserver(destruction_observer);
diff --git a/base/task/sequence_manager/sequence_manager_impl.h b/base/task/sequence_manager/sequence_manager_impl.h
index b930561..60d7ae8 100644
--- a/base/task/sequence_manager/sequence_manager_impl.h
+++ b/base/task/sequence_manager/sequence_manager_impl.h
@@ -123,6 +123,7 @@
   std::string DescribeAllPendingTasks() const override;
   std::unique_ptr<NativeWorkHandle> OnNativeWorkPending(
       TaskQueue::QueuePriority priority) override;
+  void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) override;
   void AddTaskObserver(TaskObserver* task_observer) override;
   void RemoveTaskObserver(TaskObserver* task_observer) override;
 
diff --git a/base/task/sequence_manager/thread_controller.h b/base/task/sequence_manager/thread_controller.h
index 5c5d1a5..aa0e95b8 100644
--- a/base/task/sequence_manager/thread_controller.h
+++ b/base/task/sequence_manager/thread_controller.h
@@ -106,6 +106,14 @@
   virtual void DetachFromMessagePump() = 0;
 #endif
 
+  // Currently only overridden on ThreadControllerWithMessagePumpImpl.
+  //
+  // While Now() is less than |prioritize_until| we will alternate between
+  // |work_batch_size| tasks before setting |yield_to_native| on the
+  // NextWorkInfo and yielding to the underlying sequence (e.g. the message
+  // pump).
+  virtual void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) = 0;
+
   // Sets the SingleThreadTaskRunner that will be returned by
   // ThreadTaskRunnerHandle::Get on the thread controlled by this
   // ThreadController.
diff --git a/base/task/sequence_manager/thread_controller_impl.cc b/base/task/sequence_manager/thread_controller_impl.cc
index bfc66bd..96f18384 100644
--- a/base/task/sequence_manager/thread_controller_impl.cc
+++ b/base/task/sequence_manager/thread_controller_impl.cc
@@ -344,6 +344,10 @@
 }
 #endif  // OS_IOS
 
+void ThreadControllerImpl::PrioritizeYieldingToNative(base::TimeTicks) {
+  NOTREACHED();
+}
+
 }  // namespace internal
 }  // namespace sequence_manager
 }  // namespace base
diff --git a/base/task/sequence_manager/thread_controller_impl.h b/base/task/sequence_manager/thread_controller_impl.h
index 8d40248..6877380 100644
--- a/base/task/sequence_manager/thread_controller_impl.h
+++ b/base/task/sequence_manager/thread_controller_impl.h
@@ -67,6 +67,7 @@
 #if defined(OS_IOS)
   void DetachFromMessagePump() override;
 #endif
+  void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) override;
   bool ShouldQuitRunLoopWhenIdle() override;
 
   // RunLoop::NestingObserver:
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index ef4ca3b..6a0ae0b 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -253,9 +253,21 @@
 
 MessagePump::Delegate::NextWorkInfo
 ThreadControllerWithMessagePumpImpl::DoWork() {
+  MessagePump::Delegate::NextWorkInfo next_work_info{};
+
   work_deduplicator_.OnWorkStarted();
   LazyNow continuation_lazy_now(time_source_);
   TimeDelta delay_till_next_task = DoWorkImpl(&continuation_lazy_now);
+
+  // If we are yielding after DoWorkImpl (a work batch) set the flag boolean.
+  // This will inform the MessagePump to schedule a new continuation based on
+  // the information below, but even if its immediate let the native sequence
+  // have a chance to run.
+  if (!main_thread_only().yield_to_native_after_batch.is_null() &&
+      continuation_lazy_now.Now() <
+          main_thread_only().yield_to_native_after_batch) {
+    next_work_info.yield_to_native = true;
+  }
   // Schedule a continuation.
   WorkDeduplicator::NextTask next_task =
       delay_till_next_task.is_zero() ? WorkDeduplicator::NextTask::kIsImmediate
@@ -264,14 +276,15 @@
       ShouldScheduleWork::kScheduleImmediate) {
     // Need to run new work immediately, but due to the contract of DoWork
     // we only need to return a null TimeTicks to ensure that happens.
-    return MessagePump::Delegate::NextWorkInfo();
+    return next_work_info;
   }
 
   // While the math below would saturate when |delay_till_next_task.is_max()|;
   // special-casing here avoids unnecessarily sampling Now() when out of work.
   if (delay_till_next_task.is_max()) {
     main_thread_only().next_delayed_do_work = TimeTicks::Max();
-    return {TimeTicks::Max()};
+    next_work_info.delayed_run_time = TimeTicks::Max();
+    return next_work_info;
   }
 
   // The MessagePump will schedule the delay on our behalf, so we need to update
@@ -287,13 +300,16 @@
     main_thread_only().next_delayed_do_work =
         main_thread_only().quit_runloop_after;
     // If we've passed |quit_runloop_after| there's no more work to do.
-    if (continuation_lazy_now.Now() >= main_thread_only().quit_runloop_after)
-      return {TimeTicks::Max()};
+    if (continuation_lazy_now.Now() >= main_thread_only().quit_runloop_after) {
+      next_work_info.delayed_run_time = TimeTicks::Max();
+      return next_work_info;
+    }
   }
 
-  return {CapAtOneDay(main_thread_only().next_delayed_do_work,
-                      &continuation_lazy_now),
-          continuation_lazy_now.Now()};
+  next_work_info.delayed_run_time = CapAtOneDay(
+      main_thread_only().next_delayed_do_work, &continuation_lazy_now);
+  next_work_info.recent_now = continuation_lazy_now.Now();
+  return next_work_info;
 }
 
 TimeDelta ThreadControllerWithMessagePumpImpl::DoWorkImpl(
@@ -513,6 +529,11 @@
   return pump_.get();
 }
 
+void ThreadControllerWithMessagePumpImpl::PrioritizeYieldingToNative(
+    base::TimeTicks prioritize_until) {
+  main_thread_only().yield_to_native_after_batch = prioritize_until;
+}
+
 #if defined(OS_IOS)
 void ThreadControllerWithMessagePumpImpl::AttachToMessagePump() {
   static_cast<MessagePumpCFRunLoopBase*>(pump_.get())->Attach(this);
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
index aeb1b8b2..35004ac 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
@@ -72,6 +72,7 @@
   void SetTaskExecutionAllowed(bool allowed) override;
   bool IsTaskExecutionAllowed() const override;
   MessagePump* GetBoundMessagePump() const override;
+  void PrioritizeYieldingToNative(base::TimeTicks prioritize_until) override;
 #if defined(OS_IOS) || defined(OS_ANDROID)
   void AttachToMessagePump() override;
 #endif
@@ -118,6 +119,10 @@
     // Number of tasks processed in a single DoWork invocation.
     int work_batch_size = 1;
 
+    // While Now() is less than |yield_to_native_after_batch| we will request a
+    // yield to the MessagePump after |work_batch_size| work items.
+    base::TimeTicks yield_to_native_after_batch = base::TimeTicks();
+
     // Tracks the number and state of each run-level managed by this instance.
     RunLevelTracker run_level_tracker;
 
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
index 46ede57..a1467559 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
@@ -520,6 +520,76 @@
   testing::Mock::VerifyAndClearExpectations(message_pump_);
 }
 
+TEST_F(ThreadControllerWithMessagePumpTest, PrioritizeYieldingToNative) {
+  ThreadTaskRunnerHandle handle(MakeRefCounted<FakeTaskRunner>());
+
+  testing::InSequence sequence;
+
+  RunLoop run_loop;
+  auto delayed_time = Seconds(10);
+  EXPECT_CALL(*message_pump_, Run(_))
+      .WillOnce(Invoke([&](MessagePump::Delegate* delegate) {
+        clock_.SetNowTicks(Seconds(5));
+        MockCallback<OnceClosure> tasks[5];
+
+        // A: Post 4 application tasks, 3 immediate 1 delayed.
+        // B: Run one of them (enter active)
+        //   C: Expect we return immediate work item without yield_to_native
+        //      (default behaviour).
+        // D: Set PrioritizeYieldingToNative until 8 seconds and run second
+        //    task.
+        //   E: Expect we return immediate work item with yield_to_native.
+        // F: Exceed the PrioritizeYieldingToNative deadline and run third task.
+        //   G: Expect we return immediate work item without yield_to_native.
+        // H: Set PrioritizeYieldingToNative to Max() and run third of them
+        //   I: Expect we return a delayed work item with yield_to_native.
+
+        // A:
+        task_source_.AddTask(FROM_HERE, tasks[0].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[1].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[2].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[3].Get(), TimeTicks());
+        task_source_.AddTask(FROM_HERE, tasks[4].Get(), delayed_time);
+
+        // B:
+        EXPECT_CALL(tasks[0], Run());
+        auto next_work_item = thread_controller_.DoWork();
+        // C:
+        EXPECT_EQ(next_work_item.delayed_run_time, TimeTicks());
+        EXPECT_FALSE(next_work_item.yield_to_native);
+
+        // D:
+        thread_controller_.PrioritizeYieldingToNative(Seconds(8));
+        EXPECT_CALL(tasks[1], Run());
+        next_work_item = thread_controller_.DoWork();
+        // E:
+        EXPECT_EQ(next_work_item.delayed_run_time, TimeTicks());
+        EXPECT_TRUE(next_work_item.yield_to_native);
+
+        // F:
+        clock_.SetNowTicks(Seconds(8));
+        EXPECT_CALL(tasks[2], Run());
+        next_work_item = thread_controller_.DoWork();
+        // G:
+        EXPECT_EQ(next_work_item.delayed_run_time, TimeTicks());
+        EXPECT_FALSE(next_work_item.yield_to_native);
+
+        // H:
+        thread_controller_.PrioritizeYieldingToNative(base::TimeTicks::Max());
+        EXPECT_CALL(tasks[3], Run());
+        next_work_item = thread_controller_.DoWork();
+
+        // I:
+        EXPECT_EQ(next_work_item.delayed_run_time, delayed_time);
+        EXPECT_TRUE(next_work_item.yield_to_native);
+
+        EXPECT_FALSE(thread_controller_.DoIdleWork());
+      }));
+
+  run_loop.Run();
+  testing::Mock::VerifyAndClearExpectations(message_pump_);
+}
+
 TEST_F(ThreadControllerWithMessagePumpTest, EarlyQuit) {
   // This test ensures that a opt-of-runloop Quit() (which is possible with some
   // pump implementations) doesn't affect the next RunLoop::Run call.
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 039bf6f..ac58c2e 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -157,7 +157,9 @@
     }
 
     if (is_chromeos_lacros && also_build_ash_chrome) {
-      data_deps += [ "//chrome:chrome(//build/toolchain/linux:ash_clang_x64)" ]
+      data_deps += [
+        "//chrome/test:test_ash_chrome(//build/toolchain/linux:ash_clang_x64)",
+      ]
     }
 
     if (is_win) {
diff --git a/chrome/VERSION b/chrome/VERSION
index e9395856..0051329 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=93
 MINOR=0
-BUILD=4530
+BUILD=4531
 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 8b1d8538..b520750 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -556,6 +556,7 @@
 
             // LocaleManager can only function after the native library is loaded.
             mLocaleManager = AppHooks.get().getLocaleManager();
+            mLocaleManager.setSettingsLauncher(SETTINGS_LAUNCHER);
             mLocaleManager.showSearchEnginePromoIfNeeded(this, null);
 
             mTabModelOrchestrator.onNativeLibraryReady(getTabContentManager());
@@ -896,7 +897,6 @@
         }
 
         mLocaleManager.setSnackbarManager(getSnackbarManager());
-        mLocaleManager.setSettingsLauncher(SETTINGS_LAUNCHER);
         mLocaleManager.startObservingPhoneChanges();
 
         if (isWarmOnResume()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/DEPS b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
index 3cd874b..b509cc63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/DEPS
+++ b/chrome/android/java/src/org/chromium/chrome/browser/DEPS
@@ -32,9 +32,6 @@
   "LaunchIntentDispatcher\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
-  "NavigationPopup\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "TabbedModeTabDelegateFactory\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
@@ -62,9 +59,6 @@
   "DisplayCutoutTabHelper\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
-  "ReaderModeManager\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "DownloadController\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
@@ -77,9 +71,6 @@
   "ScreenshotTask\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
-  "InfoBarContainer\.java": [
-    "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
-  ],
   "ProcessInitializationHandler\.java": [
     "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
   ],
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/NavigationPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/NavigationPopup.java
index a1ae927..e461ff2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/NavigationPopup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/NavigationPopup.java
@@ -28,10 +28,12 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.base.supplier.Supplier;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.history.HistoryManagerUtils;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabUtils;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper.DefaultFaviconHelper;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper.FaviconImageCallback;
@@ -72,6 +74,7 @@
     private final int mFaviconSize;
     @Nullable
     private final OnLayoutChangeListener mAnchorViewLayoutChangeListener;
+    private final Supplier<Tab> mCurrentTabSupplier;
 
     private DefaultFaviconHelper mDefaultFaviconHelper;
 
@@ -90,14 +93,17 @@
      * @param context The context used for building the popup.
      * @param navigationController The controller which takes care of page navigations.
      * @param type The type of navigation popup being triggered.
+     * @param currentTabSupplier Supplies the current tab.
      */
     public NavigationPopup(Profile profile, Context context,
-            NavigationController navigationController, @Type int type) {
+            NavigationController navigationController, @Type int type,
+            Supplier<Tab> currentTabSupplier) {
         mProfile = profile;
         mContext = context;
         Resources resources = mContext.getResources();
         mNavigationController = navigationController;
         mType = type;
+        mCurrentTabSupplier = currentTabSupplier;
 
         boolean isForward = type == Type.TABLET_FORWARD;
         boolean anchorToBottom = type == Type.ANDROID_SYSTEM_BACK;
@@ -245,10 +251,10 @@
         NavigationEntry entry = (NavigationEntry) parent.getItemAtPosition(position);
         if (entry.getIndex() == FULL_HISTORY_ENTRY_INDEX) {
             RecordUserAction.record(buildComputedAction("ShowFullHistory"));
-            assert mContext instanceof ChromeActivity;
-            ChromeActivity activity = (ChromeActivity) mContext;
-            HistoryManagerUtils.showHistoryManager(activity, activity.getActivityTab(),
-                    activity.getTabModelSelector().isIncognitoSelected());
+            Tab currentTab = mCurrentTabSupplier.get();
+            HistoryManagerUtils.showHistoryManager(TabUtils.getActivity(currentTab), currentTab,
+                    /* isIncognitoSelected= */ currentTab == null ? false
+                                                                  : currentTab.isIncognito());
         } else {
             // 1-based index to keep in line with Desktop implementation.
             RecordUserAction.record(buildComputedAction("HistoryClick" + (position + 1)));
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 3218e59..d3948d53 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
@@ -668,6 +668,7 @@
         } else if (eventAction == MotionEvent.ACTION_CANCEL
                 || eventAction == MotionEvent.ACTION_UP) {
             mInGesture = false;
+            updateViewportSize();
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index 05f02db..4bd363029 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -21,7 +21,6 @@
 import org.chromium.base.UserData;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.browser.IntentHandler;
-import org.chromium.chrome.browser.app.ChromeActivity;
 import org.chromium.chrome.browser.browser_controls.BrowserControlsVisibilityManager;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.CustomTabsUiType;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
@@ -30,6 +29,8 @@
 import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.dom_distiller.TabDistillabilityProvider.DistillabilityObserver;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
+import org.chromium.chrome.browser.fullscreen.BrowserControlsManagerSupplier;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.infobar.ReaderModeInfoBar;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
@@ -436,32 +437,39 @@
 
         onStartedReaderMode();
 
-        // Make sure to exit fullscreen mode before navigating.
-        getFullscreenManager().onExitFullscreen(mTab);
+        FullscreenManager fullscreenManager = getFullscreenManager();
+        if (fullscreenManager != null) {
+            // Make sure to exit fullscreen mode before navigating.
+            fullscreenManager.onExitFullscreen(mTab);
+        }
 
         // RenderWidgetHostViewAndroid hides the controls after transitioning to reader mode.
         // See the long history of the issue in https://crbug.com/825765, https://crbug.com/853686,
         // https://crbug.com/861618, https://crbug.com/922388.
         // TODO(pshmakov): find a proper solution instead of this workaround.
-        getBrowserControlsVisibilityManager()
-                .getBrowserVisibilityDelegate()
-                .showControlsTransient();
+        BrowserControlsVisibilityManager browserControlsVisibilityManager =
+                getBrowserControlsVisibilityManager();
+        if (browserControlsVisibilityManager != null) {
+            getBrowserControlsVisibilityManager()
+                    .getBrowserVisibilityDelegate()
+                    .showControlsTransient();
+        }
 
         DomDistillerTabUtils.distillCurrentPageAndView(webContents);
     }
 
-    private BrowserControlsVisibilityManager getBrowserControlsVisibilityManager() {
-        // TODO(1069815): Remove this ChromeActivity cast once BrowserControlsManager is
-        //                accessible via another mechanism.
-        ChromeActivity activity = (ChromeActivity) TabUtils.getActivity(mTab);
-        return activity.getBrowserControlsManager();
+    private @Nullable BrowserControlsManager getBrowserControlsManager() {
+        return BrowserControlsManagerSupplier.getValueOrNullFrom(mTab.getWindowAndroid());
     }
 
-    private FullscreenManager getFullscreenManager() {
-        // TODO(1069815): Remove this ChromeActivity cast once FullscreenManager is
-        //                accessible via another mechanism.
-        ChromeActivity activity = (ChromeActivity) TabUtils.getActivity(mTab);
-        return activity.getFullscreenManager();
+    private @Nullable BrowserControlsVisibilityManager getBrowserControlsVisibilityManager() {
+        return getBrowserControlsManager();
+    }
+
+    private @Nullable FullscreenManager getFullscreenManager() {
+        BrowserControlsManager browserControlsManager = getBrowserControlsManager();
+        return browserControlsManager == null ? null
+                                              : browserControlsManager.getFullscreenManager();
     }
 
     private void distillInCustomTab() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java
index f6b24833..befa5d9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/InfoBarContainer.java
@@ -16,7 +16,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.app.ChromeActivity;
+import org.chromium.chrome.browser.fullscreen.BrowserControlsManagerSupplier;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tab.TabObserver;
@@ -31,6 +31,7 @@
 import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.KeyboardVisibilityDelegate.KeyboardVisibilityListener;
+import org.chromium.ui.base.DeviceFormFactor;
 import org.chromium.ui.base.WindowAndroid;
 
 import java.util.ArrayList;
@@ -182,19 +183,19 @@
 
     /**
      * The view that {@link Tab#getView()} returns.  It will be null when the {@link Tab} is
-     * detached from a {@link ChromeActivity}.
+     * detached from a {@link Activity}.
      */
     private @Nullable View mTabView;
 
     /**
      * The view for this {@link InfoBarContainer}. It will be null when the {@link Tab} is detached
-     * from a {@link ChromeActivity}.
+     * from a {@link Activity}.
      */
     private @Nullable InfoBarContainerView mInfoBarContainerView;
 
     /**
      * Helper class to manage showing in-product help bubbles over specific info bars. It will be
-     * null when the {@link Tab} is detached from a {@link ChromeActivity}.
+     * null when the {@link Tab} is detached from a {@link Activity}.
      */
     private @Nullable IPHInfoBarSupport mIPHSupport;
 
@@ -235,7 +236,7 @@
         mTabView = tab.getView();
         mTab = tab;
 
-        ChromeActivity activity = getActivity(tab);
+        Activity activity = getActivity(tab);
         if (activity != null) initializeContainerView(activity);
 
         // Chromium's InfoBarContainer may add an InfoBar immediately during this initialization
@@ -243,9 +244,8 @@
         mNativeInfoBarContainer = InfoBarContainerJni.get().init(InfoBarContainer.this);
     }
 
-    private static ChromeActivity getActivity(Tab tab) {
-        Activity activity = tab.getWindowAndroid().getActivity().get();
-        return activity instanceof ChromeActivity ? (ChromeActivity) activity : null;
+    private static Activity getActivity(Tab tab) {
+        return tab.getWindowAndroid().getActivity().get();
     }
 
     /**
@@ -445,7 +445,7 @@
 
     private void updateWebContents() {
         // When the tab is detached, we don't update the InfoBarContainer web content so that it
-        // stays null until the tab is attached to some ChromeActivity.
+        // stays null until the tab is attached to some Activity.
         if (mInfoBarContainerView == null) return;
         WebContents webContents = mTab.getWebContents();
 
@@ -462,12 +462,10 @@
         if (mTabView != null) mTabView.addOnAttachStateChangeListener(mAttachedStateListener);
     }
 
-    private void initializeContainerView(ChromeActivity chromeActivity) {
-        assert chromeActivity
-                != null
-            : "ChromeActivity should not be null when initializing InfoBarContainerView";
-        mInfoBarContainerView = new InfoBarContainerView(chromeActivity, mContainerViewObserver,
-                chromeActivity.getBrowserControlsManager(), chromeActivity.isTablet());
+    private void initializeContainerView(Activity activity) {
+        mInfoBarContainerView = new InfoBarContainerView(activity, mContainerViewObserver,
+                BrowserControlsManagerSupplier.getValueOrNullFrom(mTab.getWindowAndroid()),
+                DeviceFormFactor.isWindowOnTablet(mTab.getWindowAndroid()));
 
         mInfoBarContainerView.addOnAttachStateChangeListener(
                 new View.OnAttachStateChangeListener() {
@@ -499,9 +497,9 @@
                 });
 
         mInfoBarContainerView.setHidden(mIsHidden);
-        setParentView(chromeActivity.findViewById(R.id.bottom_container));
+        setParentView(activity.findViewById(R.id.bottom_container));
 
-        mIPHSupport = new IPHInfoBarSupport(new IPHBubbleDelegateImpl(chromeActivity, mTab));
+        mIPHSupport = new IPHInfoBarSupport(new IPHBubbleDelegateImpl(activity, mTab));
         addAnimationListener(mIPHSupport);
         addObserver(mIPHSupport);
 
@@ -525,7 +523,7 @@
             mInfoBarContainerView = null;
         }
 
-        ChromeActivity activity = getActivity(mTab);
+        Activity activity = getActivity(mTab);
         if (activity != null && mBottomSheetObserver != null) {
             mBottomSheetController.removeObserver(mBottomSheetObserver);
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
index 9b7bfb8..ce0ea0b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/top/ToolbarTablet.java
@@ -290,7 +290,8 @@
         if (tab == null || tab.getWebContents() == null) return;
         mNavigationPopup = new NavigationPopup(Profile.fromWebContents(tab.getWebContents()),
                 getContext(), tab.getWebContents().getNavigationController(),
-                isForward ? NavigationPopup.Type.TABLET_FORWARD : NavigationPopup.Type.TABLET_BACK);
+                isForward ? NavigationPopup.Type.TABLET_FORWARD : NavigationPopup.Type.TABLET_BACK,
+                getToolbarDataProvider()::getTab);
         mNavigationPopup.show(anchorView);
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/NavigationPopupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/NavigationPopupTest.java
index 64eb8cd..629f250 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/NavigationPopupTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/NavigationPopupTest.java
@@ -145,7 +145,8 @@
     private ListPopupWindow showPopup(NavigationController controller) throws ExecutionException {
         return TestThreadUtils.runOnUiThreadBlocking(() -> {
             NavigationPopup popup = new NavigationPopup(mProfile, mActivityTestRule.getActivity(),
-                    controller, NavigationPopup.Type.TABLET_FORWARD);
+                    controller, NavigationPopup.Type.TABLET_FORWARD,
+                    mActivityTestRule.getActivity().getActivityTabProvider());
             popup.show(mActivityTestRule.getActivity()
                                .getToolbarManager()
                                .getToolbarLayoutForTesting());
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b4acb5d..aff70ab 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2365,6 +2365,7 @@
     "//ui/web_dialogs",
     "//ui/webui",
     "//ui/webui/resources/cr_components/customize_themes:mojom",
+    "//ui/webui/resources/cr_components/most_visited:mojom",
   ]
   if (is_chromeos_ash) {
     sources += [
@@ -4610,8 +4611,8 @@
       "//ash/public/cpp/external_arc",
       "//build:chromeos_buildflags",
       "//chrome/browser/ash/crosapi",
+      "//chrome/browser/ash/power/ml/smart_dim",
       "//chrome/browser/chromeos",
-      "//chrome/browser/chromeos/power/ml/smart_dim",
       "//chrome/browser/nearby_sharing:share_target",
       "//chrome/browser/nearby_sharing/certificates",
       "//chrome/browser/nearby_sharing/client",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 0bef22b4..798259c 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3239,7 +3239,7 @@
      FEATURE_WITH_PARAMS_VALUE_TYPE(
          chrome::android::kAdaptiveButtonInTopToolbar,
          kAdaptiveButtonInTopToolbarVariations,
-         "AdaptiveButtonInTopToolbar")},
+         "OptionalToolbarButton")},
     {"adaptive-button-in-top-toolbar-customization",
      flag_descriptions::kAdaptiveButtonInTopToolbarCustomizationName,
      flag_descriptions::kAdaptiveButtonInTopToolbarCustomizationDescription,
diff --git a/chrome/browser/ash/crosapi/idle_service_ash.cc b/chrome/browser/ash/crosapi/idle_service_ash.cc
index b99d76ea..774478c 100644
--- a/chrome/browser/ash/crosapi/idle_service_ash.cc
+++ b/chrome/browser/ash/crosapi/idle_service_ash.cc
@@ -35,7 +35,12 @@
 IdleServiceAsh::Dispatcher::~Dispatcher() {
   if (!IdleServiceAsh::Dispatcher::is_disabled_for_testing_) {
     chromeos::SessionManagerClient::Get()->RemoveObserver(this);
-    ui::UserActivityDetector::Get()->RemoveObserver(this);
+
+    // UserActivityDetector may not be exist on actual shutdown, because
+    // the ProfileManager destruct before CrosapiManager.
+    auto* user_activity_detector = ui::UserActivityDetector::Get();
+    if (user_activity_detector)
+      ui::UserActivityDetector::Get()->RemoveObserver(this);
   }
 }
 
diff --git a/chrome/browser/ash/crosapi/prefs_ash.cc b/chrome/browser/ash/crosapi/prefs_ash.cc
index b96058e..da08817 100644
--- a/chrome/browser/ash/crosapi/prefs_ash.cc
+++ b/chrome/browser/ash/crosapi/prefs_ash.cc
@@ -46,8 +46,11 @@
 }
 
 PrefsAsh::~PrefsAsh() {
-  // Remove this observer, in case Primary logged in profile is not yet created.
-  profile_manager_->RemoveObserver(this);
+  // Remove this observer, if the Primary logged in profile is not yet created.
+  // On actual shutdown, the ProfileManager will destruct before CrosapiManager.
+  if (IsInObserverList() && profile_manager_) {
+    profile_manager_->RemoveObserver(this);
+  }
 }
 
 void PrefsAsh::BindReceiver(mojo::PendingReceiver<mojom::Prefs> receiver) {
@@ -126,6 +129,10 @@
   }
 }
 
+void PrefsAsh::OnProfileManagerDestroying() {
+  profile_manager_ = nullptr;
+}
+
 void PrefsAsh::OnPrefChanged(mojom::PrefPath path) {
   auto state = GetState(path);
   const base::Value* value =
diff --git a/chrome/browser/ash/crosapi/prefs_ash.h b/chrome/browser/ash/crosapi/prefs_ash.h
index a1fbd8d..2305256 100644
--- a/chrome/browser/ash/crosapi/prefs_ash.h
+++ b/chrome/browser/ash/crosapi/prefs_ash.h
@@ -45,6 +45,7 @@
 
   // ProfileManagerObserver:
   void OnProfileAdded(Profile* profile) override;
+  void OnProfileManagerDestroying() override;
 
   // Used to inject |profile| as a primary profile for testing.
   void OnPrimaryProfileReadyForTesting(Profile* profile) {
@@ -67,8 +68,9 @@
   // Called when Primary logged in user profile is ready.
   void OnPrimaryProfileReady(Profile* profile);
 
-  // In production, owned by g_browser_process, which outlives this object.
-  ProfileManager* const profile_manager_;
+  // In production, owned by g_browser_process, which does not outlives this
+  // object.
+  ProfileManager* profile_manager_;
   // In production, owned by g_browser_process, which outlives this object.
   PrefService* const local_state_;
   // Owned by the primary profile. This will be set after the profile is
diff --git a/chrome/browser/ash/kerberos/kerberos_credentials_manager.cc b/chrome/browser/ash/kerberos/kerberos_credentials_manager.cc
index c1296a6..c9e6ffd0 100644
--- a/chrome/browser/ash/kerberos/kerberos_credentials_manager.cc
+++ b/chrome/browser/ash/kerberos/kerberos_credentials_manager.cc
@@ -6,7 +6,6 @@
 
 #include <vector>
 
-#include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
@@ -931,18 +930,12 @@
 
 void KerberosCredentialsManager::OnTicketExpiryNotificationClick(
     const std::string& principal_name) {
-  // The correct URL path for Kerberos accounts subpage, according to the
-  // Kerberos settings section flag.
-  const std::string kSubpagePath =
-      chromeos::features::IsKerberosSettingsSectionEnabled()
-          ? chromeos::settings::mojom::kKerberosAccountsV2SubpagePath
-          : chromeos::settings::mojom::kKerberosAccountsSubpagePath;
-
   // TODO(https://crbug.com/952245): Right now, the reauth dialog is tied to the
   // settings. Consider creating a standalone reauth dialog.
   chrome::SettingsWindowManager::GetInstance()->ShowOSSettings(
       primary_profile_,
-      kSubpagePath + "?kerberos_reauth=" +
+      chromeos::settings::mojom::kKerberosAccountsV2SubpagePath +
+          std::string("?kerberos_reauth=") +
           net::EscapeQueryParamValue(principal_name, false /* use_plus */));
 
   // Close last! |principal_name| is owned by the notification.
diff --git a/chrome/browser/ash/login/session/user_session_manager.cc b/chrome/browser/ash/login/session/user_session_manager.cc
index f3df4bf..0b96ae90 100644
--- a/chrome/browser/ash/login/session/user_session_manager.cc
+++ b/chrome/browser/ash/login/session/user_session_manager.cc
@@ -2202,8 +2202,15 @@
   VLOG(1) << "Launching browser...";
   TRACE_EVENT0("login", "LaunchBrowser");
 
-  if (should_launch_browser_ && !IsFullRestoreEnabled(profile))
-    LaunchBrowser(profile);
+  if (should_launch_browser_) {
+    if (!IsFullRestoreEnabled(profile)) {
+      LaunchBrowser(profile);
+      MaybeLaunchSettings(profile);
+    } else {
+      full_restore::FullRestoreService::GetForProfile(profile)
+          ->LaunchBrowserWhenReady();
+    }
+  }
 
   if (HatsNotificationController::ShouldShowSurveyToProfile(
           profile, kHatsGeneralSurvey)) {
@@ -2238,15 +2245,6 @@
 
   ShowNotificationsIfNeeded(profile);
 
-  if (should_launch_browser_) {
-    if (IsFullRestoreEnabled(profile)) {
-      full_restore::FullRestoreService::GetForProfile(profile)
-          ->LaunchBrowserWhenReady();
-    } else {
-      MaybeLaunchSettings(profile);
-    }
-  }
-
   StartAccountManagerMigration(profile);
 }
 
diff --git a/chrome/browser/ash/login/test/logged_in_user_mixin.h b/chrome/browser/ash/login/test/logged_in_user_mixin.h
index 5042137a..5b7e3b1e 100644
--- a/chrome/browser/ash/login/test/logged_in_user_mixin.h
+++ b/chrome/browser/ash/login/test/logged_in_user_mixin.h
@@ -10,6 +10,7 @@
 #include "chrome/browser/ash/login/test/local_policy_test_server_mixin.h"
 #include "chrome/browser/ash/login/test/login_manager_mixin.h"
 #include "chrome/browser/ash/login/test/user_policy_mixin.h"
+#include "chrome/browser/chromeos/full_restore/app_launch_handler.h"
 #include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -123,6 +124,9 @@
   FakeGaiaMixin fake_gaia_;
 
   InProcessBrowserTest* test_base_;
+
+  full_restore::ScopedLaunchBrowserForTesting
+      scoped_launch_browser_for_testing_;
 };
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/power/ml/BUILD.gn b/chrome/browser/ash/power/ml/BUILD.gn
similarity index 100%
rename from chrome/browser/chromeos/power/ml/BUILD.gn
rename to chrome/browser/ash/power/ml/BUILD.gn
diff --git a/chrome/browser/chromeos/power/ml/OWNERS b/chrome/browser/ash/power/ml/OWNERS
similarity index 100%
rename from chrome/browser/chromeos/power/ml/OWNERS
rename to chrome/browser/ash/power/ml/OWNERS
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.cc b/chrome/browser/ash/power/ml/adaptive_screen_brightness_manager.cc
similarity index 98%
rename from chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.cc
rename to chrome/browser/ash/power/ml/adaptive_screen_brightness_manager.cc
index 37605a4..54ae52f1 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.cc
+++ b/chrome/browser/ash/power/ml/adaptive_screen_brightness_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.h"
+#include "chrome/browser/ash/power/ml/adaptive_screen_brightness_manager.h"
 
 #include <cmath>
 #include <utility>
@@ -16,9 +16,9 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
-#include "chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger.h"
-#include "chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl.h"
-#include "chrome/browser/chromeos/power/ml/recent_events_counter.h"
+#include "chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger.h"
+#include "chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.h"
+#include "chrome/browser/ash/power/ml/recent_events_counter.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/tab_contents/form_interaction_tab_helper.h"
 #include "chrome/browser/ui/browser.h"
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.h b/chrome/browser/ash/power/ml/adaptive_screen_brightness_manager.h
similarity index 94%
rename from chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.h
rename to chrome/browser/ash/power/ml/adaptive_screen_brightness_manager.h
index 181607a..23d988f 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.h
+++ b/chrome/browser/ash/power/ml/adaptive_screen_brightness_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_MANAGER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_MANAGER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_MANAGER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_MANAGER_H_
 
 #include <memory>
 
@@ -16,8 +16,8 @@
 // chrome/browser/ash/.
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
-#include "chrome/browser/chromeos/power/ml/boot_clock.h"
-#include "chrome/browser/chromeos/power/ml/screen_brightness_event.pb.h"
+#include "chrome/browser/ash/power/ml/boot_clock.h"
+#include "chrome/browser/ash/power/ml/screen_brightness_event.pb.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -163,4 +163,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_MANAGER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_MANAGER_H_
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc b/chrome/browser/ash/power/ml/adaptive_screen_brightness_manager_unittest.cc
similarity index 98%
rename from chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
rename to chrome/browser/ash/power/ml/adaptive_screen_brightness_manager_unittest.cc
index 8f4effc..97c4294 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager_unittest.cc
+++ b/chrome/browser/ash/power/ml/adaptive_screen_brightness_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.h"
+#include "chrome/browser/ash/power/ml/adaptive_screen_brightness_manager.h"
 
 #include <memory>
 #include <vector>
@@ -12,8 +12,8 @@
 #include "chrome/browser/ash/accessibility/accessibility_manager.h"
 #include "chrome/browser/ash/accessibility/magnification_manager.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger.h"
-#include "chrome/browser/chromeos/power/ml/screen_brightness_event.pb.h"
+#include "chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger.h"
+#include "chrome/browser/ash/power/ml/screen_brightness_event.pb.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_activity_simulator.h"
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger.h b/chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger.h
similarity index 78%
rename from chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger.h
rename to chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger.h
index 61dd9be..aa206025 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger.h
+++ b/chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_H_
 
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
@@ -30,4 +30,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_H_
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl.cc b/chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.cc
similarity index 96%
rename from chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl.cc
rename to chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.cc
index e95630e..5a03d6c 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl.cc
+++ b/chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.cc
@@ -2,14 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl.h"
+#include "chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.h"
 
 #include <array>
 #include <cmath>
 
 #include "base/check.h"
-#include "chrome/browser/chromeos/power/ml/screen_brightness_event.pb.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h"
+#include "chrome/browser/ash/power/ml/screen_brightness_event.pb.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl.h b/chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.h
similarity index 75%
rename from chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl.h
rename to chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.h
index dc3481515d..bccf3e5 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl.h
+++ b/chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_IMPL_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_IMPL_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_IMPL_H_
+#define CHROME_BROWSER_ASH_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_IMPL_H_
 
 #include "base/macros.h"
-#include "chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger.h"
+#include "chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
 namespace chromeos {
@@ -36,4 +36,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_IMPL_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_ADAPTIVE_SCREEN_BRIGHTNESS_UKM_LOGGER_IMPL_H_
diff --git a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl_unittest.cc b/chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl_unittest.cc
rename to chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl_unittest.cc
index 7bff2eb..62ff9ff6 100644
--- a/chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl_unittest.cc
+++ b/chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl_unittest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/adaptive_screen_brightness_ukm_logger_impl.h"
+#include "chrome/browser/ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.h"
 
 #include <memory>
 
-#include "chrome/browser/chromeos/power/ml/screen_brightness_event.pb.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.h"
+#include "chrome/browser/ash/power/ml/screen_brightness_event.pb.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.h"
 #include "chrome/browser/ui/tabs/tab_ukm_test_helper.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
diff --git a/chrome/browser/chromeos/power/ml/boot_clock.cc b/chrome/browser/ash/power/ml/boot_clock.cc
similarity index 95%
rename from chrome/browser/chromeos/power/ml/boot_clock.cc
rename to chrome/browser/ash/power/ml/boot_clock.cc
index 40fbb21..9cef048 100644
--- a/chrome/browser/chromeos/power/ml/boot_clock.cc
+++ b/chrome/browser/ash/power/ml/boot_clock.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/boot_clock.h"
+#include "chrome/browser/ash/power/ml/boot_clock.h"
 
 #include <time.h>
 
diff --git a/chrome/browser/chromeos/power/ml/boot_clock.h b/chrome/browser/ash/power/ml/boot_clock.h
similarity index 86%
rename from chrome/browser/chromeos/power/ml/boot_clock.h
rename to chrome/browser/ash/power/ml/boot_clock.h
index 3f1ca85..7634878b 100644
--- a/chrome/browser/chromeos/power/ml/boot_clock.h
+++ b/chrome/browser/ash/power/ml/boot_clock.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_BOOT_CLOCK_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_BOOT_CLOCK_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_BOOT_CLOCK_H_
+#define CHROME_BROWSER_ASH_POWER_ML_BOOT_CLOCK_H_
 
 #include "base/macros.h"
 #include "base/time/time.h"
@@ -37,4 +37,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_BOOT_CLOCK_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_BOOT_CLOCK_H_
diff --git a/chrome/browser/chromeos/power/ml/boot_clock_unittest.cc b/chrome/browser/ash/power/ml/boot_clock_unittest.cc
similarity index 96%
rename from chrome/browser/chromeos/power/ml/boot_clock_unittest.cc
rename to chrome/browser/ash/power/ml/boot_clock_unittest.cc
index b1d768e..59fb02c 100644
--- a/chrome/browser/chromeos/power/ml/boot_clock_unittest.cc
+++ b/chrome/browser/ash/power/ml/boot_clock_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/boot_clock.h"
+#include "chrome/browser/ash/power/ml/boot_clock.h"
 
 #include "base/test/task_environment.h"
 #include "base/threading/platform_thread.h"
diff --git a/chrome/browser/chromeos/power/ml/idle_event_notifier.cc b/chrome/browser/ash/power/ml/idle_event_notifier.cc
similarity index 98%
rename from chrome/browser/chromeos/power/ml/idle_event_notifier.cc
rename to chrome/browser/ash/power/ml/idle_event_notifier.cc
index 80005896..95e90c3a 100644
--- a/chrome/browser/chromeos/power/ml/idle_event_notifier.cc
+++ b/chrome/browser/ash/power/ml/idle_event_notifier.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/idle_event_notifier.h"
+#include "chrome/browser/ash/power/ml/idle_event_notifier.h"
 
 #include "base/check.h"
 #include "base/notreached.h"
-#include "chrome/browser/chromeos/power/ml/recent_events_counter.h"
+#include "chrome/browser/ash/power/ml/recent_events_counter.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
 #include "chromeos/dbus/power_manager/suspend.pb.h"
diff --git a/chrome/browser/chromeos/power/ml/idle_event_notifier.h b/chrome/browser/ash/power/ml/idle_event_notifier.h
similarity index 95%
rename from chrome/browser/chromeos/power/ml/idle_event_notifier.h
rename to chrome/browser/ash/power/ml/idle_event_notifier.h
index 2bbe42e..933d9bd 100644
--- a/chrome/browser/chromeos/power/ml/idle_event_notifier.h
+++ b/chrome/browser/ash/power/ml/idle_event_notifier.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_IDLE_EVENT_NOTIFIER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_IDLE_EVENT_NOTIFIER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_IDLE_EVENT_NOTIFIER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_IDLE_EVENT_NOTIFIER_H_
 
 #include <memory>
 
@@ -11,8 +11,8 @@
 #include "base/observer_list.h"
 #include "base/scoped_observation.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/power/ml/boot_clock.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
+#include "chrome/browser/ash/power/ml/boot_clock.h"
+#include "chrome/browser/ash/power/ml/user_activity_event.pb.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -177,4 +177,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_IDLE_EVENT_NOTIFIER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_IDLE_EVENT_NOTIFIER_H_
diff --git a/chrome/browser/chromeos/power/ml/idle_event_notifier_unittest.cc b/chrome/browser/ash/power/ml/idle_event_notifier_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/power/ml/idle_event_notifier_unittest.cc
rename to chrome/browser/ash/power/ml/idle_event_notifier_unittest.cc
index 2ba06848..095cc6b 100644
--- a/chrome/browser/chromeos/power/ml/idle_event_notifier_unittest.cc
+++ b/chrome/browser/ash/power/ml/idle_event_notifier_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/idle_event_notifier.h"
+#include "chrome/browser/ash/power/ml/idle_event_notifier.h"
 
 #include <memory>
 
diff --git a/chrome/browser/chromeos/power/ml/recent_events_counter.cc b/chrome/browser/ash/power/ml/recent_events_counter.cc
similarity index 97%
rename from chrome/browser/chromeos/power/ml/recent_events_counter.cc
rename to chrome/browser/ash/power/ml/recent_events_counter.cc
index 16f3df49..1b2fdb4 100644
--- a/chrome/browser/chromeos/power/ml/recent_events_counter.cc
+++ b/chrome/browser/ash/power/ml/recent_events_counter.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/recent_events_counter.h"
+#include "chrome/browser/ash/power/ml/recent_events_counter.h"
 
 #include <algorithm>
 
diff --git a/chrome/browser/chromeos/power/ml/recent_events_counter.h b/chrome/browser/ash/power/ml/recent_events_counter.h
similarity index 92%
rename from chrome/browser/chromeos/power/ml/recent_events_counter.h
rename to chrome/browser/ash/power/ml/recent_events_counter.h
index b1345b5a..ab88e38 100644
--- a/chrome/browser/chromeos/power/ml/recent_events_counter.h
+++ b/chrome/browser/ash/power/ml/recent_events_counter.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_RECENT_EVENTS_COUNTER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_RECENT_EVENTS_COUNTER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_RECENT_EVENTS_COUNTER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_RECENT_EVENTS_COUNTER_H_
 
 #include <vector>
 
@@ -71,4 +71,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_RECENT_EVENTS_COUNTER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_RECENT_EVENTS_COUNTER_H_
diff --git a/chrome/browser/chromeos/power/ml/recent_events_counter_unittest.cc b/chrome/browser/ash/power/ml/recent_events_counter_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/power/ml/recent_events_counter_unittest.cc
rename to chrome/browser/ash/power/ml/recent_events_counter_unittest.cc
index c006ad1..51cf9a0 100644
--- a/chrome/browser/chromeos/power/ml/recent_events_counter_unittest.cc
+++ b/chrome/browser/ash/power/ml/recent_events_counter_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/recent_events_counter.h"
+#include "chrome/browser/ash/power/ml/recent_events_counter.h"
 
 #include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/chromeos/power/ml/screen_brightness_event.proto b/chrome/browser/ash/power/ml/screen_brightness_event.proto
similarity index 100%
rename from chrome/browser/chromeos/power/ml/screen_brightness_event.proto
rename to chrome/browser/ash/power/ml/screen_brightness_event.proto
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/20190521_example_preprocessor_config.pb b/chrome/browser/ash/power/ml/smart_dim/20190521_example_preprocessor_config.pb
similarity index 100%
rename from chrome/browser/chromeos/power/ml/smart_dim/20190521_example_preprocessor_config.pb
rename to chrome/browser/ash/power/ml/smart_dim/20190521_example_preprocessor_config.pb
Binary files differ
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/BUILD.gn b/chrome/browser/ash/power/ml/smart_dim/BUILD.gn
similarity index 92%
rename from chrome/browser/chromeos/power/ml/smart_dim/BUILD.gn
rename to chrome/browser/ash/power/ml/smart_dim/BUILD.gn
index 7c6ba4ff..ae574d5 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/BUILD.gn
+++ b/chrome/browser/ash/power/ml/smart_dim/BUILD.gn
@@ -26,8 +26,8 @@
     "//ash/constants",
     "//base",
     "//chrome/browser:resources",
+    "//chrome/browser/ash/power/ml:user_activity_ukm_logger_helpers",
     "//chrome/browser/chromeos:user_activity_event_proto",
-    "//chrome/browser/chromeos/power/ml:user_activity_ukm_logger_helpers",
     "//chrome/common:constants",
     "//chromeos:chromeos",
     "//chromeos/services/machine_learning/public/cpp",
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/README.md b/chrome/browser/ash/power/ml/smart_dim/README.md
similarity index 97%
rename from chrome/browser/chromeos/power/ml/smart_dim/README.md
rename to chrome/browser/ash/power/ml/smart_dim/README.md
index 6b08c04..c923ffd 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/README.md
+++ b/chrome/browser/ash/power/ml/smart_dim/README.md
@@ -44,7 +44,7 @@
 ```shell
 ./components/assist_ranker/print_example_preprocessor_config.py \
   out/Release \
-  chrome/browser/chromeos/power/ml/smart_dim/example_preprocessor_config.pb
+  chrome/browser/ash/power/ml/smart_dim/example_preprocessor_config.pb
 ```
 
 ## Smart dim model interface
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/builtin_worker.cc b/chrome/browser/ash/power/ml/smart_dim/builtin_worker.cc
similarity index 95%
rename from chrome/browser/chromeos/power/ml/smart_dim/builtin_worker.cc
rename to chrome/browser/ash/power/ml/smart_dim/builtin_worker.cc
index e722733..da9b83d 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/builtin_worker.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/builtin_worker.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/smart_dim/builtin_worker.h"
+#include "chrome/browser/ash/power/ml/smart_dim/builtin_worker.h"
 
 #include "ash/constants/ash_features.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/memory/ref_counted_memory.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent_util.h"
 #include "chrome/grit/browser_resources.h"
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
 #include "components/assist_ranker/proto/example_preprocessor.pb.h"
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/builtin_worker.h b/chrome/browser/ash/power/ml/smart_dim/builtin_worker.h
similarity index 79%
rename from chrome/browser/chromeos/power/ml/smart_dim/builtin_worker.h
rename to chrome/browser/ash/power/ml/smart_dim/builtin_worker.h
index 970dbd9..b2f97d3 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/builtin_worker.h
+++ b/chrome/browser/ash/power/ml/smart_dim/builtin_worker.h
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_BUILTIN_WORKER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_BUILTIN_WORKER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_BUILTIN_WORKER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_BUILTIN_WORKER_H_
 
-#include "chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.h"
+#include "chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.h"
 #include "chromeos/services/machine_learning/public/mojom/graph_executor.mojom.h"
 #include "chromeos/services/machine_learning/public/mojom/model.mojom.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -39,4 +39,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_BUILTIN_WORKER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_BUILTIN_WORKER_H_
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/download_worker.cc b/chrome/browser/ash/power/ml/smart_dim/download_worker.cc
similarity index 95%
rename from chrome/browser/chromeos/power/ml/smart_dim/download_worker.cc
rename to chrome/browser/ash/power/ml/smart_dim/download_worker.cc
index 93a34ee..7482216 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/download_worker.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/download_worker.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/smart_dim/download_worker.h"
+#include "chrome/browser/ash/power/ml/smart_dim/download_worker.h"
 
 #include "base/bind.h"
 #include "base/task/task_traits.h"
 #include "base/threading/sequenced_task_runner_handle.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/metrics.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.h"
+#include "chrome/browser/ash/power/ml/smart_dim/metrics.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent_util.h"
 #include "chromeos/services/machine_learning/public/cpp/service_connection.h"
 #include "components/assist_ranker/proto/example_preprocessor.pb.h"
 #include "content/public/browser/browser_task_traits.h"
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/download_worker.h b/chrome/browser/ash/power/ml/smart_dim/download_worker.h
similarity index 87%
rename from chrome/browser/chromeos/power/ml/smart_dim/download_worker.h
rename to chrome/browser/ash/power/ml/smart_dim/download_worker.h
index d47e75a..a4a9a5f3 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/download_worker.h
+++ b/chrome/browser/ash/power/ml/smart_dim/download_worker.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_DOWNLOAD_WORKER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_DOWNLOAD_WORKER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_DOWNLOAD_WORKER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_DOWNLOAD_WORKER_H_
 
 #include "base/containers/flat_map.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.h"
+#include "chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
 
 namespace chromeos {
@@ -61,4 +61,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_DOWNLOAD_WORKER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_DOWNLOAD_WORKER_H_
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/metrics.cc b/chrome/browser/ash/power/ml/smart_dim/metrics.cc
similarity index 93%
rename from chrome/browser/chromeos/power/ml/smart_dim/metrics.cc
rename to chrome/browser/ash/power/ml/smart_dim/metrics.cc
index 05d6a734..995b2a8 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/metrics.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/metrics.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/smart_dim/metrics.h"
+#include "chrome/browser/ash/power/ml/smart_dim/metrics.h"
 
 #include "base/metrics/histogram_macros.h"
 
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/metrics.h b/chrome/browser/ash/power/ml/smart_dim/metrics.h
similarity index 89%
rename from chrome/browser/chromeos/power/ml/smart_dim/metrics.h
rename to chrome/browser/ash/power/ml/smart_dim/metrics.h
index fbe0a11..885f2c4 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/metrics.h
+++ b/chrome/browser/ash/power/ml/smart_dim/metrics.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_METRICS_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_METRICS_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_METRICS_H_
+#define CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_METRICS_H_
 
 namespace chromeos {
 namespace power {
@@ -67,4 +67,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_METRICS_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_METRICS_H_
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent.cc b/chrome/browser/ash/power/ml/smart_dim/ml_agent.cc
similarity index 97%
rename from chrome/browser/chromeos/power/ml/smart_dim/ml_agent.cc
rename to chrome/browser/ash/power/ml/smart_dim/ml_agent.cc
index e5ab43dd..84189241 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/ml_agent.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent.h"
 
 #include <cstddef>
 #include <memory>
@@ -10,9 +10,9 @@
 #include "ash/constants/ash_features.h"
 #include "base/containers/flat_map.h"
 #include "base/metrics/field_trial_params.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/metrics.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h"
+#include "chrome/browser/ash/power/ml/smart_dim/metrics.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent_util.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h"
 #include "chromeos/services/machine_learning/public/mojom/graph_executor.mojom.h"
 #include "chromeos/services/machine_learning/public/mojom/model.mojom.h"
 #include "chromeos/services/machine_learning/public/mojom/tensor.mojom.h"
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h b/chrome/browser/ash/power/ml/smart_dim/ml_agent.h
similarity index 81%
rename from chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h
rename to chrome/browser/ash/power/ml/smart_dim/ml_agent.h
index 528796227..b194aa47 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h
+++ b/chrome/browser/ash/power/ml/smart_dim/ml_agent.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_ML_AGENT_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_ML_AGENT_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_ML_AGENT_H_
+#define CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_ML_AGENT_H_
 
 #include "base/cancelable_callback.h"
 #include "base/no_destructor.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/builtin_worker.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/download_worker.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
+#include "chrome/browser/ash/power/ml/smart_dim/builtin_worker.h"
+#include "chrome/browser/ash/power/ml/smart_dim/download_worker.h"
+#include "chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.h"
+#include "chrome/browser/ash/power/ml/user_activity_event.pb.h"
 
 namespace chromeos {
 namespace power {
@@ -72,4 +72,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_ML_AGENT_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_ML_AGENT_H_
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent_unittest.cc b/chrome/browser/ash/power/ml/smart_dim/ml_agent_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/power/ml/smart_dim/ml_agent_unittest.cc
rename to chrome/browser/ash/power/ml/smart_dim/ml_agent_unittest.cc
index 1fe74a9f..310f1a8 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent_unittest.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/ml_agent_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent.h"
 
 #include <memory>
 
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.cc b/chrome/browser/ash/power/ml/smart_dim/ml_agent_util.cc
similarity index 97%
rename from chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.cc
rename to chrome/browser/ash/power/ml/smart_dim/ml_agent_util.cc
index 5563e12..bd83da7 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/ml_agent_util.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent_util.h"
 
 #include <string>
 #include <vector>
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.h b/chrome/browser/ash/power/ml/smart_dim/ml_agent_util.h
similarity index 84%
rename from chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.h
rename to chrome/browser/ash/power/ml/smart_dim/ml_agent_util.h
index 309f76a5..93562ec 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.h
+++ b/chrome/browser/ash/power/ml/smart_dim/ml_agent_util.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_ML_AGENT_UTIL_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_ML_AGENT_UTIL_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_ML_AGENT_UTIL_H_
+#define CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_ML_AGENT_UTIL_H_
 
 #include <string>
 
@@ -32,4 +32,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_ML_AGENT_UTIL_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_ML_AGENT_UTIL_H_
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util_unittest.cc b/chrome/browser/ash/power/ml/smart_dim/ml_agent_util_unittest.cc
similarity index 97%
rename from chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util_unittest.cc
rename to chrome/browser/ash/power/ml/smart_dim/ml_agent_util_unittest.cc
index 4ed1c5a..3ac7b8f 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util_unittest.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/ml_agent_util_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent_util.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent_util.h"
 
 #include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.cc b/chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.cc
similarity index 92%
rename from chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.cc
rename to chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.cc
index 8f66f997..5f616d0 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.cc
+++ b/chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.h"
+#include "chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.h"
 
 #include "components/assist_ranker/proto/example_preprocessor.pb.h"
 
diff --git a/chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.h b/chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.h
similarity index 92%
rename from chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.h
rename to chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.h
index 1eef7139..ed35ecf 100644
--- a/chrome/browser/chromeos/power/ml/smart_dim/smart_dim_worker.h
+++ b/chrome/browser/ash/power/ml/smart_dim/smart_dim_worker.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_SMART_DIM_WORKER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_SMART_DIM_WORKER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_SMART_DIM_WORKER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_SMART_DIM_WORKER_H_
 
 #include <memory>
 
@@ -72,4 +72,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_SMART_DIM_SMART_DIM_WORKER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_SMART_DIM_SMART_DIM_WORKER_H_
diff --git a/chrome/browser/chromeos/power/ml/user_activity_controller.cc b/chrome/browser/ash/power/ml/user_activity_controller.cc
similarity index 97%
rename from chrome/browser/chromeos/power/ml/user_activity_controller.cc
rename to chrome/browser/ash/power/ml/user_activity_controller.cc
index 6395e472..488b474b 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_controller.cc
+++ b/chrome/browser/ash/power/ml/user_activity_controller.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/user_activity_controller.h"
+#include "chrome/browser/ash/power/ml/user_activity_controller.h"
 
 #include "base/feature_list.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager.h"
diff --git a/chrome/browser/chromeos/power/ml/user_activity_controller.h b/chrome/browser/ash/power/ml/user_activity_controller.h
similarity index 70%
rename from chrome/browser/chromeos/power/ml/user_activity_controller.h
rename to chrome/browser/ash/power/ml/user_activity_controller.h
index 7a6f7d4..3ed19e6 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_controller.h
+++ b/chrome/browser/ash/power/ml/user_activity_controller.h
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_CONTROLLER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_CONTROLLER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_CONTROLLER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_CONTROLLER_H_
 
 #include <memory>
 
 #include "base/callback.h"
-#include "chrome/browser/chromeos/power/ml/idle_event_notifier.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_manager.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.h"
+#include "chrome/browser/ash/power/ml/idle_event_notifier.h"
+#include "chrome/browser/ash/power/ml/user_activity_manager.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.h"
 
 namespace chromeos {
 namespace power {
@@ -40,4 +40,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_CONTROLLER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_CONTROLLER_H_
diff --git a/chrome/browser/chromeos/power/ml/user_activity_event.proto b/chrome/browser/ash/power/ml/user_activity_event.proto
similarity index 100%
rename from chrome/browser/chromeos/power/ml/user_activity_event.proto
rename to chrome/browser/ash/power/ml/user_activity_event.proto
diff --git a/chrome/browser/chromeos/power/ml/user_activity_manager.cc b/chrome/browser/ash/power/ml/user_activity_manager.cc
similarity index 99%
rename from chrome/browser/chromeos/power/ml/user_activity_manager.cc
rename to chrome/browser/ash/power/ml/user_activity_manager.cc
index cc32fb62..abfc953 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_manager.cc
+++ b/chrome/browser/ash/power/ml/user_activity_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/user_activity_manager.h"
+#include "chrome/browser/ash/power/ml/user_activity_manager.h"
 
 #include <cmath>
 
@@ -13,7 +13,7 @@
 #include "base/metrics/field_trial_params.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/string_number_conversions.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/resource_coordinator/tab_metrics_logger.h"
diff --git a/chrome/browser/chromeos/power/ml/user_activity_manager.h b/chrome/browser/ash/power/ml/user_activity_manager.h
similarity index 95%
rename from chrome/browser/chromeos/power/ml/user_activity_manager.h
rename to chrome/browser/ash/power/ml/user_activity_manager.h
index c9e6d1b0..f229316 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_manager.h
+++ b/chrome/browser/ash/power/ml/user_activity_manager.h
@@ -2,18 +2,18 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_MANAGER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_MANAGER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_MANAGER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_MANAGER_H_
 
 #include "base/macros.h"
 #include "base/scoped_observation.h"
 #include "base/sequenced_task_runner.h"
 #include "base/time/time.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager.h"
-#include "chrome/browser/chromeos/power/ml/boot_clock.h"
-#include "chrome/browser/chromeos/power/ml/idle_event_notifier.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger.h"
+#include "chrome/browser/ash/power/ml/boot_clock.h"
+#include "chrome/browser/ash/power/ml/idle_event_notifier.h"
+#include "chrome/browser/ash/power/ml/user_activity_event.pb.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger.h"
 #include "chrome/browser/resource_coordinator/tab_metrics_event.pb.h"
 #include "chromeos/dbus/power/power_manager_client.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
@@ -254,4 +254,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_MANAGER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_MANAGER_H_
diff --git a/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc b/chrome/browser/ash/power/ml/user_activity_manager_unittest.cc
similarity index 99%
rename from chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc
rename to chrome/browser/ash/power/ml/user_activity_manager_unittest.cc
index cdd4f98a..b7cb383 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_manager_unittest.cc
+++ b/chrome/browser/ash/power/ml/user_activity_manager_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/user_activity_manager.h"
+#include "chrome/browser/ash/power/ml/user_activity_manager.h"
 
 #include <map>
 #include <memory>
@@ -18,10 +18,10 @@
 #include "base/test/task_environment.h"
 #include "base/timer/timer.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
-#include "chrome/browser/chromeos/power/ml/idle_event_notifier.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger.h"
+#include "chrome/browser/ash/power/ml/idle_event_notifier.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent.h"
+#include "chrome/browser/ash/power/ml/user_activity_event.pb.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_activity_simulator.h"
diff --git a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger.h b/chrome/browser/ash/power/ml/user_activity_ukm_logger.h
similarity index 74%
rename from chrome/browser/chromeos/power/ml/user_activity_ukm_logger.h
rename to chrome/browser/ash/power/ml/user_activity_ukm_logger.h
index b83ca980..af1020cb 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger.h
+++ b/chrome/browser/ash/power/ml/user_activity_ukm_logger.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_UKM_LOGGER_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_UKM_LOGGER_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_UKM_LOGGER_H_
+#define CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_UKM_LOGGER_H_
 
 #include "services/metrics/public/cpp/ukm_source_id.h"
 
@@ -26,4 +26,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_UKM_LOGGER_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_UKM_LOGGER_H_
diff --git a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.cc b/chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.cc
similarity index 96%
rename from chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.cc
rename to chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.cc
index 41cb29be3..6d012e4 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.cc
+++ b/chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.cc
@@ -2,11 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h"
 
 #include <array>
 
-
 namespace chromeos {
 namespace power {
 namespace ml {
diff --git a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h b/chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h
similarity index 90%
rename from chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h
rename to chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h
index 1150d94..3839031 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h
+++ b/chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_UKM_LOGGER_HELPERS_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_UKM_LOGGER_HELPERS_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_UKM_LOGGER_HELPERS_H_
+#define CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_UKM_LOGGER_HELPERS_H_
 
 #include <map>
 #include <string>
@@ -11,7 +11,7 @@
 #include "base/check_op.h"
 #include "base/logging.h"
 #include "base/macros.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
+#include "chrome/browser/ash/power/ml/user_activity_event.pb.h"
 
 namespace chromeos {
 namespace power {
@@ -81,4 +81,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_UKM_LOGGER_HELPERS_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_UKM_LOGGER_HELPERS_H_
diff --git a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers_unittest.cc b/chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers_unittest.cc
similarity index 95%
rename from chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers_unittest.cc
rename to chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers_unittest.cc
index 74ad0ea..ae1e2168 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers_unittest.cc
+++ b/chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers_unittest.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h"
 
 #include <array>
 #include <memory>
 
-#include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
+#include "chrome/browser/ash/power/ml/user_activity_event.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace chromeos {
diff --git a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.cc b/chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.cc
similarity index 94%
rename from chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.cc
rename to chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.cc
index b403ceb..e97206a 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.cc
+++ b/chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.cc
@@ -4,10 +4,10 @@
 
 #include <cmath>
 
-#include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_manager.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.h"
+#include "chrome/browser/ash/power/ml/user_activity_event.pb.h"
+#include "chrome/browser/ash/power/ml/user_activity_manager.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.h"
 #include "content/public/browser/web_contents.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "ui/gfx/native_widget_types.h"
diff --git a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.h b/chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.h
similarity index 76%
rename from chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.h
rename to chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.h
index 2974291..1ebe4d2 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.h
+++ b/chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.h
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_UKM_LOGGER_IMPL_H_
-#define CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_UKM_LOGGER_IMPL_H_
+#ifndef CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_UKM_LOGGER_IMPL_H_
+#define CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_UKM_LOGGER_IMPL_H_
 
 #include "base/macros.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger.h"
 #include "services/metrics/public/cpp/ukm_recorder.h"
 
 namespace chromeos {
@@ -39,4 +39,4 @@
 }  // namespace power
 }  // namespace chromeos
 
-#endif  // CHROME_BROWSER_CHROMEOS_POWER_ML_USER_ACTIVITY_UKM_LOGGER_IMPL_H_
+#endif  // CHROME_BROWSER_ASH_POWER_ML_USER_ACTIVITY_UKM_LOGGER_IMPL_H_
diff --git a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_unittest.cc b/chrome/browser/ash/power/ml/user_activity_ukm_logger_unittest.cc
similarity index 98%
rename from chrome/browser/chromeos/power/ml/user_activity_ukm_logger_unittest.cc
rename to chrome/browser/ash/power/ml/user_activity_ukm_logger_unittest.cc
index cd333aff..3515116 100644
--- a/chrome/browser/chromeos/power/ml/user_activity_ukm_logger_unittest.cc
+++ b/chrome/browser/ash/power/ml/user_activity_ukm_logger_unittest.cc
@@ -2,13 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_impl.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_impl.h"
 
 #include <memory>
 #include <vector>
 
-#include "chrome/browser/chromeos/power/ml/user_activity_event.pb.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_manager.h"
+#include "chrome/browser/ash/power/ml/user_activity_event.pb.h"
+#include "chrome/browser/ash/power/ml/user_activity_manager.h"
 #include "components/ukm/test_ukm_recorder.h"
 #include "services/metrics/public/cpp/ukm_builders.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 7e749b0..c914dfd0f 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -425,7 +425,7 @@
         <include name="IDR_TAB_RANKER_PAIRWISE_EXAMPLE_PREPROCESSOR_CONFIG_PB" file="resource_coordinator\tab_ranker\pairwise_preprocessor_config.pb" type="BINDATA" />
       </if>
       <if expr="chromeos">
-        <include name="IDR_SMART_DIM_20190521_EXAMPLE_PREPROCESSOR_CONFIG_PB" file="chromeos\power\ml\smart_dim\20190521_example_preprocessor_config.pb" type="BINDATA" />
+        <include name="IDR_SMART_DIM_20190521_EXAMPLE_PREPROCESSOR_CONFIG_PB" file="ash\power\ml\smart_dim\20190521_example_preprocessor_config.pb" type="BINDATA" />
         <include name="IDR_SEARCH_RANKER_20190923_EXAMPLE_PREPROCESSOR_CONFIG_PB" file="ui\app_list\search\search_result_ranker\search_ranker_assets\20190923_example_preprocessor_config.pb" type="BINDATA" />
       </if>
       <if expr="chromeos">
diff --git a/chrome/browser/chrome_browser_interface_binders.cc b/chrome/browser/chrome_browser_interface_binders.cc
index 450df5d..488c8e0 100644
--- a/chrome/browser/chrome_browser_interface_binders.cc
+++ b/chrome/browser/chrome_browser_interface_binders.cc
@@ -142,6 +142,7 @@
 #include "media/base/media_switches.h"
 #include "media/mojo/mojom/speech_recognition_service.mojom.h"
 #include "mojo/public/cpp/bindings/self_owned_receiver.h"
+#include "ui/webui/resources/cr_components/most_visited/most_visited.mojom.h"
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_WIN) || defined(OS_MAC) || defined(OS_LINUX) || \
@@ -657,6 +658,9 @@
   RegisterWebUIControllerInterfaceBinder<
       new_tab_page::mojom::PageHandlerFactory, NewTabPageUI>(map);
 
+  RegisterWebUIControllerInterfaceBinder<
+      most_visited::mojom::MostVisitedPageHandlerFactory, NewTabPageUI>(map);
+
   RegisterWebUIControllerInterfaceBinder<history_clusters::mojom::PageHandler,
                                          MemoriesUI>(map);
 
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 0e4427a..73f27ef 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -31,7 +31,7 @@
     "//chrome/app/resources:platform_locale_settings",
     "//chrome/app/theme:chrome_unscaled_resources",
     "//chrome/app/theme:theme_resources",
-    "//chrome/browser/chromeos/power/ml:user_activity_ukm_logger_helpers",
+    "//chrome/browser/ash/power/ml:user_activity_ukm_logger_helpers",
     "//chrome/browser/ui/webui/chromeos/crostini_installer:mojo_bindings",
     "//chrome/browser/ui/webui/chromeos/emoji:mojo_bindings",
     "//chromeos/components/security_token_pin",
@@ -105,10 +105,10 @@
     "//chrome/browser/ash/child_accounts/time_limits/web_time_limit_error_page",
     "//chrome/browser/ash/crosapi",
     "//chrome/browser/ash/crostini:crostini_installer_types_mojom",
+    "//chrome/browser/ash/power/ml/smart_dim",
     "//chrome/browser/ash/wilco_dtc_supportd:mojo_utils",
     "//chrome/browser/browsing_data:constants",
     "//chrome/browser/chromeos/nearby:bluetooth_adapter_manager",
-    "//chrome/browser/chromeos/power/ml/smart_dim",
     "//chrome/browser/devtools",
     "//chrome/browser/extensions",
     "//chrome/browser/image_decoder",
@@ -1840,6 +1840,24 @@
     "../ash/power/auto_screen_brightness/trainer.h",
     "../ash/power/auto_screen_brightness/utils.cc",
     "../ash/power/auto_screen_brightness/utils.h",
+    "../ash/power/ml/adaptive_screen_brightness_manager.cc",
+    "../ash/power/ml/adaptive_screen_brightness_manager.h",
+    "../ash/power/ml/adaptive_screen_brightness_ukm_logger.h",
+    "../ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.cc",
+    "../ash/power/ml/adaptive_screen_brightness_ukm_logger_impl.h",
+    "../ash/power/ml/boot_clock.cc",
+    "../ash/power/ml/boot_clock.h",
+    "../ash/power/ml/idle_event_notifier.cc",
+    "../ash/power/ml/idle_event_notifier.h",
+    "../ash/power/ml/recent_events_counter.cc",
+    "../ash/power/ml/recent_events_counter.h",
+    "../ash/power/ml/user_activity_controller.cc",
+    "../ash/power/ml/user_activity_controller.h",
+    "../ash/power/ml/user_activity_manager.cc",
+    "../ash/power/ml/user_activity_manager.h",
+    "../ash/power/ml/user_activity_ukm_logger.h",
+    "../ash/power/ml/user_activity_ukm_logger_impl.cc",
+    "../ash/power/ml/user_activity_ukm_logger_impl.h",
     "../ash/profiles/profile_helper.cc",
     "../ash/profiles/profile_helper.h",
     "../ash/release_notes/release_notes_notification.cc",
@@ -2373,6 +2391,8 @@
     "input_method/ui/candidate_window_constants.h",
     "input_method/ui/candidate_window_view.cc",
     "input_method/ui/candidate_window_view.h",
+    "input_method/ui/grammar_suggestion_window.cc",
+    "input_method/ui/grammar_suggestion_window.h",
     "input_method/ui/infolist_window.cc",
     "input_method/ui/infolist_window.h",
     "input_method/ui/input_method_menu_item.cc",
@@ -2846,24 +2866,6 @@
     "power/idle_action_warning_dialog_view.h",
     "power/idle_action_warning_observer.cc",
     "power/idle_action_warning_observer.h",
-    "power/ml/adaptive_screen_brightness_manager.cc",
-    "power/ml/adaptive_screen_brightness_manager.h",
-    "power/ml/adaptive_screen_brightness_ukm_logger.h",
-    "power/ml/adaptive_screen_brightness_ukm_logger_impl.cc",
-    "power/ml/adaptive_screen_brightness_ukm_logger_impl.h",
-    "power/ml/boot_clock.cc",
-    "power/ml/boot_clock.h",
-    "power/ml/idle_event_notifier.cc",
-    "power/ml/idle_event_notifier.h",
-    "power/ml/recent_events_counter.cc",
-    "power/ml/recent_events_counter.h",
-    "power/ml/user_activity_controller.cc",
-    "power/ml/user_activity_controller.h",
-    "power/ml/user_activity_manager.cc",
-    "power/ml/user_activity_manager.h",
-    "power/ml/user_activity_ukm_logger.h",
-    "power/ml/user_activity_ukm_logger_impl.cc",
-    "power/ml/user_activity_ukm_logger_impl.h",
     "power/power_data_collector.cc",
     "power/power_data_collector.h",
     "power/power_metrics_reporter.cc",
@@ -3861,6 +3863,16 @@
     "../ash/power/auto_screen_brightness/model_config_loader_impl_unittest.cc",
     "../ash/power/auto_screen_brightness/modeller_impl_unittest.cc",
     "../ash/power/auto_screen_brightness/monotone_cubic_spline_unittest.cc",
+    "../ash/power/ml/adaptive_screen_brightness_manager_unittest.cc",
+    "../ash/power/ml/adaptive_screen_brightness_ukm_logger_impl_unittest.cc",
+    "../ash/power/ml/boot_clock_unittest.cc",
+    "../ash/power/ml/idle_event_notifier_unittest.cc",
+    "../ash/power/ml/recent_events_counter_unittest.cc",
+    "../ash/power/ml/smart_dim/ml_agent_unittest.cc",
+    "../ash/power/ml/smart_dim/ml_agent_util_unittest.cc",
+    "../ash/power/ml/user_activity_manager_unittest.cc",
+    "../ash/power/ml/user_activity_ukm_logger_helpers_unittest.cc",
+    "../ash/power/ml/user_activity_ukm_logger_unittest.cc",
     "../ash/release_notes/release_notes_notification_unittest.cc",
     "../ash/release_notes/release_notes_storage_unittest.cc",
     "../ash/remote_apps/remote_apps_model_unittest.cc",
@@ -3998,6 +4010,7 @@
     "input_method/suggestions_service_client_unittest.cc",
     "input_method/ui/candidate_view_unittest.cc",
     "input_method/ui/candidate_window_view_unittest.cc",
+    "input_method/ui/grammar_suggestion_window_unittest.cc",
     "input_method/ui/input_method_menu_item_unittest.cc",
     "input_method/ui/input_method_menu_manager_unittest.cc",
     "input_method/ui/suggestion_window_view_unittest.cc",
@@ -4140,16 +4153,6 @@
     "policy/user_cloud_policy_token_forwarder_unittest.cc",
     "power/cpu_data_collector_unittest.cc",
     "power/extension_event_observer_unittest.cc",
-    "power/ml/adaptive_screen_brightness_manager_unittest.cc",
-    "power/ml/adaptive_screen_brightness_ukm_logger_impl_unittest.cc",
-    "power/ml/boot_clock_unittest.cc",
-    "power/ml/idle_event_notifier_unittest.cc",
-    "power/ml/recent_events_counter_unittest.cc",
-    "power/ml/smart_dim/ml_agent_unittest.cc",
-    "power/ml/smart_dim/ml_agent_util_unittest.cc",
-    "power/ml/user_activity_manager_unittest.cc",
-    "power/ml/user_activity_ukm_logger_helpers_unittest.cc",
-    "power/ml/user_activity_ukm_logger_unittest.cc",
     "power/power_data_collector_unittest.cc",
     "power/power_metrics_reporter_unittest.cc",
     "power/process_data_collector_unittest.cc",
@@ -4437,7 +4440,7 @@
 }
 
 proto_library("screen_brightness_event_proto") {
-  sources = [ "power/ml/screen_brightness_event.proto" ]
+  sources = [ "../ash/power/ml/screen_brightness_event.proto" ]
 }
 
 proto_library("user_charging_event_proto") {
@@ -4445,7 +4448,7 @@
 }
 
 proto_library("user_activity_event_proto") {
-  sources = [ "power/ml/user_activity_event.proto" ]
+  sources = [ "../ash/power/ml/user_activity_event.proto" ]
 }
 
 proto_library("backdrop_wallpaper_proto") {
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 379f56a..c40ae46 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -72,6 +72,7 @@
 #include "chrome/browser/ash/notifications/low_disk_notification.h"
 #include "chrome/browser/ash/ownership/owner_settings_service_ash_factory.h"
 #include "chrome/browser/ash/power/auto_screen_brightness/controller.h"
+#include "chrome/browser/ash/power/ml/adaptive_screen_brightness_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/settings/device_settings_service.h"
 #include "chrome/browser/ash/settings/shutdown_policy_forwarder.h"
@@ -128,7 +129,6 @@
 #include "chrome/browser/chromeos/policy/lock_to_single_user_manager.h"
 #include "chrome/browser/chromeos/power/freezer_cgroup_process_manager.h"
 #include "chrome/browser/chromeos/power/idle_action_warning_observer.h"
-#include "chrome/browser/chromeos/power/ml/adaptive_screen_brightness_manager.h"
 #include "chrome/browser/chromeos/power/power_data_collector.h"
 #include "chrome/browser/chromeos/power/power_metrics_reporter.h"
 #include "chrome/browser/chromeos/power/process_data_collector.h"
@@ -1192,9 +1192,10 @@
 
   BootTimesRecorder::Get()->AddLogoutTimeMarker("UIMessageLoopEnded", true);
 
+  // This needs to be called before the
+  // ChromeBrowserMainPartsLinux::PostMainMessageLoopRun, because the
+  // SessionControllerClientImpl is destroyed there.
   browser_manager_->RemoveObserver(SessionControllerClientImpl::Get());
-  browser_manager_.reset();
-  crosapi_manager_.reset();
 
   if (lock_screen_apps_state_controller_)
     lock_screen_apps_state_controller_->Shutdown();
@@ -1339,6 +1340,11 @@
   // NOTE: Closes ash and destroys ash::Shell.
   ChromeBrowserMainPartsLinux::PostMainMessageLoopRun();
 
+  // BrowserManager and CrosapiManager need to outlive the Profile, which
+  // is destroyed inside ChromeBrowserMainPartsLinux::PostMainMessageLoopRun().
+  browser_manager_.reset();
+  crosapi_manager_.reset();
+
   // Destroy classes that may have ash observers or dependencies.
   arc_kiosk_app_manager_.reset();
   web_kiosk_app_manager_.reset();
diff --git a/chrome/browser/chromeos/dbus/encrypted_reporting_service_provider.cc b/chrome/browser/chromeos/dbus/encrypted_reporting_service_provider.cc
index b381413..d19c9905 100644
--- a/chrome/browser/chromeos/dbus/encrypted_reporting_service_provider.cc
+++ b/chrome/browser/chromeos/dbus/encrypted_reporting_service_provider.cc
@@ -180,9 +180,10 @@
             if (!self) {
               return;  // Provider expired
             }
-            self->build_cloud_policy_client_cb_.Run(
+            self->build_cloud_policy_client_cb_.Run(base::BindPostTask(
+                self->sequenced_task_runner_,
                 base::BindOnce(&UploadHelper::OnCloudPolicyClientResult,
-                               self->weak_ptr_factory_.GetWeakPtr()));
+                               self->weak_ptr_factory_.GetWeakPtr())));
           },
           weak_ptr_factory_.GetWeakPtr()),
       backoff_entry_->GetTimeUntilRelease());
diff --git a/chrome/browser/chromeos/dbus/machine_learning_decision_service_provider.cc b/chrome/browser/chromeos/dbus/machine_learning_decision_service_provider.cc
index 23c28d8..58f8d659 100644
--- a/chrome/browser/chromeos/dbus/machine_learning_decision_service_provider.cc
+++ b/chrome/browser/chromeos/dbus/machine_learning_decision_service_provider.cc
@@ -6,7 +6,7 @@
 
 #include "base/bind.h"
 #include "base/feature_list.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_controller.h"
+#include "chrome/browser/ash/power/ml/user_activity_controller.h"
 #include "chrome/common/chrome_features.h"
 #include "dbus/bus.h"
 #include "dbus/message.h"
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
index b95243d3..b69fa3df 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_misc.cc
@@ -407,7 +407,6 @@
 
   // Tell the ZipFileCreator to stop.
   creator->Stop();
-  DCHECK_EQ(zip_creators->count(dest_file), 0);
 
   return RespondNow(NoArguments());
 }
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler.cc b/chrome/browser/chromeos/full_restore/app_launch_handler.cc
index 403b62a..8fe23e9 100644
--- a/chrome/browser/chromeos/full_restore/app_launch_handler.cc
+++ b/chrome/browser/chromeos/full_restore/app_launch_handler.cc
@@ -38,6 +38,8 @@
 
 namespace {
 
+bool g_launch_browser_for_testing = false;
+
 constexpr char kRestoredAppLaunchHistogramPrefix[] = "Apps.RestoredAppLaunch";
 constexpr char kArcGhostWindowLaunchHistogramPrefix[] =
     "Apps.ArcGhostWindowLaunch";
@@ -108,6 +110,11 @@
 }
 
 void AppLaunchHandler::LaunchBrowserWhenReady() {
+  if (g_launch_browser_for_testing) {
+    ForceLaunchBrowserForTesting();
+    return;
+  }
+
   // If the restore data has been loaded, and the user has chosen to restore,
   // launch the browser.
   if (should_restore_ && restore_data_) {
@@ -126,8 +133,9 @@
   MaybePostRestore();
 }
 
-void AppLaunchHandler::SetForceLaunchBrowserForTesting() {
-  force_launch_browser_ = true;
+void AppLaunchHandler::ForceLaunchBrowserForTesting() {
+  UserSessionManager::GetInstance()->LaunchBrowser(profile_);
+  UserSessionManager::GetInstance()->MaybeLaunchSettings(profile_);
 }
 
 void AppLaunchHandler::OnGetRestoreData(
@@ -195,10 +203,8 @@
   // If the browser is not launched before reboot, don't launch browser during
   // the startup phase.
   const auto& launch_list = restore_data_->app_id_to_launch_list();
-  if (launch_list.find(extension_misc::kChromeAppId) == launch_list.end() &&
-      !force_launch_browser_) {
+  if (launch_list.find(extension_misc::kChromeAppId) == launch_list.end())
     return;
-  }
 
   RecordRestoredAppLaunch(apps::AppTypeName::kChromeBrowser);
 
@@ -351,5 +357,13 @@
                             is_arc_ghost_window);
 }
 
+ScopedLaunchBrowserForTesting::ScopedLaunchBrowserForTesting() {
+  g_launch_browser_for_testing = true;
+}
+
+ScopedLaunchBrowserForTesting::~ScopedLaunchBrowserForTesting() {
+  g_launch_browser_for_testing = false;
+}
+
 }  // namespace full_restore
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler.h b/chrome/browser/chromeos/full_restore/app_launch_handler.h
index d0e41d9..cfbfb1a 100644
--- a/chrome/browser/chromeos/full_restore/app_launch_handler.h
+++ b/chrome/browser/chromeos/full_restore/app_launch_handler.h
@@ -50,8 +50,8 @@
   // true to allow the restoration.
   void SetShouldRestore();
 
-  // Set force_launch_browser_ to launch browser for testing.
-  void SetForceLaunchBrowserForTesting();
+  // Force launch browser for testing.
+  void ForceLaunchBrowserForTesting();
 
  private:
   friend class AppLaunchHandlerArcAppBrowserTest;
@@ -86,13 +86,20 @@
 
   bool should_launch_browser_ = false;
 
-  bool force_launch_browser_ = false;
-
   std::unique_ptr<::full_restore::RestoreData> restore_data_;
 
   base::WeakPtrFactory<AppLaunchHandler> weak_ptr_factory_{this};
 };
 
+class ScopedLaunchBrowserForTesting {
+ public:
+  ScopedLaunchBrowserForTesting();
+  ScopedLaunchBrowserForTesting(const ScopedLaunchBrowserForTesting&) = delete;
+  ScopedLaunchBrowserForTesting& operator=(
+      const ScopedLaunchBrowserForTesting&) = delete;
+  ~ScopedLaunchBrowserForTesting();
+};
+
 }  // namespace full_restore
 }  // namespace chromeos
 
diff --git a/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc b/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
index a894034a..3ecf6ecb 100644
--- a/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
+++ b/chrome/browser/chromeos/full_restore/app_launch_handler_browsertest.cc
@@ -85,7 +85,8 @@
 namespace {
 
 constexpr char kAppId[] = "mldnpnnoiloahfhddhobgjeophloidmo";
-constexpr int32_t kId = 100;
+constexpr int32_t kWindowId1 = 100;
+constexpr int32_t kWindowId2 = 200;
 
 constexpr char kTestAppName[] = "Test ARC App";
 constexpr char kTestAppName2[] = "Test ARC App 2";
@@ -149,7 +150,7 @@
   // SaveWindowInfo to work.
   auto window = std::make_unique<aura::Window>(nullptr);
   window->Init(ui::LAYER_NOT_DRAWN);
-  window->SetProperty(::full_restore::kWindowIdKey, kId);
+  window->SetProperty(::full_restore::kWindowIdKey, kWindowId1);
 
   ::full_restore::WindowInfo window_info;
   window_info.window = window.get();
@@ -273,6 +274,7 @@
       : faster_animations_(
             ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) {
     scoped_feature_list_.InitAndEnableFeature(ash::features::kFullRestore);
+    scoped_restore_for_testing_ = std::make_unique<ScopedRestoreForTesting>();
   }
   ~AppLaunchHandlerBrowserTest() override = default;
 
@@ -290,8 +292,10 @@
   bool FindWebAppWindow() {
     for (auto* browser : *BrowserList::GetInstance()) {
       aura::Window* window = browser->window()->GetNativeWindow();
-      if (window->GetProperty(::full_restore::kRestoreWindowIdKey) == kId)
+      if (window->GetProperty(::full_restore::kRestoreWindowIdKey) ==
+          kWindowId2) {
         return true;
+      }
     }
     return false;
   }
@@ -311,16 +315,19 @@
         restore_window_id);
   }
 
+  void ResetRestoreForTesting() { scoped_restore_for_testing_.reset(); }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
   ui::ScopedAnimationDurationScaleMode faster_animations_;
+  std::unique_ptr<ScopedRestoreForTesting> scoped_restore_for_testing_;
 };
 
 IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, NotLaunchBrowser) {
   // Add app launch info.
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
+                                extension_misc::kChromeAppId, kWindowId1));
 
   WaitForAppLaunchInfoSaved();
 
@@ -341,7 +348,8 @@
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(),
       std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          kAppId, kWindowId2,
+          apps::mojom::LaunchContainer::kLaunchContainerWindow,
           WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
           std::vector<base::FilePath>{}, nullptr));
 
@@ -363,7 +371,8 @@
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(),
       std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          kAppId, kWindowId2,
+          apps::mojom::LaunchContainer::kLaunchContainerWindow,
           WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
           std::vector<base::FilePath>{}, nullptr));
 
@@ -386,11 +395,12 @@
   // Add app launch infos.
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
+                                extension_misc::kChromeAppId, kWindowId1));
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(),
       std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          kAppId, kWindowId2,
+          apps::mojom::LaunchContainer::kLaunchContainerWindow,
           WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
           std::vector<base::FilePath>{}, nullptr));
 
@@ -417,7 +427,7 @@
   // Add the chrome browser launch info.
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
+                                extension_misc::kChromeAppId, kWindowId1));
 
   WaitForAppLaunchInfoSaved();
 
@@ -443,7 +453,8 @@
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(),
       std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          kAppId, kWindowId2,
+          apps::mojom::LaunchContainer::kLaunchContainerWindow,
           WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
           std::vector<base::FilePath>{}, nullptr));
 
@@ -473,7 +484,7 @@
   // Add the chrome browser launch info.
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
+                                extension_misc::kChromeAppId, kWindowId1));
 
   WaitForAppLaunchInfoSaved();
 
@@ -501,11 +512,12 @@
   // Add app launch infos.
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
+                                extension_misc::kChromeAppId, kWindowId1));
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(),
       std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          kAppId, kWindowId2,
+          apps::mojom::LaunchContainer::kLaunchContainerWindow,
           WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
           std::vector<base::FilePath>{}, nullptr));
 
@@ -534,11 +546,12 @@
   // Add app launch infos.
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
+                                extension_misc::kChromeAppId, kWindowId1));
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(),
       std::make_unique<::full_restore::AppLaunchInfo>(
-          kAppId, kId, apps::mojom::LaunchContainer::kLaunchContainerWindow,
+          kAppId, kWindowId2,
+          apps::mojom::LaunchContainer::kLaunchContainerWindow,
           WindowOpenDisposition::NEW_WINDOW, display::kDefaultDisplayId,
           std::vector<base::FilePath>{}, nullptr));
 
@@ -569,7 +582,7 @@
 
   ::full_restore::SaveAppLaunchInfo(
       profile()->GetPath(), std::make_unique<::full_restore::AppLaunchInfo>(
-                                extension_misc::kChromeAppId, kId));
+                                extension_misc::kChromeAppId, kWindowId1));
 
   CreateAndSaveWindowInfo(kDeskId, kRestoreBounds, kCurrentBounds,
                           kWindowStateType);
@@ -586,7 +599,7 @@
   // TODO(sammiequon): Check the values from the actual browser window.
   auto window = std::make_unique<aura::Window>(nullptr);
   window->Init(ui::LAYER_NOT_DRAWN);
-  window->SetProperty(::full_restore::kRestoreWindowIdKey, kId);
+  window->SetProperty(::full_restore::kRestoreWindowIdKey, kWindowId1);
   auto stored_window_info = ::full_restore::GetWindowInfo(window.get());
   EXPECT_EQ(kDeskId, *stored_window_info->desk_id);
   EXPECT_EQ(kRestoreBounds, *stored_window_info->restore_bounds);
@@ -594,7 +607,14 @@
   EXPECT_EQ(kWindowStateType, *stored_window_info->window_state_type);
 }
 
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, RestoreChromeApp) {
+class AppLaunchHandlerChromeAppBrowserTest
+    : public AppLaunchHandlerBrowserTest {
+ public:
+  AppLaunchHandlerChromeAppBrowserTest() { ResetRestoreForTesting(); }
+  ~AppLaunchHandlerChromeAppBrowserTest() override = default;
+};
+
+IN_PROC_BROWSER_TEST_F(AppLaunchHandlerChromeAppBrowserTest, RestoreChromeApp) {
   // Have 4 desks total.
   ash::AutotestDesksApi().CreateNewDesk();
   ash::AutotestDesksApi().CreateNewDesk();
@@ -656,7 +676,8 @@
   RemoveInactiveDesks();
 }
 
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, RestoreMinimizedChromeApp) {
+IN_PROC_BROWSER_TEST_F(AppLaunchHandlerChromeAppBrowserTest,
+                       RestoreMinimizedChromeApp) {
   ::full_restore::SetActiveProfilePath(profile()->GetPath());
 
   // Create the restore data.
@@ -685,7 +706,7 @@
   EXPECT_TRUE(app_window->GetBaseWindow()->IsMinimized());
 }
 
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest,
+IN_PROC_BROWSER_TEST_F(AppLaunchHandlerChromeAppBrowserTest,
                        RestoreMultipleChromeAppWindows) {
   ::full_restore::SetActiveProfilePath(profile()->GetPath());
 
@@ -753,7 +774,8 @@
 // Tests that fullscreened windows will not be restored as fullscreen, which is
 // not supported for full restore. Regression test for
 // https://crbug.com/1203010.
-IN_PROC_BROWSER_TEST_F(AppLaunchHandlerBrowserTest, ImmersiveFullscreenApp) {
+IN_PROC_BROWSER_TEST_F(AppLaunchHandlerChromeAppBrowserTest,
+                       ImmersiveFullscreenApp) {
   ::full_restore::SetActiveProfilePath(profile()->GetPath());
 
   // Create the restore data.
diff --git a/chrome/browser/chromeos/full_restore/full_restore_service.cc b/chrome/browser/chromeos/full_restore/full_restore_service.cc
index 5df1bc8..e6dfa32 100644
--- a/chrome/browser/chromeos/full_restore/full_restore_service.cc
+++ b/chrome/browser/chromeos/full_restore/full_restore_service.cc
@@ -30,6 +30,8 @@
 namespace chromeos {
 namespace full_restore {
 
+bool g_restore_for_testing = true;
+
 const char kRestoreForCrashNotificationId[] = "restore_for_crash_notification";
 const char kRestoreNotificationId[] = "restore_notification";
 const char kSetRestorePrefNotificationId[] = "set_restore_pref_notification";
@@ -64,6 +66,9 @@
 FullRestoreService::~FullRestoreService() = default;
 
 void FullRestoreService::LaunchBrowserWhenReady() {
+  if (!g_restore_for_testing)
+    return;
+
   app_launch_handler_->LaunchBrowserWhenReady();
 }
 
@@ -124,11 +129,12 @@
 }
 
 void FullRestoreService::RestoreForTesting() {
-  // If there is no browser launch info, the browser won't be launched. So call
-  // SetForceLaunchBrowserForTesting to launch the browser for testing.
-  app_launch_handler_->SetForceLaunchBrowserForTesting();
+  if (!g_restore_for_testing)
+    return;
 
-  Restore();
+  // If there is no browser launch info, the browser won't be launched. So call
+  // ForceLaunchBrowserForTesting to launch the browser for testing.
+  app_launch_handler_->ForceLaunchBrowserForTesting();
 }
 
 void FullRestoreService::Init() {
@@ -276,5 +282,13 @@
   }
 }
 
+ScopedRestoreForTesting::ScopedRestoreForTesting() {
+  g_restore_for_testing = false;
+}
+
+ScopedRestoreForTesting::~ScopedRestoreForTesting() {
+  g_restore_for_testing = true;
+}
+
 }  // namespace full_restore
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/full_restore/full_restore_service.h b/chrome/browser/chromeos/full_restore/full_restore_service.h
index 7537e1b..367decfc 100644
--- a/chrome/browser/chromeos/full_restore/full_restore_service.h
+++ b/chrome/browser/chromeos/full_restore/full_restore_service.h
@@ -114,6 +114,14 @@
   base::WeakPtrFactory<FullRestoreService> weak_ptr_factory_{this};
 };
 
+class ScopedRestoreForTesting {
+ public:
+  ScopedRestoreForTesting();
+  ScopedRestoreForTesting(const ScopedRestoreForTesting&) = delete;
+  ScopedRestoreForTesting& operator=(const ScopedRestoreForTesting&) = delete;
+  ~ScopedRestoreForTesting();
+};
+
 }  // namespace full_restore
 }  // namespace chromeos
 
diff --git a/chrome/browser/chromeos/input_method/native_input_method_engine.cc b/chrome/browser/chromeos/input_method/native_input_method_engine.cc
index 10aedaf176..bc06c47 100644
--- a/chrome/browser/chromeos/input_method/native_input_method_engine.cc
+++ b/chrome/browser/chromeos/input_method/native_input_method_engine.cc
@@ -274,11 +274,9 @@
     remote_to_engine_.reset();
     receiver_from_engine_.reset();
 
-    // Pass an extra parameter to indicate that this connection is from
-    // NativeInputMethodEngine.
-    remote_manager_->ConnectToImeEngine(
+    remote_manager_->ConnectToInputMethod(
         new_engine_id, remote_to_engine_.BindNewPipeAndPassReceiver(),
-        receiver_from_engine_.BindNewPipeAndPassRemote(), /*extra=*/{0},
+        receiver_from_engine_.BindNewPipeAndPassRemote(),
         base::BindOnce(&ImeObserver::OnConnected, base::Unretained(this),
                        base::Time::Now(), new_engine_id));
 
@@ -522,6 +520,9 @@
     case ui::ime::ButtonId::kUndo:
       autocorrect_manager_->UndoAutocorrect();
       break;
+    case ui::ime::ButtonId::kIgnoreSuggestion:
+      // TODO(crbug/1201454): To be implemented.
+      break;
     case ui::ime::ButtonId::kAddToDictionary:
     case ui::ime::ButtonId::kNone:
       ime_base_observer_->OnAssistiveWindowButtonClicked(button);
diff --git a/chrome/browser/chromeos/input_method/native_input_method_engine_unittest.cc b/chrome/browser/chromeos/input_method/native_input_method_engine_unittest.cc
index 427124e0..9750031 100644
--- a/chrome/browser/chromeos/input_method/native_input_method_engine_unittest.cc
+++ b/chrome/browser/chromeos/input_method/native_input_method_engine_unittest.cc
@@ -76,6 +76,18 @@
     std::move(callback).Run(/*bound=*/true);
   }
 
+  void ConnectToInputMethod(
+      const std::string& ime_spec,
+      mojo::PendingReceiver<ime::mojom::InputChannel> to_engine,
+      mojo::PendingRemote<ime::mojom::InputChannel> from_engine,
+      ConnectToInputMethodCallback callback) override {
+    receiver_.Bind(std::move(to_engine));
+    if (remote_) {
+      remote_->Bind(std::move(from_engine));
+    }
+    std::move(callback).Run(/*bound=*/true);
+  }
+
  private:
   mojo::Receiver<ime::mojom::InputChannel> receiver_;
   mojo::Remote<ime::mojom::InputChannel>* remote_;
diff --git a/chrome/browser/chromeos/input_method/ui/assistive_delegate.h b/chrome/browser/chromeos/input_method/ui/assistive_delegate.h
index 726fb8d..a2d141d 100644
--- a/chrome/browser/chromeos/input_method/ui/assistive_delegate.h
+++ b/chrome/browser/chromeos/input_method/ui/assistive_delegate.h
@@ -19,6 +19,7 @@
   kSmartInputsSettingLink,
   kSuggestion,
   kLearnMore,
+  kIgnoreSuggestion,
 };
 
 enum class AssistiveWindowType {
diff --git a/chrome/browser/chromeos/input_method/ui/grammar_suggestion_window.cc b/chrome/browser/chromeos/input_method/ui/grammar_suggestion_window.cc
new file mode 100644
index 0000000..e96379a
--- /dev/null
+++ b/chrome/browser/chromeos/input_method/ui/grammar_suggestion_window.cc
@@ -0,0 +1,143 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/input_method/ui/grammar_suggestion_window.h"
+
+// #include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/chromeos/input_method/ui/border_factory.h"
+#include "components/vector_icons/vector_icons.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/gfx/color_palette.h"
+#include "ui/gfx/paint_vector_icon.h"
+#include "ui/views/bubble/bubble_border.h"
+#include "ui/views/bubble/bubble_frame_view.h"
+#include "ui/views/layout/box_layout.h"
+#include "ui/views/layout/layout_provider.h"
+#include "ui/wm/core/window_animations.h"
+
+namespace ui {
+namespace ime {
+
+namespace {
+constexpr SkColor kButtonHighlightColor =
+    SkColorSetA(SK_ColorBLACK, 0x0F);  // 6% Black.
+constexpr SkColor kSecondaryIconColor = gfx::kGoogleGrey500;
+}  // namespace
+
+GrammarSuggestionWindow::GrammarSuggestionWindow(gfx::NativeView parent,
+                                                 AssistiveDelegate* delegate)
+    : delegate_(delegate) {
+  DialogDelegate::SetButtons(ui::DIALOG_BUTTON_NONE);
+  SetCanActivate(false);
+  DCHECK(parent);
+  set_parent_window(parent);
+  set_margins(gfx::Insets());
+
+  SetArrow(views::BubbleBorder::Arrow::BOTTOM_LEFT);
+  SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kHorizontal));
+
+  suggestion_button_ =
+      AddChildView(std::make_unique<views::LabelButton>(base::BindRepeating(
+          &AssistiveDelegate::AssistiveWindowButtonClicked,
+          base::Unretained(delegate_),
+          AssistiveWindowButton{
+              .id = ui::ime::ButtonId::kSuggestion,
+              .window_type =
+                  ui::ime::AssistiveWindowType::kGrammarSuggestion})));
+  suggestion_button_->SetBackground(nullptr);
+  suggestion_button_->SetFocusBehavior(
+      views::View::FocusBehavior::ACCESSIBLE_ONLY);
+  suggestion_button_->SetVisible(true);
+
+  ignore_button_ =
+      AddChildView(std::make_unique<views::ImageButton>(base::BindRepeating(
+          &AssistiveDelegate::AssistiveWindowButtonClicked,
+          base::Unretained(delegate_),
+          AssistiveWindowButton{
+              .id = ui::ime::ButtonId::kIgnoreSuggestion,
+              .window_type = ui::ime::AssistiveWindowType::kGrammarSuggestion,
+          })));
+  ignore_button_->SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
+  ignore_button_->SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
+  ignore_button_->SetVisible(true);
+}
+
+GrammarSuggestionWindow::~GrammarSuggestionWindow() = default;
+
+void GrammarSuggestionWindow::OnThemeChanged() {
+  ignore_button_->SetBorder(
+      views::CreateEmptyBorder(views::LayoutProvider::Get()->GetInsetsMetric(
+          views::INSETS_VECTOR_IMAGE_BUTTON)));
+
+  ignore_button_->SetImage(
+      views::Button::ButtonState::STATE_NORMAL,
+      gfx::CreateVectorIcon(vector_icons::kCloseIcon, kSecondaryIconColor));
+
+  BubbleDialogDelegateView::OnThemeChanged();
+}
+
+views::Widget* GrammarSuggestionWindow::InitWidget() {
+  views::Widget* widget = BubbleDialogDelegateView::CreateBubble(this);
+
+  wm::SetWindowVisibilityAnimationTransition(widget->GetNativeView(),
+                                             wm::ANIMATE_NONE);
+
+  GetBubbleFrameView()->SetBubbleBorder(
+      GetBorderForWindow(WindowBorderType::Suggestion));
+  GetBubbleFrameView()->OnThemeChanged();
+  return widget;
+}
+
+void GrammarSuggestionWindow::Show() {
+  GetWidget()->Show();
+}
+
+void GrammarSuggestionWindow::Hide() {
+  GetWidget()->Close();
+}
+
+void GrammarSuggestionWindow::SetSuggestion(const std::u16string& suggestion) {
+  suggestion_button_->SetText(suggestion);
+}
+
+void GrammarSuggestionWindow::SetButtonHighlighted(
+    const AssistiveWindowButton& button,
+    bool highlighted) {
+  if (highlighted && button.id == current_highlighted_button_id_) {
+    return;
+  }
+
+  suggestion_button_->SetBackground(nullptr);
+  ignore_button_->SetBackground(nullptr);
+
+  if (highlighted) {
+    switch (button.id) {
+      case ButtonId::kSuggestion:
+        suggestion_button_->SetBackground(
+            views::CreateSolidBackground(kButtonHighlightColor));
+        break;
+      case ButtonId::kIgnoreSuggestion:
+        ignore_button_->SetBackground(
+            views::CreateSolidBackground(kButtonHighlightColor));
+        break;
+      default:
+        break;
+    }
+  }
+}
+
+views::LabelButton* GrammarSuggestionWindow::GetSuggestionButtonForTesting() {
+  return suggestion_button_;
+}
+
+views::Button* GrammarSuggestionWindow::GetIgnoreButtonForTesting() {
+  return ignore_button_;
+}
+
+BEGIN_METADATA(GrammarSuggestionWindow, views::BubbleDialogDelegateView)
+END_METADATA
+
+}  // namespace ime
+}  // namespace ui
diff --git a/chrome/browser/chromeos/input_method/ui/grammar_suggestion_window.h b/chrome/browser/chromeos/input_method/ui/grammar_suggestion_window.h
new file mode 100644
index 0000000..de680eb
--- /dev/null
+++ b/chrome/browser/chromeos/input_method/ui/grammar_suggestion_window.h
@@ -0,0 +1,63 @@
+// Copyright 2021 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_CHROMEOS_INPUT_METHOD_UI_GRAMMAR_SUGGESTION_WINDOW_H_
+#define CHROME_BROWSER_CHROMEOS_INPUT_METHOD_UI_GRAMMAR_SUGGESTION_WINDOW_H_
+
+#include "chrome/browser/chromeos/input_method/ui/assistive_delegate.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+#include "ui/chromeos/ui_chromeos_export.h"
+#include "ui/views/bubble/bubble_dialog_delegate_view.h"
+#include "ui/views/controls/button/image_button.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/metadata/view_factory.h"
+
+namespace ui {
+namespace ime {
+
+// Pop up UI for users to undo an autocorrected word.
+class UI_CHROMEOS_EXPORT GrammarSuggestionWindow
+    : public views::BubbleDialogDelegateView {
+ public:
+  METADATA_HEADER(GrammarSuggestionWindow);
+  explicit GrammarSuggestionWindow(gfx::NativeView parent,
+                                   AssistiveDelegate* delegate);
+  GrammarSuggestionWindow(const GrammarSuggestionWindow&) = delete;
+  GrammarSuggestionWindow& operator=(const GrammarSuggestionWindow&) = delete;
+  ~GrammarSuggestionWindow() override;
+
+  views::Widget* InitWidget();
+  void Show();
+  void Hide();
+
+  void SetSuggestion(const std::u16string& suggestion);
+
+  void SetButtonHighlighted(const AssistiveWindowButton& button,
+                            bool highlighted);
+
+  views::LabelButton* GetSuggestionButtonForTesting();
+  views::Button* GetIgnoreButtonForTesting();
+
+ protected:
+  void OnThemeChanged() override;
+
+ private:
+  AssistiveDelegate* delegate_;
+  views::LabelButton* suggestion_button_;
+  views::ImageButton* ignore_button_;
+
+  ButtonId current_highlighted_button_id_ = ButtonId::kNone;
+};
+
+BEGIN_VIEW_BUILDER(UI_CHROMEOS_EXPORT,
+                   GrammarSuggestionWindow,
+                   views::BubbleDialogDelegateView)
+END_VIEW_BUILDER
+
+}  // namespace ime
+}  // namespace ui
+
+DEFINE_VIEW_BUILDER(UI_CHROMEOS_EXPORT, ui::ime::GrammarSuggestionWindow)
+
+#endif  // CHROME_BROWSER_CHROMEOS_INPUT_METHOD_UI_GRAMMAR_SUGGESTION_WINDOW_H_
diff --git a/chrome/browser/chromeos/input_method/ui/grammar_suggestion_window_unittest.cc b/chrome/browser/chromeos/input_method/ui/grammar_suggestion_window_unittest.cc
new file mode 100644
index 0000000..d519cc56
--- /dev/null
+++ b/chrome/browser/chromeos/input_method/ui/grammar_suggestion_window_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/input_method/ui/grammar_suggestion_window.h"
+
+#include "chrome/browser/chromeos/input_method/ui/assistive_delegate.h"
+#include "chrome/test/views/chrome_views_test_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+namespace ime {
+
+class MockAssistiveDelegate : public AssistiveDelegate {
+ public:
+  ~MockAssistiveDelegate() override = default;
+  void AssistiveWindowButtonClicked(
+      const ui::ime::AssistiveWindowButton& button) const override {}
+};
+
+class GrammarSuggestionWindowTest : public ChromeViewsTestBase {
+ public:
+  GrammarSuggestionWindowTest() {}
+  ~GrammarSuggestionWindowTest() override {}
+
+ protected:
+  void SetUp() override {
+    ChromeViewsTestBase::SetUp();
+
+    grammar_suggestion_window_ =
+        new GrammarSuggestionWindow(GetContext(), delegate_.get());
+    suggestion_button_.id = ButtonId::kSuggestion;
+    ignore_button_.id = ButtonId::kIgnoreSuggestion;
+    grammar_suggestion_window_->InitWidget();
+  }
+
+  void TearDown() override {
+    grammar_suggestion_window_->GetWidget()->CloseNow();
+    ChromeViewsTestBase::TearDown();
+  }
+
+  GrammarSuggestionWindow* grammar_suggestion_window_;
+  std::unique_ptr<MockAssistiveDelegate> delegate_ =
+      std::make_unique<MockAssistiveDelegate>();
+  AssistiveWindowButton suggestion_button_;
+  AssistiveWindowButton ignore_button_;
+};
+
+TEST_F(GrammarSuggestionWindowTest, HighlightsSuggestionButton) {
+  grammar_suggestion_window_->Show();
+  grammar_suggestion_window_->SetButtonHighlighted(suggestion_button_, true);
+
+  EXPECT_NE(
+      grammar_suggestion_window_->GetSuggestionButtonForTesting()->background(),
+      nullptr);
+  EXPECT_EQ(
+      grammar_suggestion_window_->GetIgnoreButtonForTesting()->background(),
+      nullptr);
+}
+
+TEST_F(GrammarSuggestionWindowTest, HighlightsIgnoreButton) {
+  grammar_suggestion_window_->Show();
+  grammar_suggestion_window_->SetButtonHighlighted(ignore_button_, true);
+
+  EXPECT_NE(
+      grammar_suggestion_window_->GetIgnoreButtonForTesting()->background(),
+      nullptr);
+  EXPECT_EQ(
+      grammar_suggestion_window_->GetSuggestionButtonForTesting()->background(),
+      nullptr);
+}
+
+TEST_F(GrammarSuggestionWindowTest,
+       HighlightsSuggestionButtonThenIgnoreButton) {
+  grammar_suggestion_window_->Show();
+  grammar_suggestion_window_->SetButtonHighlighted(suggestion_button_, true);
+  grammar_suggestion_window_->SetButtonHighlighted(ignore_button_, true);
+
+  EXPECT_NE(
+      grammar_suggestion_window_->GetIgnoreButtonForTesting()->background(),
+      nullptr);
+  EXPECT_EQ(
+      grammar_suggestion_window_->GetSuggestionButtonForTesting()->background(),
+      nullptr);
+}
+
+TEST_F(GrammarSuggestionWindowTest,
+       HighlightsIgnoreButtonThenSuggestionButton) {
+  grammar_suggestion_window_->Show();
+  grammar_suggestion_window_->SetButtonHighlighted(ignore_button_, true);
+  grammar_suggestion_window_->SetButtonHighlighted(suggestion_button_, true);
+
+  EXPECT_NE(
+      grammar_suggestion_window_->GetSuggestionButtonForTesting()->background(),
+      nullptr);
+  EXPECT_EQ(
+      grammar_suggestion_window_->GetIgnoreButtonForTesting()->background(),
+      nullptr);
+}
+
+TEST_F(GrammarSuggestionWindowTest, UnhighlightsSuggestionButton) {
+  grammar_suggestion_window_->Show();
+  grammar_suggestion_window_->SetButtonHighlighted(suggestion_button_, true);
+  grammar_suggestion_window_->SetButtonHighlighted(suggestion_button_, false);
+
+  EXPECT_EQ(
+      grammar_suggestion_window_->GetSuggestionButtonForTesting()->background(),
+      nullptr);
+  EXPECT_EQ(
+      grammar_suggestion_window_->GetIgnoreButtonForTesting()->background(),
+      nullptr);
+}
+
+TEST_F(GrammarSuggestionWindowTest, UnhighlightsIgnoreButton) {
+  grammar_suggestion_window_->Show();
+  grammar_suggestion_window_->SetButtonHighlighted(ignore_button_, true);
+  grammar_suggestion_window_->SetButtonHighlighted(ignore_button_, false);
+
+  EXPECT_EQ(
+      grammar_suggestion_window_->GetIgnoreButtonForTesting()->background(),
+      nullptr);
+  EXPECT_EQ(
+      grammar_suggestion_window_->GetSuggestionButtonForTesting()->background(),
+      nullptr);
+}
+
+TEST_F(GrammarSuggestionWindowTest, SetsSuggestion) {
+  std::u16string test_suggestion = u"test suggestion";
+  grammar_suggestion_window_->Show();
+  grammar_suggestion_window_->SetSuggestion(test_suggestion);
+
+  EXPECT_EQ(
+      grammar_suggestion_window_->GetSuggestionButtonForTesting()->GetText(),
+      test_suggestion);
+}
+
+}  // namespace ime
+}  // namespace ui
diff --git a/chrome/browser/chromeos/policy/auto_enrollment_client_impl.cc b/chrome/browser/chromeos/policy/auto_enrollment_client_impl.cc
index 74dde7b4..e1edc3d 100644
--- a/chrome/browser/chromeos/policy/auto_enrollment_client_impl.cc
+++ b/chrome/browser/chromeos/policy/auto_enrollment_client_impl.cc
@@ -209,6 +209,8 @@
           << "PSM error: unexpected internal logic error during creating "
              "PSM RLWE client";
       has_psm_error_ = true;
+      base::UmaHistogramEnumeration(kUMAPsmResult + uma_suffix_,
+                                    PsmResult::kCreateRlweClientLibraryError);
       return;
     }
 
@@ -240,9 +242,6 @@
       return;
     }
 
-    // Report the psm attempt and start the timer to measure successful private
-    // set membership requests.
-    base::UmaHistogramEnumeration(kUMAPsmRequestStatus, PsmStatus::kAttempt);
     time_start_ = base::TimeTicks::Now();
 
     on_completion_callback_ = std::move(callback);
@@ -250,7 +249,8 @@
     // Start the protocol and its timeout timer.
     psm_timeout_.Start(
         FROM_HERE, kPsmTimeout,
-        base::BindOnce(&PsmHelper::OnTimeout, base::Unretained(this)));
+        base::BindOnce(&PsmHelper::StoreErrorAndStop, base::Unretained(this),
+                       PsmResult::kTimeout));
     SendPsmRlweOprfRequest();
   }
 
@@ -292,14 +292,10 @@
   }
 
  private:
-  void OnTimeout() {
-    base::UmaHistogramEnumeration(kUMAPsmRequestStatus, PsmStatus::kTimeout);
-    StoreErrorAndStop();
-  }
-
-  void StoreErrorAndStop() {
-    // Record the error. Note that a timeout is also recorded as error.
-    base::UmaHistogramEnumeration(kUMAPsmRequestStatus, PsmStatus::kError);
+  void StoreErrorAndStop(PsmResult psm_result) {
+    // Note that kUMAPsmResult histogram is only using initial enrollment as a
+    // suffix until PSM support FRE.
+    base::UmaHistogramEnumeration(kUMAPsmResult + uma_suffix_, psm_result);
 
     // Stop the PSM timer.
     psm_timeout_.Stop();
@@ -323,7 +319,7 @@
       LOG(ERROR)
           << "PSM error: unexpected internal logic error during creating "
              "RLWE OPRF request";
-      StoreErrorAndStop();
+      StoreErrorAndStop(PsmResult::kCreateOprfRequestLibraryError);
       return;
     }
 
@@ -354,6 +350,9 @@
       const em::DeviceManagementResponse& response) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+    base::UmaHistogramSparse(kUMAPsmDmServerRequestStatus + uma_suffix_,
+                             status);
+
     switch (status) {
       case DM_STATUS_SUCCESS: {
         // Check if the RLWE OPRF response is empty.
@@ -362,7 +361,7 @@
                  .rlwe_response()
                  .has_oprf_response()) {
           LOG(ERROR) << "PSM error: empty OPRF RLWE response";
-          StoreErrorAndStop();
+          StoreErrorAndStop(PsmResult::kEmptyOprfResponseError);
           return;
         }
 
@@ -373,12 +372,14 @@
       case DM_STATUS_REQUEST_FAILED: {
         LOG(ERROR)
             << "PSM error: RLWE OPRF request failed due to connection error";
-        StoreErrorAndStop();
+        base::UmaHistogramSparse(kUMAPsmNetworkErrorCode + uma_suffix_,
+                                 -net_error);
+        StoreErrorAndStop(PsmResult::kConnectionError);
         return;
       }
       default: {
         LOG(ERROR) << "PSM error: RLWE OPRF request failed due to server error";
-        StoreErrorAndStop();
+        StoreErrorAndStop(PsmResult::kServerError);
         return;
       }
     }
@@ -401,7 +402,7 @@
       LOG(ERROR)
           << "PSM error: unexpected internal logic error during creating "
              "RLWE query request";
-      StoreErrorAndStop();
+      StoreErrorAndStop(PsmResult::kCreateQueryRequestLibraryError);
       return;
     }
 
@@ -433,6 +434,9 @@
       const em::DeviceManagementResponse& response) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
+    base::UmaHistogramSparse(kUMAPsmDmServerRequestStatus + uma_suffix_,
+                             status);
+
     switch (status) {
       case DM_STATUS_SUCCESS: {
         // Check if the RLWE query response is empty.
@@ -441,7 +445,7 @@
                  .rlwe_response()
                  .has_query_response()) {
           LOG(ERROR) << "PSM error: empty query RLWE response";
-          StoreErrorAndStop();
+          StoreErrorAndStop(PsmResult::kEmptyQueryResponseError);
           return;
         }
 
@@ -459,14 +463,14 @@
           LOG(ERROR) << "PSM error: unexpected internal logic error during "
                         "processing the "
                         "RLWE query response";
-          StoreErrorAndStop();
+          StoreErrorAndStop(PsmResult::kProcessingQueryResponseLibraryError);
           return;
         }
 
         LOG(WARNING) << "PSM query request completed successfully";
 
-        base::UmaHistogramEnumeration(kUMAPsmRequestStatus,
-                                      PsmStatus::kSuccessfulDetermination);
+        base::UmaHistogramEnumeration(kUMAPsmResult + uma_suffix_,
+                                      PsmResult::kSuccessfulDetermination);
         RecordPsmSuccessTimeHistogram();
 
         // The RLWE query response has been processed successfully. Extract
@@ -499,13 +503,15 @@
       case DM_STATUS_REQUEST_FAILED: {
         LOG(ERROR)
             << "PSM error: RLWE query request failed due to connection error";
-        StoreErrorAndStop();
+        base::UmaHistogramSparse(kUMAPsmNetworkErrorCode + uma_suffix_,
+                                 -net_error);
+        StoreErrorAndStop(PsmResult::kConnectionError);
         return;
       }
       default: {
         LOG(ERROR)
             << "PSM error: RLWE query request failed due to server error";
-        StoreErrorAndStop();
+        StoreErrorAndStop(PsmResult::kServerError);
         return;
       }
     }
@@ -578,6 +584,10 @@
   // The time when the PSM request started.
   base::TimeTicks time_start_;
 
+  // The UMA histogram suffix. It's set only to ".InitialEnrollment" for an
+  // |AutoEnrollmentClient| until PSM will support FRE.
+  const std::string uma_suffix_ = kUMAHashDanceSuffixInitialEnrollment;
+
   // A sequence checker to prevent the race condition of having the possibility
   // of the destructor being called and any of the callbacks.
   SEQUENCE_CHECKER(sequence_checker_);
diff --git a/chrome/browser/chromeos/policy/auto_enrollment_client_impl.h b/chrome/browser/chromeos/policy/auto_enrollment_client_impl.h
index ac3a5f8..adbef325 100644
--- a/chrome/browser/chromeos/policy/auto_enrollment_client_impl.h
+++ b/chrome/browser/chromeos/policy/auto_enrollment_client_impl.h
@@ -47,13 +47,21 @@
 // determination it won't allow another membership check.
 class PsmHelper;
 
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class PsmStatus {
-  kAttempt = 0,
-  kSuccessfulDetermination = 1,
-  kError = 2,
-  kTimeout = 3,
+// Indicates all possible PSM protocol results after it has executed
+// successfully or terminated because of an error or timeout. These values are
+// persisted to logs. Entries should not be renumbered and numeric values should
+// never be reused.
+enum class PsmResult {
+  kSuccessfulDetermination = 0,
+  kCreateRlweClientLibraryError = 1,
+  kCreateOprfRequestLibraryError = 2,
+  kCreateQueryRequestLibraryError = 3,
+  kProcessingQueryResponseLibraryError = 4,
+  kEmptyOprfResponseError = 5,
+  kEmptyQueryResponseError = 6,
+  kConnectionError = 7,
+  kServerError = 8,
+  kTimeout = 9,
   kMaxValue = kTimeout,
 };
 
diff --git a/chrome/browser/chromeos/policy/auto_enrollment_client_impl_unittest.cc b/chrome/browser/chromeos/policy/auto_enrollment_client_impl_unittest.cc
index b4b5856b..8fe2f2d 100644
--- a/chrome/browser/chromeos/policy/auto_enrollment_client_impl_unittest.cc
+++ b/chrome/browser/chromeos/policy/auto_enrollment_client_impl_unittest.cc
@@ -1564,21 +1564,37 @@
     return psm_test_case_.is_positive_membership_expected();
   }
 
-  // Expects list of samples |status_list| for kUMAPsmRequestStatus and each one
-  // of them to be recorded once.
-  // If |success_time_recorded| is true it expects one sample for
-  // kUMAPsmSuccessTime. Otherwise, expects no sample to be recorded for
+  // Expects a sample for kUMAPsmResult to be recorded once with value
+  // |protocol_result|.
+  // If |success_time_recorded| is true it expects one sample
+  // for kUMAPsmSuccessTime. Otherwise, expects no sample to be recorded for
   // kUMAPsmSuccessTime.
-  void ExpectPsmHistograms(const std::vector<PsmStatus> status_list,
+  void ExpectPsmHistograms(PsmResult protocol_result,
                            bool success_time_recorded) const {
-    for (PsmStatus status : status_list) {
-      histogram_tester_.ExpectBucketCount(kUMAPsmRequestStatus, status,
-                                          /*expected_count=*/1);
-    }
+    histogram_tester_.ExpectBucketCount(
+        kUMAPsmResult + GetAutoEnrollmentProtocolUmaSuffix(), protocol_result,
+        /*expected_count=*/1);
     histogram_tester_.ExpectTotalCount(kUMAPsmSuccessTime,
                                        success_time_recorded ? 1 : 0);
   }
 
+  // Expects a sample |dm_status| for kUMAPsmDmServerRequestStatus with count
+  // |dm_status_count|.
+  void ExpectPsmRequestStatusHistogram(DeviceManagementStatus dm_status,
+                                       int dm_status_count) const {
+    histogram_tester_.ExpectBucketCount(
+        kUMAPsmDmServerRequestStatus + GetAutoEnrollmentProtocolUmaSuffix(),
+        dm_status, dm_status_count);
+  }
+
+  // Expects one sample for |kUMAPsmNetworkErrorCode| which has value of
+  // |network_error|.
+  void ExpectPsmNetworkErrorHistogram(int network_error) const {
+    histogram_tester_.ExpectBucketCount(
+        kUMAPsmNetworkErrorCode + GetAutoEnrollmentProtocolUmaSuffix(),
+        network_error, /*expected_count=*/1);
+  }
+
   // Expects a sample for kUMAPsmHashDanceComparison to be recorded once with
   // value |comparison|.
   void ExpectPsmHashDanceComparisonRecorded(
@@ -1664,9 +1680,10 @@
             GetExpectedMembershipResult()
                 ? StateDiscoveryResult::kSuccessHasServerSideState
                 : StateDiscoveryResult::kSuccessNoServerSideState);
-  ExpectPsmHistograms(
-      {PsmStatus::kAttempt, PsmStatus::kSuccessfulDetermination},
-      /*success_time_recorded=*/true);
+  ExpectPsmHistograms(PsmResult::kSuccessfulDetermination,
+                      /*success_time_recorded=*/true);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/2);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 }
@@ -1682,8 +1699,10 @@
   client()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kEmptyQueryResponseError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/2);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 }
@@ -1698,8 +1717,10 @@
   client()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kEmptyOprfResponseError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/1);
   VerifyPsmRlweOprfRequest();
   VerifyPsmLastRequestJobType();
 }
@@ -1715,8 +1736,13 @@
   client()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kConnectionError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/1);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_REQUEST_FAILED,
+                                  /*dm_status_count=*/1);
+  ExpectPsmNetworkErrorHistogram(-net::ERR_FAILED);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 }
@@ -1731,15 +1757,18 @@
   client()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kConnectionError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_REQUEST_FAILED,
+                                  /*dm_status_count=*/1);
+  ExpectPsmNetworkErrorHistogram(-net::ERR_FAILED);
   VerifyPsmRlweOprfRequest();
   VerifyPsmLastRequestJobType();
 }
 
 TEST_P(PsmHelperTest, NetworkFailureForRlweOprfResponse) {
   InSequence sequence;
-  ServerWillFailForPsm(net::OK, DeviceManagementService::kServiceUnavailable);
+  ServerWillFailForPsm(net::OK, net::ERR_CONNECTION_CLOSED);
 
   // Fail for DeviceAutoEnrollmentRequest i.e. hash dance request.
   ServerWillFail(net::OK, DeviceManagementService::kServiceUnavailable);
@@ -1747,8 +1776,10 @@
   client()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kServerError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_HTTP_STATUS_ERROR,
+                                  /*dm_status_count=*/1);
   VerifyPsmLastRequestJobType();
 }
 
@@ -1763,8 +1794,12 @@
   client()->Start();
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kServerError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/1);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_HTTP_STATUS_ERROR,
+                                  /*dm_status_count=*/1);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 }
@@ -1796,9 +1831,10 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(GetStateDiscoveryResult(), expected_state_result);
-  ExpectPsmHistograms(
-      {PsmStatus::kAttempt, PsmStatus::kSuccessfulDetermination},
-      /*success_time_recorded=*/true);
+  ExpectPsmHistograms(PsmResult::kSuccessfulDetermination,
+                      /*success_time_recorded=*/true);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/2);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 }
@@ -1828,8 +1864,12 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(GetStateDiscoveryResult(), expected_state_result);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kServerError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/1);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_HTTP_STATUS_ERROR,
+                                  /*dm_status_count=*/1);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 }
@@ -1865,8 +1905,12 @@
 
   // Verify failure of PSM protocol.
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kServerError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/1);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_HTTP_STATUS_ERROR,
+                                  /*dm_status_count=*/1);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 
@@ -1913,8 +1957,10 @@
 
   // Verify failure of PSM protocol.
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kServerError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_TEMPORARY_UNAVAILABLE,
+                                  /*dm_status_count=*/1);
   VerifyPsmLastRequestJobType();
 
   // Verify Hash dance result.
@@ -1966,9 +2012,10 @@
             GetExpectedMembershipResult()
                 ? StateDiscoveryResult::kSuccessHasServerSideState
                 : StateDiscoveryResult::kSuccessNoServerSideState);
-  ExpectPsmHistograms(
-      {PsmStatus::kAttempt, PsmStatus::kSuccessfulDetermination},
-      /*success_time_recorded=*/true);
+  ExpectPsmHistograms(PsmResult::kSuccessfulDetermination,
+                      /*success_time_recorded=*/true);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/2);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 
@@ -2025,9 +2072,10 @@
             GetExpectedMembershipResult()
                 ? StateDiscoveryResult::kSuccessHasServerSideState
                 : StateDiscoveryResult::kSuccessNoServerSideState);
-  ExpectPsmHistograms(
-      {PsmStatus::kAttempt, PsmStatus::kSuccessfulDetermination},
-      /*success_time_recorded=*/true);
+  ExpectPsmHistograms(PsmResult::kSuccessfulDetermination,
+                      /*success_time_recorded=*/true);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/2);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 
@@ -2073,8 +2121,10 @@
 
   // Verify failure of PSM protocol.
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kServerError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_TEMPORARY_UNAVAILABLE,
+                                  /*dm_status_count=*/1);
   VerifyPsmLastRequestJobType();
 
   // Verify failure of Hash dance by inexistence of its cached decision.
@@ -2138,9 +2188,10 @@
   // Verify that PSM cached decision hasn't changed, and no new requests have
   // been sent.
   EXPECT_EQ(GetStateDiscoveryResult(), expected_psm_state_result);
-  ExpectPsmHistograms(
-      {PsmStatus::kAttempt, PsmStatus::kSuccessfulDetermination},
-      /*success_time_recorded=*/true);
+  ExpectPsmHistograms(PsmResult::kSuccessfulDetermination,
+                      /*success_time_recorded=*/true);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/2);
   VerifyPsmRlweQueryRequest();
   VerifyPsmLastRequestJobType();
 
@@ -2206,8 +2257,10 @@
   // Verify that PSM cached decision hasn't changed, and no
   // new requests have been sent.
   EXPECT_EQ(GetStateDiscoveryResult(), expected_psm_state_result);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kServerError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_TEMPORARY_UNAVAILABLE,
+                                  /*dm_status_count=*/1);
   VerifyPsmRlweOprfRequest();
   VerifyPsmLastRequestJobType();
 
@@ -2267,8 +2320,10 @@
 
   // Verify failure of PSM protocol.
   EXPECT_EQ(GetStateDiscoveryResult(), StateDiscoveryResult::kFailure);
-  ExpectPsmHistograms({PsmStatus::kAttempt, PsmStatus::kError},
+  ExpectPsmHistograms(PsmResult::kServerError,
                       /*success_time_recorded=*/false);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_TEMPORARY_UNAVAILABLE,
+                                  /*dm_status_count=*/1);
 
   // Verify hash dance job has been captured.
   ASSERT_TRUE(hash_dance_job);
@@ -2360,9 +2415,10 @@
             GetExpectedMembershipResult()
                 ? StateDiscoveryResult::kSuccessHasServerSideState
                 : StateDiscoveryResult::kSuccessNoServerSideState);
-  ExpectPsmHistograms(
-      {PsmStatus::kAttempt, PsmStatus::kSuccessfulDetermination},
-      /*success_time_recorded=*/true);
+  ExpectPsmHistograms(PsmResult::kSuccessfulDetermination,
+                      /*success_time_recorded=*/true);
+  ExpectPsmRequestStatusHistogram(DM_STATUS_SUCCESS,
+                                  /*dm_status_count=*/2);
 
   // Verify hash dance job has been captured.
   ASSERT_TRUE(hash_dance_job);
diff --git a/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.cc b/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.cc
index aa54daf..a1f571bf 100644
--- a/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.cc
+++ b/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.cc
@@ -18,7 +18,7 @@
 #include "base/task/thread_pool.h"
 #include "base/task_runner_util.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "chrome/browser/chromeos/power/ml/recent_events_counter.h"
+#include "chrome/browser/ash/power/ml/recent_events_counter.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chromeos/dbus/power_manager/backlight.pb.h"
diff --git a/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.h b/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.h
index 06b398ab..d7d4cc4 100644
--- a/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.h
+++ b/chrome/browser/chromeos/power/smart_charging/smart_charging_manager.h
@@ -12,7 +12,7 @@
 #include "base/scoped_observation.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/chromeos/power/ml/boot_clock.h"
+#include "chrome/browser/ash/power/ml/boot_clock.h"
 #include "chrome/browser/chromeos/power/smart_charging/smart_charging_ukm_logger.h"
 #include "chrome/browser/chromeos/power/smart_charging/user_charging_event.pb.h"
 #include "chrome/browser/profiles/profile.h"
diff --git a/chrome/browser/component_updater/smart_dim_component_installer.cc b/chrome/browser/component_updater/smart_dim_component_installer.cc
index 104e499..bc93ba9 100644
--- a/chrome/browser/component_updater/smart_dim_component_installer.cc
+++ b/chrome/browser/component_updater/smart_dim_component_installer.cc
@@ -18,8 +18,8 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/version.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/metrics.h"
-#include "chrome/browser/chromeos/power/ml/smart_dim/ml_agent.h"
+#include "chrome/browser/ash/power/ml/smart_dim/metrics.h"
+#include "chrome/browser/ash/power/ml/smart_dim/ml_agent.h"
 #include "components/component_updater/component_updater_service.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
index 5d15ab5d..7617ca2 100644
--- a/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
+++ b/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.cc
@@ -129,6 +129,7 @@
     case ui::ime::ButtonId::kSmartInputsSettingLink:
     case ui::ime::ButtonId::kSuggestion:
     case ui::ime::ButtonId::kLearnMore:
+    case ui::ime::ButtonId::kIgnoreSuggestion:
       return input_ime::ASSISTIVE_WINDOW_BUTTON_NONE;
     case ui::ime::ButtonId::kUndo:
       return input_ime::ASSISTIVE_WINDOW_BUTTON_UNDO;
diff --git a/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc b/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc
index 513afa4..729cde7 100644
--- a/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc
+++ b/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc
@@ -45,7 +45,10 @@
 
 namespace {
 
-class SettingsPrivateApiTest : public ExtensionApiTest {
+using ContextType = ExtensionBrowserTest::ContextType;
+
+class SettingsPrivateApiTest : public ExtensionApiTest,
+                               public testing::WithParamInterface<ContextType> {
  public:
   SettingsPrivateApiTest() {}
   ~SettingsPrivateApiTest() override {}
@@ -60,9 +63,10 @@
 
  protected:
   bool RunSettingsSubtest(const std::string& subtest) {
-    const std::string page_url = "main.html?" + subtest;
-    return RunExtensionTest("settings_private", {.page_url = page_url.c_str()},
-                            {.load_as_component = true});
+    return RunExtensionTest(
+        "settings_private", {.custom_arg = subtest.c_str()},
+        {.load_as_service_worker = GetParam() == ContextType::kServiceWorker,
+         .load_as_component = true});
   }
 
   void SetPrefPolicy(const std::string& key, policy::PolicyLevel level) {
@@ -85,37 +89,43 @@
   DISALLOW_COPY_AND_ASSIGN(SettingsPrivateApiTest);
 };
 
+INSTANTIATE_TEST_SUITE_P(PersistentBackground,
+                         SettingsPrivateApiTest,
+                         ::testing::Values(ContextType::kPersistentBackground));
+INSTANTIATE_TEST_SUITE_P(ServiceWorker,
+                         SettingsPrivateApiTest,
+                         ::testing::Values(ContextType::kServiceWorker));
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, SetPref) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, SetPref) {
   EXPECT_TRUE(RunSettingsSubtest("setPref")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetPref) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, GetPref) {
   EXPECT_TRUE(RunSettingsSubtest("getPref")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetEnforcedPref) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, GetEnforcedPref) {
   SetPrefPolicy(policy::key::kHomepageIsNewTabPage,
                 policy::POLICY_LEVEL_MANDATORY);
   EXPECT_TRUE(RunSettingsSubtest("getEnforcedPref")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetRecommendedPref) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, GetRecommendedPref) {
   SetPrefPolicy(policy::key::kHomepageIsNewTabPage,
                 policy::POLICY_LEVEL_RECOMMENDED);
   EXPECT_TRUE(RunSettingsSubtest("getRecommendedPref")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetDisabledPref) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, GetDisabledPref) {
   HostContentSettingsMapFactory::GetForProfile(profile())
       ->SetDefaultContentSetting(ContentSettingsType::COOKIES,
                                  ContentSetting::CONTENT_SETTING_BLOCK);
   EXPECT_TRUE(RunSettingsSubtest("getDisabledPref")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetPartiallyManagedPref) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, GetPartiallyManagedPref) {
   auto provider = std::make_unique<content_settings::MockProvider>();
   provider->SetWebsiteSetting(
       ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(),
@@ -127,24 +137,24 @@
   EXPECT_TRUE(RunSettingsSubtest("getPartiallyManagedPref")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetAllPrefs) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, GetAllPrefs) {
   EXPECT_TRUE(RunSettingsSubtest("getAllPrefs")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, OnPrefsChanged) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, OnPrefsChanged) {
   EXPECT_TRUE(RunSettingsSubtest("onPrefsChanged")) << message_;
 }
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetPref_CrOSSetting) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, GetPref_CrOSSetting) {
   EXPECT_TRUE(RunSettingsSubtest("getPref_CrOSSetting")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, SetPref_CrOSSetting) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, SetPref_CrOSSetting) {
   EXPECT_TRUE(RunSettingsSubtest("setPref_CrOSSetting")) << message_;
 }
 
-IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, OnPrefsChanged_CrOSSetting) {
+IN_PROC_BROWSER_TEST_P(SettingsPrivateApiTest, OnPrefsChanged_CrOSSetting) {
   EXPECT_TRUE(RunSettingsSubtest("onPrefsChanged_CrOSSetting")) << message_;
 }
 #endif
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
index f075a23..fb1ff21 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -125,7 +125,7 @@
         callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(frame_id);
-  if (!rfh || !rfh->IsCurrent()) {
+  if (!rfh || !rfh->IsActive()) {
     // Requested from a no longer valid render frame host.
     std::move(callback).Run(ChromeFileSystemAccessPermissionContext::
                                 SensitiveDirectoryResult::kAbort);
@@ -497,7 +497,7 @@
     // Otherwise, perform checks and ask the user for permission.
 
     content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(frame_id);
-    if (!rfh || !rfh->IsCurrent()) {
+    if (!rfh || !rfh->IsActive()) {
       // Requested from a no longer valid render frame host.
       RunCallbackAndRecordPermissionRequestOutcome(
           std::move(callback), PermissionRequestOutcome::kInvalidFrame);
diff --git a/chrome/browser/long_screenshots/long_screenshots_tab_service.cc b/chrome/browser/long_screenshots/long_screenshots_tab_service.cc
index 338100c..51cc40b5 100644
--- a/chrome/browser/long_screenshots/long_screenshots_tab_service.cc
+++ b/chrome/browser/long_screenshots/long_screenshots_tab_service.cc
@@ -116,7 +116,7 @@
   // defunct pointer.
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id);
   if (!contents || !rfh || contents->IsBeingDestroyed() ||
-      contents->GetMainFrame() != rfh || !rfh->IsCurrent()) {
+      contents->GetMainFrame() != rfh || !rfh->IsActive()) {
     JNIEnv* env = base::android::AttachCurrentThread();
     Java_LongScreenshotsTabService_processCaptureTabStatus(
         env, java_ref_, Status::kWebContentsGone);
diff --git a/chrome/browser/media/history/media_history_browsertest.cc b/chrome/browser/media/history/media_history_browsertest.cc
index 2eef76cb..8001e9c 100644
--- a/chrome/browser/media/history/media_history_browsertest.cc
+++ b/chrome/browser/media/history/media_history_browsertest.cc
@@ -1163,9 +1163,8 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
-// Test is flaky: crbug.com/1213333.
 IN_PROC_BROWSER_TEST_F(MediaHistoryForPrerenderBrowserTest,
-                       DISABLED_KeepRecordingMediaSession) {
+                       KeepRecordingMediaSession) {
   // Start a page and wait for significant playback so we record watchtime.
   ASSERT_TRUE(SetupPageAndStartPlaying(browser(), GetTestURL()));
   EXPECT_TRUE(SetMediaMetadata(browser()));
diff --git a/chrome/browser/media/webrtc/desktop_capture_devices_util.cc b/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
index b5a9e6149..5be4e1ac 100644
--- a/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
+++ b/chrome/browser/media/webrtc/desktop_capture_devices_util.cc
@@ -39,7 +39,7 @@
       content::RenderFrameHost::FromID(
           captured_id.web_contents_id.render_process_id,
           captured_id.web_contents_id.main_render_frame_id);
-  if (!captured_rfh || !captured_rfh->IsCurrent()) {
+  if (!captured_rfh || !captured_rfh->IsActive()) {
     return nullptr;
   }
 
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index 88d216b..f1bf3ec2 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -89,9 +89,12 @@
 // Returns whether the particular component version can be processed, and if it
 // can be, locks the semaphore (in the form of a pref) to signal that the
 // processing of this particular version has started.
-bool CanProcessComponentVersion(PrefService* pref_service,
-                                const base::Version& version) {
+bool CanProcessComponentVersion(
+    PrefService* pref_service,
+    const base::Version& version,
+    optimization_guide::ProcessHintsComponentResult* out_result) {
   DCHECK(version.IsValid());
+  DCHECK(out_result);
 
   const std::string previous_attempted_version_string = pref_service->GetString(
       optimization_guide::prefs::kPendingHintsProcessingVersion);
@@ -103,9 +106,13 @@
       // Clear pref for fresh start next time.
       pref_service->ClearPref(
           optimization_guide::prefs::kPendingHintsProcessingVersion);
+      *out_result = optimization_guide::ProcessHintsComponentResult::
+          kFailedPreviouslyAttemptedVersionInvalid;
       return false;
     }
     if (previous_attempted_version.CompareTo(version) == 0) {
+      *out_result = optimization_guide::ProcessHintsComponentResult::
+          kFailedFinishProcessing;
       // Previously attempted same version without completion.
       return false;
     }
@@ -335,6 +342,17 @@
       NavigationPredictorKeyedServiceFactory::GetForProfile(profile_);
   if (navigation_predictor_service)
     navigation_predictor_service->RemoveObserver(this);
+
+  base::UmaHistogramBoolean("OptimizationGuide.ProcessingComponentAtShutdown",
+                            is_processing_component_);
+  if (is_processing_component_) {
+    // If we are currently processing the component and we are asked to shut
+    // down, we should clear the pref since the function to clear the pref will
+    // not run after shut down and we will think that we failed to process the
+    // component due to a crash.
+    pref_service_->ClearPref(
+        optimization_guide::prefs::kPendingHintsProcessingVersion);
+  }
 }
 
 // static
@@ -377,10 +395,9 @@
     return;
   }
 
-  if (!CanProcessComponentVersion(pref_service_, info.version)) {
-    optimization_guide::RecordProcessHintsComponentResult(
-        optimization_guide::ProcessHintsComponentResult::
-            kFailedFinishProcessing);
+  optimization_guide::ProcessHintsComponentResult out_result;
+  if (!CanProcessComponentVersion(pref_service_, info.version, &out_result)) {
+    optimization_guide::RecordProcessHintsComponentResult(out_result);
     MaybeRunUpdateClosure(std::move(next_update_closure_));
     return;
   }
@@ -398,6 +415,7 @@
   // processing will be skipped.
   // base::Unretained(this) is safe since |this| owns |background_task_runner_|
   // and the callback will be canceled if destroyed.
+  is_processing_component_ = true;
   background_task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE, base::BindOnce(&ReadComponentFile, info),
       base::BindOnce(&OptimizationGuideHintsManager::UpdateComponentHints,
@@ -524,6 +542,7 @@
 
   // If we get here, the component file has been processed correctly and did not
   // crash the device.
+  is_processing_component_ = false;
   pref_service_->ClearPref(
       optimization_guide::prefs::kPendingHintsProcessingVersion);
 
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
index ba9a71a..b2cf1c5 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.h
@@ -366,6 +366,9 @@
   // |optimization_guide_service_|.
   absl::optional<optimization_guide::HintsComponentInfo> hints_component_info_;
 
+  // Whether the component is currently being processed.
+  bool is_processing_component_ = false;
+
   // The set of optimization types that have been registered with the hints
   // manager.
   //
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index 9c10d0cf..bbe3614f4 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -357,19 +357,23 @@
   }
 
   void ProcessHints(const optimization_guide::proto::Configuration& config,
-                    const std::string& version) {
+                    const std::string& version,
+                    bool should_wait = true) {
     optimization_guide::HintsComponentInfo info(
         base::Version(version),
         temp_dir().Append(FILE_PATH_LITERAL("somefile.pb")));
     ASSERT_NO_FATAL_FAILURE(WriteConfigToFile(config, info.path));
 
     base::RunLoop run_loop;
-    hints_manager_->ListenForNextUpdateForTesting(run_loop.QuitClosure());
+    if (should_wait)
+      hints_manager_->ListenForNextUpdateForTesting(run_loop.QuitClosure());
     hints_manager_->OnHintsComponentAvailable(info);
-    run_loop.Run();
+    if (should_wait)
+      run_loop.Run();
   }
 
-  void InitializeWithDefaultConfig(const std::string& version) {
+  void InitializeWithDefaultConfig(const std::string& version,
+                                   bool should_wait = true) {
     optimization_guide::proto::Configuration config;
     optimization_guide::proto::Hint* hint1 = config.add_hints();
     hint1->set_key("somedomain.org");
@@ -389,7 +393,7 @@
         hint2->add_whitelisted_optimizations();
     opt->set_optimization_type(optimization_guide::proto::NOSCRIPT);
 
-    ProcessHints(config, version);
+    ProcessHints(config, version, should_wait);
   }
 
   std::unique_ptr<optimization_guide::HintsFetcherFactory>
@@ -655,6 +659,20 @@
   }
 }
 
+TEST_F(OptimizationGuideHintsManagerTest, ComponentProcessingWhileShutdown) {
+  base::HistogramTester histogram_tester;
+  InitializeWithDefaultConfig("10.0.0.0", /*should_wait=*/false);
+  hints_manager()->Shutdown();
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.ProcessingComponentAtShutdown", true, 1);
+
+  EXPECT_TRUE(
+      pref_service()
+          ->GetString(optimization_guide::prefs::kPendingHintsProcessingVersion)
+          .empty());
+}
+
 TEST_F(OptimizationGuideHintsManagerTest, ParseOlderConfigVersions) {
   // Test the first time parsing the config.
   {
@@ -749,6 +767,32 @@
   }
 }
 
+TEST_F(OptimizationGuideHintsManagerTest,
+       ProcessHintsWithExistingPrefDoesNotClearOrCountAsMidProcessing) {
+  // Write hints processing pref for version 2.0.0.
+  pref_service()->SetString(
+      optimization_guide::prefs::kPendingHintsProcessingVersion, "2.0.0");
+
+  // Verify component for same version counts as "failed".
+  base::HistogramTester histogram_tester;
+  InitializeWithDefaultConfig("2.0.0", /*should_wait=*/false);
+  hints_manager()->Shutdown();
+
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.ProcessHintsResult",
+      optimization_guide::ProcessHintsComponentResult::kFailedFinishProcessing,
+      1);
+
+  // Verify that pref still not cleared at shutdown and was not counted as
+  // mid-processing.
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.ProcessingComponentAtShutdown", false, 1);
+  EXPECT_FALSE(
+      pref_service()
+          ->GetString(optimization_guide::prefs::kPendingHintsProcessingVersion)
+          .empty());
+}
+
 TEST_F(OptimizationGuideHintsManagerTest, ProcessHintsWithInvalidPref) {
   // Create pref file with invalid version.
   pref_service()->SetString(
@@ -767,7 +811,7 @@
     histogram_tester.ExpectUniqueSample(
         "OptimizationGuide.ProcessHintsResult",
         optimization_guide::ProcessHintsComponentResult::
-            kFailedFinishProcessing,
+            kFailedPreviouslyAttemptedVersionInvalid,
         1);
   }
 
diff --git a/chrome/browser/paint_preview/services/paint_preview_tab_service.cc b/chrome/browser/paint_preview/services/paint_preview_tab_service.cc
index 155b0ff6..4bbdd681 100644
--- a/chrome/browser/paint_preview/services/paint_preview_tab_service.cc
+++ b/chrome/browser/paint_preview/services/paint_preview_tab_service.cc
@@ -278,7 +278,7 @@
       content::WebContents::FromFrameTreeNodeId(task->frame_tree_node_id());
   auto* rfh = content::RenderFrameHost::FromID(task->frame_routing_id());
   if (!contents || !rfh || contents->IsBeingDestroyed() ||
-      contents->GetMainFrame() != rfh || !rfh->IsCurrent() ||
+      contents->GetMainFrame() != rfh || !rfh->IsActive() ||
       !rfh->IsRenderFrameCreated() || !rfh->IsRenderFrameLive()) {
     task->OnCaptured(Status::kWebContentsGone);
     return;
diff --git a/chrome/browser/payments/android/payment_app_service_bridge.cc b/chrome/browser/payments/android/payment_app_service_bridge.cc
index 4a84612..06afc0f 100644
--- a/chrome/browser/payments/android/payment_app_service_bridge.cc
+++ b/chrome/browser/payments/android/payment_app_service_bridge.cc
@@ -233,9 +233,8 @@
 
 content::WebContents* PaymentAppServiceBridge::GetWebContents() {
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  return rfh && rfh->IsCurrent()
-             ? content::WebContents::FromRenderFrameHost(rfh)
-             : nullptr;
+  return rfh && rfh->IsActive() ? content::WebContents::FromRenderFrameHost(rfh)
+                                : nullptr;
 }
 const GURL& PaymentAppServiceBridge::GetTopOrigin() {
   return top_origin_;
@@ -268,7 +267,7 @@
   // displays the top-level origin in its UI before the user can click on the
   // [Verify] button to invoke this authenticator.
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  return rfh && rfh->IsCurrent()
+  return rfh && rfh->IsActive()
              ? std::make_unique<InternalAuthenticatorAndroid>(
                    rfh->GetMainFrame())
              : nullptr;
diff --git a/chrome/browser/payments/chrome_payment_request_delegate.cc b/chrome/browser/payments/chrome_payment_request_delegate.cc
index 6f26d2d7..ac93bf3 100644
--- a/chrome/browser/payments/chrome_payment_request_delegate.cc
+++ b/chrome/browser/payments/chrome_payment_request_delegate.cc
@@ -141,10 +141,9 @@
 
 const GURL& ChromePaymentRequestDelegate::GetLastCommittedURL() const {
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  return rfh && rfh->IsCurrent()
-             ? content::WebContents::FromRenderFrameHost(rfh)
-                   ->GetLastCommittedURL()
-             : GURL::EmptyGURL();
+  return rfh && rfh->IsActive() ? content::WebContents::FromRenderFrameHost(rfh)
+                                      ->GetLastCommittedURL()
+                                : GURL::EmptyGURL();
 }
 
 void ChromePaymentRequestDelegate::DoFullCardRequest(
@@ -152,7 +151,7 @@
     base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
         result_delegate) {
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  if (!rfh || !rfh->IsCurrent() || !shown_dialog_)
+  if (!rfh || !rfh->IsActive() || !shown_dialog_)
     return;
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(rfh);
@@ -204,7 +203,7 @@
 
 bool ChromePaymentRequestDelegate::IsBrowserWindowActive() const {
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  if (!rfh || !rfh->IsCurrent())
+  if (!rfh || !rfh->IsActive())
     return false;
 
   Browser* browser = chrome::FindBrowserWithWebContents(
@@ -220,7 +219,7 @@
   // displays the top-level origin in its UI before the user can click on the
   // [Verify] button to invoke this authenticator.
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  return rfh && rfh->IsCurrent()
+  return rfh && rfh->IsActive()
              ? std::make_unique<content::InternalAuthenticatorImpl>(
                    rfh->GetMainFrame())
              : nullptr;
@@ -258,7 +257,7 @@
 std::string
 ChromePaymentRequestDelegate::GetInvalidSslCertificateErrorMessage() {
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  return rfh && rfh->IsCurrent()
+  return rfh && rfh->IsActive()
              ? SslValidityChecker::GetInvalidSslCertificateErrorMessage(
                    content::WebContents::FromRenderFrameHost(rfh))
              : "";
@@ -271,7 +270,7 @@
 std::string ChromePaymentRequestDelegate::GetTwaPackageName() const {
 #if BUILDFLAG(IS_CHROMEOS_ASH)
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  if (!rfh || !rfh->IsCurrent())
+  if (!rfh || !rfh->IsActive())
     return "";
 
   auto* web_contents = content::WebContents::FromRenderFrameHost(rfh);
diff --git a/chrome/browser/payments/payment_request_factory.cc b/chrome/browser/payments/payment_request_factory.cc
index d3ff75b0..06867ab 100644
--- a/chrome/browser/payments/payment_request_factory.cc
+++ b/chrome/browser/payments/payment_request_factory.cc
@@ -33,7 +33,7 @@
 void CreatePaymentRequest(
     content::RenderFrameHost* render_frame_host,
     mojo::PendingReceiver<mojom::PaymentRequest> receiver) {
-  if (!render_frame_host->IsCurrent()) {
+  if (!render_frame_host->IsActive()) {
     // This happens when the page has navigated away, which would cause the
     // blink PaymentRequest to be released shortly, or when the iframe is being
     // removed from the page, which is not a use case that we support.
diff --git a/chrome/browser/performance_manager/observers/page_load_metrics_observer.cc b/chrome/browser/performance_manager/observers/page_load_metrics_observer.cc
index bfc8339..40e8ba8 100644
--- a/chrome/browser/performance_manager/observers/page_load_metrics_observer.cc
+++ b/chrome/browser/performance_manager/observers/page_load_metrics_observer.cc
@@ -301,10 +301,10 @@
 
 void PageLoadMetricsWebContentsObserver::DidFinishNavigation(
     content::NavigationHandle* navigation_handle) {
-  // TODO(https://crbug.com/1190112): Using IsCurrent as a proxy for "is in
+  // TODO(https://crbug.com/1190112): Using IsActive as a proxy for "is in
   // primary FrameTree". Add support for Prerender.
   if (!navigation_handle->HasCommitted() ||
-      !navigation_handle->GetRenderFrameHost()->IsCurrent()) {
+      !navigation_handle->GetRenderFrameHost()->IsActive()) {
     return;
   }
 
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index d45753e..cdcb880 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -517,6 +517,9 @@
 
 ProfileManager::~ProfileManager() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  for (auto& observer : observers_) {
+    observer.OnProfileManagerDestroying();
+  }
   if (base::FeatureList::IsEnabled(features::kDestroyProfileOnBrowserClose)) {
     // Ideally, all the keepalives should've been cleared already. Report
     // metrics for incorrect usage of ScopedProfileKeepAlive.
diff --git a/chrome/browser/profiles/profile_manager_observer.h b/chrome/browser/profiles/profile_manager_observer.h
index 34ffd38..21cdba1 100644
--- a/chrome/browser/profiles/profile_manager_observer.h
+++ b/chrome/browser/profiles/profile_manager_observer.h
@@ -25,6 +25,9 @@
   // erased. Note that the Profile object will not be destroyed until Chrome
   // shuts down. See https://crbug.com/88586
   virtual void OnProfileMarkedForPermanentDeletion(Profile* profile) {}
+
+  // Called when the profile manager is destroying.
+  virtual void OnProfileManagerDestroying() {}
 };
 
 #endif  // CHROME_BROWSER_PROFILES_PROFILE_MANAGER_OBSERVER_H_
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
index 35b4e3d..819d0c2d 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/action_manager.js
@@ -293,7 +293,7 @@
       case SAConstants.ActionResponse.CLOSE_MENU:
         ActionManager.exitAllMenus();
         return;
-      case SAConstants.ActionResponse.EXIT_MENU:
+      case SAConstants.ActionResponse.EXIT_SUBMENU:
         ActionManager.exitCurrentMenu();
         return;
       case SAConstants.ActionResponse.REMAIN_OPEN:
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js
index 1e59ef067..893ad17 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/nodes/editable_text_node.js
@@ -111,7 +111,7 @@
         return SAConstants.ActionResponse.OPEN_TEXT_NAVIGATION_MENU;
       case SwitchAccessMenuAction.END_TEXT_SELECTION:
         TextNavigationManager.saveSelectEnd();
-        return SAConstants.ActionResponse.EXIT_MENU;
+        return SAConstants.ActionResponse.EXIT_SUBMENU;
 
       case SwitchAccessMenuAction.JUMP_TO_BEGINNING_OF_TEXT:
         TextNavigationManager.jumpToBeginning();
diff --git a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_constants.js b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_constants.js
index 95e9c18..fa3358a5 100644
--- a/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_constants.js
+++ b/chrome/browser/resources/chromeos/accessibility/switch_access/switch_access_constants.js
@@ -46,8 +46,8 @@
     REMAIN_OPEN: 0,
     // Closes the menu entirely.
     CLOSE_MENU: 1,
-    // Exits the current menu.
-    EXIT_MENU: 2,
+    // Exits the current submenu.
+    EXIT_SUBMENU: 2,
     RELOAD_MENU: 3,
     OPEN_TEXT_NAVIGATION_MENU: 4,
   },
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
index a2e191f..f665d0f1 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.html
@@ -94,10 +94,13 @@
     aria-label="[[_label(emoji, variants)]]">
   [[emoji]]
 </button>
-<paper-tooltip id="tooltip" for="emoji-button" fit-to-visible-bounds
-  part="tooltip" offset="8">
-  [[tooltip]]
-</paper-tooltip>
+<template is="dom-if" if="[[!variant]]">
+  <paper-tooltip id="tooltip" for="emoji-button" fit-to-visible-bounds
+    part="tooltip" offset="8">
+    [[tooltip]]
+  </paper-tooltip>
+</tooltip>
 <template is="dom-if" if="[[variantsVisible]]">
-  <emoji-variants variants="[[variants]]"></emoji-variants>
+  <emoji-variants variants="[[variants]]" tooltip="[[tooltip]]">
+  </emoji-variants>
 </template>
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.js
index 8e94d51..c3d48b5 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_button.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_button.js
@@ -36,7 +36,7 @@
       /** @type {?Array<Emoji>} */
       allVariants: {type: Array, readonly: true},
       /** @type {!string} */
-      toolTip: {type: String, readonly: true},
+      tooltip: {type: String, readonly: true},
     };
   }
 
@@ -59,7 +59,8 @@
       emoji: this.emoji,
       isVariant: this.variant,
       baseEmoji: this.base,
-      allVariants: this.allVariants ? this.allVariants : this.variants
+      allVariants: this.allVariants ? this.allVariants : this.variants,
+      name: this.tooltip
     }));
     ev.preventDefault();
     ev.stopPropagation();
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
index 1a084c9..0eaea462 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_picker.js
@@ -103,10 +103,11 @@
  * @return {!Array<EmojiVariants>} list of emoji data structures
  */
 function makeRecentlyUsed(recentEmoji) {
-  return recentEmoji.map(emoji => ({
-                           base: {string: emoji.base, name: '', keywords: []},
-                           alternates: emoji.alternates
-                         }));
+  return recentEmoji.map(
+      emoji => ({
+        base: {string: emoji.base, name: emoji.name, keywords: []},
+        alternates: emoji.alternates
+      }));
 }
 
 export class EmojiPicker extends PolymerElement {
@@ -180,7 +181,7 @@
         EMOJI_BUTTON_CLICK,
         ev => this.insertEmoji(
             ev.detail.emoji, ev.detail.isVariant, ev.detail.baseEmoji,
-            ev.detail.allVariants));
+            ev.detail.allVariants, ev.detail.name));
     this.addEventListener(
         EMOJI_CLEAR_RECENTS_CLICK, ev => this.clearRecentEmoji());
     // variant popup related handlers
@@ -272,15 +273,17 @@
    * @param {boolean} isVariant
    * @param {!string} baseEmoji
    * @param {!Array<!string>} allVariants
+   * @param {!string} name
    */
-  async insertEmoji(emoji, isVariant, baseEmoji, allVariants) {
+  async insertEmoji(emoji, isVariant, baseEmoji, allVariants, name) {
     document.activeElement.blur();
     this.apiProxy_.closeUI();
     this.$['search-container'].clearSearch();
     this.$.message.textContent = emoji + ' inserted.';
     const incognito = (await this.apiProxy_.isIncognitoTextField()).incognito;
     if (!incognito) {
-      this.recentEmojiStore.bumpEmoji({base: emoji, alternates: allVariants});
+      this.recentEmojiStore.bumpEmoji(
+          {base: emoji, alternates: allVariants, name: name});
       this.recentEmojiStore.savePreferredVariant(baseEmoji, emoji);
 
       this.set(
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
index 66555a91..b22d39c 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_search.html
@@ -61,11 +61,13 @@
     color: var(--cr-primary-text-color);
     font-size: 13px;
     line-height: var(--emoji-size);
+    user-select: none;
   }
 
   .sr-only {
     color: transparent;
     position: absolute;
+    user-select: none;
     z-index: -2;
   }
 
@@ -92,7 +94,7 @@
 
   #results {
     flex-grow: 1;
-    margin-inline-end: calc(0px - var(--emoji-picker-side-padding));
+    margin-inline-end: var(--emoji-picker-side-padding);
     overflow-y: scroll;
     padding-bottom: var(--emoji-picker-bottom-padding);
   }
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html
index 8cebbcd..8f506592 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.html
@@ -108,7 +108,7 @@
     <div class="variant-row">
       <template is="dom-repeat" items="[[row]]" as="emoji">
         <emoji-button emoji="[[emoji.string]]" variant base="[[baseEmoji]]"
-          all-variants="[[variants]]">
+          all-variants="[[variants]]" tooltip="[[tooltip]]">
         </emoji-button>
       </template>
     </div>
diff --git a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js
index b0f2aa4..00175df 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/emoji_variants.js
@@ -64,7 +64,9 @@
       /** @private {boolean} */
       showSkinTones: {type: Boolean},
       /** @private {boolean} */
-      showBaseEmoji: {type: Boolean}
+      showBaseEmoji: {type: Boolean},
+      /** @private {!string} */
+      tooltip: {type: String},
     };
   }
 
diff --git a/chrome/browser/resources/chromeos/emoji_picker/events.js b/chrome/browser/resources/chromeos/emoji_picker/events.js
index 0aa600d..aee6b3e 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/events.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/events.js
@@ -10,7 +10,8 @@
 export const GROUP_BUTTON_CLICK = 'group-button-click';
 
 /**
- * @typedef {!CustomEvent<{emoji: string, isVariant: boolean}>}
+ * @typedef {!CustomEvent<{emoji: string, isVariant: boolean, baseEmoji: string,
+ * allVariants:!Array<!string>, name:!string}>}
  */
 export let EmojiButtonClickEvent;
 
diff --git a/chrome/browser/resources/chromeos/emoji_picker/types.js b/chrome/browser/resources/chromeos/emoji_picker/types.js
index 3197737..bbec633 100644
--- a/chrome/browser/resources/chromeos/emoji_picker/types.js
+++ b/chrome/browser/resources/chromeos/emoji_picker/types.js
@@ -23,6 +23,7 @@
 export let EmojiGroupData;
 
 /**
- * @typedef {{base:string, alternates:!Array<!string>}} StoredEmoji
+ * @typedef {{base:string, alternates:!Array<!string>, name:!string}}
+ * StoredEmoji
  */
 export let StoredEmoji;
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index 2b0e896..d3006ce 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -62,9 +62,9 @@
   deps = [
     ":customize_dialog",
     ":middle_slot_promo",
-    ":most_visited",
     ":voice_search_overlay",
     "modules/dummy:module",
+    "//ui/webui/resources/cr_components/most_visited",
   ]
 }
 
@@ -107,21 +107,6 @@
   ]
 }
 
-js_library("most_visited") {
-  deps = [
-    ":i18n_setup",
-    ":new_tab_page_proxy",
-    ":window_proxy",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu.m",
-    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
-    "//ui/webui/resources/cr_elements/cr_toast:cr_toast.m",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:cr.m",
-    "//ui/webui/resources/js/cr/ui:focus_outline_manager.m",
-  ]
-}
-
 js_library("customize_dialog") {
   deps = [
     ":customize_backgrounds",
@@ -260,7 +245,6 @@
     "logo.js",
     "middle_slot_promo.js",
     "mini_page.js",
-    "most_visited.js",
     "iframe.js",
     "voice_search_overlay.js",
     "customize_modules.js",
@@ -313,7 +297,6 @@
   in_files = [
     "app.js",
     "middle_slot_promo.js",
-    "most_visited.js",
     "customize_dialog.js",
     "voice_search_overlay.js",
     "customize_backgrounds.js",
@@ -491,18 +474,22 @@
       "shared.rollup.js",
     ]
     excludes = [
+      "chrome://resources/cr_components/most_visited/most_visited.mojom-lite.js",
       "chrome://resources/js/cr.m.js",
       "chrome://resources/mojo/mojo/public/js/bindings.js",
       "chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js",
       "chrome://resources/mojo/mojo/public/mojom/base/big_buffer.mojom-lite.js",
       "chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-lite.js",
       "chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-lite.js",
+      "chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js",
       "chrome://resources/mojo/mojo/public/mojom/base/time.mojom-lite.js",
+      "chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js",
       "chrome://resources/mojo/mojo/public/mojom/base/unguessable_token.mojom-lite.js",
       "chrome://resources/mojo/skia/public/mojom/skcolor.mojom-lite.js",
       "chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js",
       "chrome://resources/mojo/url/mojom/origin.mojom-lite.js",
       "chrome://resources/mojo/url/mojom/url.mojom-lite.js",
+      "chrome://resources/mojo/url/mojom/url.mojom-webui.js",
       "new_tab_page.mojom-lite.js",
       "realbox/realbox.mojom-lite.js",
       "promo_browser_command.mojom-lite.js",
diff --git a/chrome/browser/resources/new_tab_page/app.html b/chrome/browser/resources/new_tab_page/app.html
index e424c5f..6e5fec7 100644
--- a/chrome/browser/resources/new_tab_page/app.html
+++ b/chrome/browser/resources/new_tab_page/app.html
@@ -1,6 +1,5 @@
 <style include="cr-shared-style">
   :host {
-    --ntp-theme-shortcut-background-color: rgb(229, 231, 232);
     --ntp-theme-text-color: var(--google-grey-800);
     --ntp-theme-text-shadow: none;
     --ntp-one-google-bar-height: 56px;
@@ -21,7 +20,6 @@
 
   @media (prefers-color-scheme: dark) {
     :host {
-      --ntp-theme-shortcut-background-color: var(--google-grey-refresh-100);
       --ntp-theme-text-color: white;
     }
   }
@@ -75,10 +73,10 @@
     visibility: visible;
   }
 
-  ntp-most-visited[dark] {
-    --icon-button-color-active: var(--google-grey-refresh-300);
-    --icon-button-color: white;
-    --tile-hover-color: rgba(255, 255, 255, .1);
+  cr-most-visited {
+    --most-visited-focus-shadow: var(--ntp-focus-shadow);
+    --most-visited-text-color: var(--ntp-theme-text-color);
+    --most-visited-text-shadow: var(--ntp-theme-text-shadow);
   }
 
   /* ~ because the dom-if results in a template between the middle-slot-promo
@@ -229,10 +227,8 @@
     position: fixed;
   }
 </style>
-<div id="content"
-    style="--ntp-theme-text-color: [[rgbaOrInherit_(theme_.shortcutTextColor)]];
-        --ntp-theme-shortcut-background-color:
-              [[rgbaOrInherit_(theme_.shortcutBackgroundColor)]];
+<div id="content" style="
+        --ntp-theme-text-color: [[rgbaOrInherit_(theme_.textColor)]];
         --ntp-logo-color: [[rgbaOrInherit_(logoColor_)]];">
   <template is="dom-if" if="[[lazyRender_]]">
     <ntp-iframe id="oneGoogleBar" src="[[oneGoogleBarIframePath_]]"
@@ -253,10 +249,8 @@
   <dom-if if="[[lazyRender_]]" on-dom-change="onLazyRendered_">
     <template>
       <template is="dom-if" if="[[shortcutsEnabled_]]">
-        <ntp-most-visited id="mostVisited" dark$="[[theme_.isDark]]"
-            use-white-add-icon$="[[theme_.shortcutUseWhiteAddIcon]]"
-            use-title-pill$="[[theme_.shortcutUseTitlePill]]">
-        </ntp-most-visited>
+        <cr-most-visited id="mostVisited" theme="[[theme_.mostVisited]]">
+        </cr-most-visited>
       </template>
       <template is="dom-if" if="[[middleSlotPromoEnabled_]]">
         <ntp-middle-slot-promo
diff --git a/chrome/browser/resources/new_tab_page/app.js b/chrome/browser/resources/new_tab_page/app.js
index af519b0..8aad4dc 100644
--- a/chrome/browser/resources/new_tab_page/app.js
+++ b/chrome/browser/resources/new_tab_page/app.js
@@ -806,7 +806,7 @@
         case $$(this, 'ntp-realbox'):
           recordClick(NtpElement.kRealbox);
           return;
-        case $$(this, 'ntp-most-visited'):
+        case $$(this, 'cr-most-visited'):
           recordClick(NtpElement.kMostVisited);
           return;
         case $$(this, 'ntp-middle-slot-promo'):
diff --git a/chrome/browser/resources/new_tab_page/customize_shortcuts.js b/chrome/browser/resources/new_tab_page/customize_shortcuts.js
index 5591393a..c5557f4e 100644
--- a/chrome/browser/resources/new_tab_page/customize_shortcuts.js
+++ b/chrome/browser/resources/new_tab_page/customize_shortcuts.js
@@ -41,37 +41,25 @@
 
   constructor() {
     super();
-    const {callbackRouter, handler} = NewTabPageProxy.getInstance();
-    /** @private {!newTabPage.mojom.PageCallbackRouter} */
-    this.callbackRouter_ = callbackRouter;
-    /** @private {newTabPage.mojom.PageHandlerRemote} */
+    const {handler} = NewTabPageProxy.getInstance();
+    /** @private {!newTabPage.mojom.PageHandlerRemote} */
     this.pageHandler_ = handler;
-    /** @private {?number} */
-    this.setMostVisitedInfoListenerId_ = null;
+    this.pageHandler_.getMostVisitedSettings().then(
+        ({customLinksEnabled, shortcutsVisible}) => {
+          this.customLinksEnabled_ = customLinksEnabled;
+          this.hide_ = !shortcutsVisible;
+        });
   }
 
   /** @override */
   connectedCallback() {
     super.connectedCallback();
-    this.setMostVisitedInfoListenerId_ =
-        this.callbackRouter_.setMostVisitedInfo.addListener(info => {
-          this.customLinksEnabled_ = info.customLinksEnabled;
-          this.hide_ = !info.visible;
-        });
-    this.pageHandler_.updateMostVisitedInfo();
     FocusOutlineManager.forDocument(document);
   }
 
-  /** @override */
-  disconnectedCallback() {
-    super.disconnectedCallback();
-    this.callbackRouter_.removeListener(
-        assert(this.setMostVisitedInfoListenerId_));
-  }
-
   apply() {
     this.pageHandler_.setMostVisitedSettings(
-        this.customLinksEnabled_, /* visible= */ !this.hide_);
+        this.customLinksEnabled_, /* shortcutsVisible= */ !this.hide_);
   }
 
   /**
diff --git a/chrome/browser/resources/new_tab_page/lazy_load.js b/chrome/browser/resources/new_tab_page/lazy_load.js
index 9fb298d..ed9476d 100644
--- a/chrome/browser/resources/new_tab_page/lazy_load.js
+++ b/chrome/browser/resources/new_tab_page/lazy_load.js
@@ -12,5 +12,5 @@
 
 import './customize_dialog.js';
 import './middle_slot_promo.js';
-import './most_visited.js';
 import './voice_search_overlay.js';
+import 'chrome://resources/cr_components/most_visited/most_visited.js';
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page_proxy.js b/chrome/browser/resources/new_tab_page/new_tab_page_proxy.js
index 7177489..4e94834 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page_proxy.js
+++ b/chrome/browser/resources/new_tab_page/new_tab_page_proxy.js
@@ -11,6 +11,8 @@
 import 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-lite.js';
 import 'chrome://resources/mojo/url/mojom/url.mojom-lite.js';
 
+import 'chrome://resources/cr_components/most_visited/most_visited.mojom-lite.js';
+
 import './realbox/realbox.mojom-lite.js';
 import './new_tab_page.mojom-lite.js';
 
diff --git a/chrome/browser/resources/settings/a11y_page/captions_subpage.html b/chrome/browser/resources/settings/a11y_page/captions_subpage.html
index 86ef0284..817e6d1 100644
--- a/chrome/browser/resources/settings/a11y_page/captions_subpage.html
+++ b/chrome/browser/resources/settings/a11y_page/captions_subpage.html
@@ -4,8 +4,8 @@
         min-height: auto;
       }
       .preview-box {
-        align-items: center;
         all: initial;
+        align-items: center;
         background-image:
           url(chrome://theme/IDR_ACCESSIBILITY_CAPTIONS_PREVIEW_BACKGROUND);
         background-position: center;
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html
index 4c222789..aba954d 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.html
@@ -131,7 +131,8 @@
         </template>
         <cr-button id="done"
             on-click="onRenameDialogDoneTap_"
-            disabled="[[isRenameInProgress_]]"
+            disabled="[[isDoneButtonDisabled_(isRenameInProgress_,
+                esimProfileName_)]]"
             aria-label$="[[getDoneBtnA11yLabel_(esimProfileName_)]]"
             aria-describedby="warningMessage"
             class="action-button">
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.js b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.js
index 9058b80..d3305da 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/esim_rename_dialog.js
@@ -5,6 +5,9 @@
 /** @type {number} */
 const MAX_INPUT_LENGTH = 20;
 
+/** @type {number} */
+const MIN_INPUT_LENGTH = 1;
+
 /** @type {RegExp} */
 const EMOJI_REGEX_EXP =
     /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/gi;
@@ -199,8 +202,22 @@
   },
 
   /**
+   * @param {boolean} isRenameInProgress
    * @param {string} esimProfileName
-   * @returns {string}
+   * @return {boolean}
+   * @private
+   */
+  isDoneButtonDisabled_(isRenameInProgress, esimProfileName) {
+    if (isRenameInProgress) {
+      return true;
+    }
+    return esimProfileName.length < MIN_INPUT_LENGTH;
+  },
+
+  /**
+   * @param {string} esimProfileName
+   * @return {string}
+   * @private
    */
   getDoneBtnA11yLabel_(esimProfileName) {
     return this.i18n('eSimRenameProfileDoneBtnA11yLabel', esimProfileName);
diff --git a/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.html b/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.html
index b97158f..dc012fc7 100644
--- a/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.html
+++ b/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.html
@@ -96,8 +96,7 @@
       </template>
       <cr-button id="add-account-button" on-click="onAddAccountClick_"
           disabled="[[!addAccountsAllowed_]]"
-          deep-link-focus-id$="[[Setting.kAddKerberosTicket]]
-              [[Setting.kAddKerberosTicketV2]]">
+          deep-link-focus-id$="[[Setting.kAddKerberosTicketV2]]">
         <div id="add-account-icon"></div>
         $i18n{kerberosAccountsAddAccountLabel}
       </cr-button>
@@ -148,9 +147,7 @@
           <cr-icon-button class="icon-more-vert more-actions"
               title="$i18n{moreActions}"
               on-click="onAccountActionsMenuButtonClick_"
-              deep-link-focus-id$="[[Setting.kRemoveKerberosTicket]]
-                  [[Setting.kRemoveKerberosTicketV2]]
-                  [[Setting.kSetActiveKerberosTicket]]
+              deep-link-focus-id$="[[Setting.kRemoveKerberosTicketV2]]
                   [[Setting.kSetActiveKerberosTicketV2]]">
           </cr-icon-button>
         </div>
diff --git a/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.js b/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.js
index b35ac71..fe0bd3a 100644
--- a/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.js
+++ b/chrome/browser/resources/settings/chromeos/kerberos_page/kerberos_accounts.js
@@ -62,9 +62,6 @@
     supportedSettingIds: {
       type: Object,
       value: () => new Set([
-        chromeos.settings.mojom.Setting.kAddKerberosTicket,
-        chromeos.settings.mojom.Setting.kRemoveKerberosTicket,
-        chromeos.settings.mojom.Setting.kSetActiveKerberosTicket,
         chromeos.settings.mojom.Setting.kAddKerberosTicketV2,
         chromeos.settings.mojom.Setting.kRemoveKerberosTicketV2,
         chromeos.settings.mojom.Setting.kSetActiveKerberosTicketV2,
@@ -109,8 +106,7 @@
    */
   currentRouteChanged(route, oldRoute) {
     // Does not apply to this page.
-    if (route !== settings.routes.KERBEROS_ACCOUNTS &&
-        route !== settings.routes.KERBEROS_ACCOUNTS_V2) {
+    if (route !== settings.routes.KERBEROS_ACCOUNTS_V2) {
       return;
     }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_page_visibility.js b/chrome/browser/resources/settings/chromeos/os_page_visibility.js
index af2be2c..ab2b5d4 100644
--- a/chrome/browser/resources/settings/chromeos/os_page_visibility.js
+++ b/chrome/browser/resources/settings/chromeos/os_page_visibility.js
@@ -50,7 +50,6 @@
 /**
  * @typedef {{
  *   googleAccounts: boolean,
- *   kerberosAccounts: boolean,
  *   lockScreen: boolean,
  *   manageUsers: boolean,
  * }}
@@ -137,8 +136,6 @@
       autofill: true,
       people: {
         lockScreen: true,
-        kerberosAccounts:
-            isKerberosEnabled && !isKerberosSettingsSectionEnabled,
         googleAccounts: isAccountManagerEnabled,
         manageUsers: true,
       },
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
index 9f517e32..68df054 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
@@ -134,8 +134,8 @@
     "..:os_page_visibility.m",
     "..:os_route.m",
     "../..:router",
+    "../../people_page:account_manager_browser_proxy",
     "../../settings_page:settings_animated_pages",
-    "../kerberos_page:kerberos_accounts.m",
     "../localized_link:localized_link",
     "../parental_controls_page:parental_controls_page.m",
     "//chrome/browser/resources/settings/people_page:profile_info_browser_proxy",
@@ -268,7 +268,6 @@
     "../..:web_components_local",
     "../../people_page:web_components",
     "../../privacy_page:polymer3_elements",
-    "../kerberos_page:polymer3_elements",
   ]
 }
 
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
index 8a7e75e..5ccf5ef 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
@@ -26,7 +26,6 @@
 <link rel="import" href="../../settings_shared_css.html">
 <link rel="import" href="../../i18n_setup.html">
 <link rel="import" href="../deep_linking_behavior.html">
-<link rel="import" href="../kerberos_page/kerberos_accounts.html">
 <link rel="import" href="../os_route.html">
 <link rel="import" href="../os_page_visibility.html">
 <link rel="import" href="../parental_controls_page/parental_controls_page.html">
@@ -145,16 +144,6 @@
           </cr-link-row>
         </template>
 
-        <template is="dom-if" if="[[pageVisibility.people.kerberosAccounts]]">
-          <cr-link-row id="kerberos-accounts-subpage-trigger" class="hr"
-              on-click="onKerberosAccountsTap_"
-              label="$i18n{kerberosAccountsSubMenuLabel}"
-              role-description="$i18n{subpageArrowRoleDescription}">
-            <cr-policy-indicator indicator-type="userPolicy">
-            </cr-policy-indicator>
-          </cr-link-row>
-        </template>
-
       </div>
 
       <template is="dom-if" route-path="/syncSetup">
@@ -238,15 +227,6 @@
           </settings-account-manager>
         </settings-subpage>
       </template>
-      <template is="dom-if" if="[[pageVisibility.people.kerberosAccounts]]">
-        <template is="dom-if" route-path="/kerberosAccounts">
-          <settings-subpage
-              associated-control="[[$$('#kerberos-accounts-subpage-trigger')]]"
-              page-title="$i18n{kerberosAccountsPageTitle}">
-            <settings-kerberos-accounts></settings-kerberos-accounts>
-          </settings-subpage>
-        </template>
-      </template>
     </settings-animated-pages>
 
     <template is="dom-if" if="[[!isAccountManagementFlowsV2Enabled_]]">
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
index 646c970..34db6cba 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
@@ -149,11 +149,6 @@
               settings.routes.ACCOUNT_MANAGER.path,
               '#account-manager-subpage-trigger');
         }
-        if (settings.routes.KERBEROS_ACCOUNTS) {
-          map.set(
-              settings.routes.KERBEROS_ACCOUNTS.path,
-              '#kerberos-accounts-subpage-trigger');
-        }
         return map;
       },
     },
@@ -522,11 +517,6 @@
   },
 
   /** @private */
-  onKerberosAccountsTap_() {
-    settings.Router.getInstance().navigateTo(settings.routes.KERBEROS_ACCOUNTS);
-  },
-
-  /** @private */
   onManageOtherPeople_() {
     assert(
         !this.isAccountManagementFlowsV2Enabled_,
diff --git a/chrome/browser/resources/settings/chromeos/os_route.js b/chrome/browser/resources/settings/chromeos/os_route.js
index 31d7c02..761d5a2 100644
--- a/chrome/browser/resources/settings/chromeos/os_route.js
+++ b/chrome/browser/resources/settings/chromeos/os_route.js
@@ -117,9 +117,6 @@
             r.OS_PEOPLE, mojom.MANAGE_OTHER_PEOPLE_SUBPAGE_PATH,
             Subpage.kManageOtherPeople);
       }
-      r.KERBEROS_ACCOUNTS = createSubpage(
-          r.OS_PEOPLE, mojom.KERBEROS_ACCOUNTS_SUBPAGE_PATH,
-          Subpage.kKerberosAccounts);
     }
 
     const isKerberosEnabled = loadTimeData.valueExists('isKerberosEnabled') &&
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_routes.js b/chrome/browser/resources/settings/chromeos/os_settings_routes.js
index 763ba60a..d169d29 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_routes.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_routes.js
@@ -46,7 +46,6 @@
  *   INTERNET: !settings.Route,
  *   INTERNET_NETWORKS: !settings.Route,
  *   KERBEROS: !settings.Route,
- *   KERBEROS_ACCOUNTS: !settings.Route,
  *   KERBEROS_ACCOUNTS_V2: !settings.Route,
  *   KEYBOARD: !settings.Route,
  *   KNOWN_NETWORKS: !settings.Route,
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
index 5566592..77f7b59d 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
@@ -79,6 +79,8 @@
 
     if (reset_shortcuts_enabled_)
       scoped_feature_list_.InitAndEnableFeature(kResetShortcutsFeature);
+    else
+      scoped_feature_list_.InitAndDisableFeature(kResetShortcutsFeature);
 
     SetChromeCleanerRunnerTestDelegateForTesting(this);
   }
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc
index 86f6dc7..cb936e8 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/srt_field_trial_win.cc
@@ -40,7 +40,7 @@
     "ChromeCleanupDistribution", base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kResetShortcutsFeature{"ChromeCleanupResetShortcuts",
-                                           base::FEATURE_DISABLED_BY_DEFAULT};
+                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
 bool IsSRTPromptFeatureEnabled() {
   return base::FeatureList::IsEnabled(kChromeCleanupInBrowserPromptFeature);
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_delegate.cc b/chrome/browser/safe_browsing/client_side_detection_host_delegate.cc
index 0612867..7b29d98 100644
--- a/chrome/browser/safe_browsing/client_side_detection_host_delegate.cc
+++ b/chrome/browser/safe_browsing/client_side_detection_host_delegate.cc
@@ -93,6 +93,8 @@
           kCSDAttributionUserGestureLimitForExtendedReporting,
           verdict->mutable_referrer_chain());
 
+  UMA_HISTOGRAM_COUNTS_100("SafeBrowsing.ReferrerURLChainSize.CSDAttribution",
+                           verdict->referrer_chain().size());
   UMA_HISTOGRAM_ENUMERATION(
       "SafeBrowsing.ReferrerAttributionResult.CSDAttribution", result,
       SafeBrowsingNavigationObserverManager::ATTRIBUTION_FAILURE_TYPE_MAX);
diff --git a/chrome/browser/subresource_redirect/subresource_redirect_observer.cc b/chrome/browser/subresource_redirect/subresource_redirect_observer.cc
index 6ab7444..d36d497 100644
--- a/chrome/browser/subresource_redirect/subresource_redirect_observer.cc
+++ b/chrome/browser/subresource_redirect/subresource_redirect_observer.cc
@@ -317,7 +317,7 @@
   content::RenderFrameHost* parent_render_frame_host =
       navigation_handle->GetRenderFrameHost();
   while ((parent_render_frame_host = parent_render_frame_host->GetParent())) {
-    if (!parent_render_frame_host->IsCurrent())
+    if (!parent_render_frame_host->IsActive())
       continue;
     // Existence of ImageCompressionAppliedDocument for the parent render frame
     // indicates the parent is not logged-in and allowed fo subresource
diff --git a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
index 4681502..0aad1b0 100644
--- a/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
+++ b/chrome/browser/task_manager/providers/web_contents/web_contents_task_provider.cc
@@ -169,7 +169,7 @@
 void WebContentsTaskProvider::WebContentsEntry::RenderFrameHostChanged(
     RenderFrameHost* old_host,
     RenderFrameHost* new_host) {
-  DCHECK(new_host->IsCurrent());
+  DCHECK(new_host->IsActive());
 
   // The navigating frame and its subframes are now pending deletion. Stop
   // tracking them immediately rather than when they are destroyed. The order of
@@ -188,7 +188,7 @@
 
   // Skip pending/speculative hosts. We'll create tasks for these if the
   // navigation commits, at which point RenderFrameHostChanged() will fire.
-  if (!render_frame_host->IsCurrent())
+  if (!render_frame_host->IsActive())
     return;
 
   CreateTaskForFrame(render_frame_host);
@@ -271,7 +271,7 @@
     RenderFrameHost* render_frame_host) {
   // Currently we do not track pending hosts, or pending delete hosts.
   DCHECK(render_frame_host);
-  DCHECK(render_frame_host->IsCurrent());
+  DCHECK(render_frame_host->IsActive());
 
   // Exclude sad tabs and sad OOPIFs.
   if (!render_frame_host->IsRenderFrameLive())
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index e5f42e0b9..47e8d73 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -599,6 +599,7 @@
     "//ui/web_dialogs",
     "//ui/webui",
     "//ui/webui/resources/cr_components/customize_themes:mojom",
+    "//ui/webui/resources/cr_components/most_visited:mojom",
     "//v8:v8_version",
   ]
 
@@ -1341,6 +1342,8 @@
       "webui/commander/commander_handler.h",
       "webui/commander/commander_ui.cc",
       "webui/commander/commander_ui.h",
+      "webui/cr_components/most_visited/most_visited_handler.cc",
+      "webui/cr_components/most_visited/most_visited_handler.h",
       "webui/customize_themes/chrome_customize_themes_handler.cc",
       "webui/customize_themes/chrome_customize_themes_handler.h",
       "webui/devtools_ui.cc",
diff --git a/chrome/browser/ui/DEPS b/chrome/browser/ui/DEPS
index 1d75bb8b..4d5cdd7e 100644
--- a/chrome/browser/ui/DEPS
+++ b/chrome/browser/ui/DEPS
@@ -29,6 +29,9 @@
   "fullscreen_controller_interactive_browsertest\.cc": [
     "+ash/shell.h",
   ],
+  "popup_browsertest\.cc": [
+    "+ash/shell.h",
+  ],
   "sad_tab_helper.cc": [
     "+content/common/content_navigation_policy.h",
   ]
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc
index 448d7819..f986a5e 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger.cc
@@ -16,8 +16,8 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/power/ml/recent_events_counter.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h"
+#include "chrome/browser/ash/power/ml/recent_events_counter.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h"
 #include "chrome/browser/metrics/chrome_metrics_service_client.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
diff --git a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger_helper.h b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger_helper.h
index 314b54b..de6b85a 100644
--- a/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger_helper.h
+++ b/chrome/browser/ui/app_list/search/search_result_ranker/app_launch_event_logger_helper.h
@@ -8,7 +8,7 @@
 #include <array>
 
 #include "base/time/time.h"
-#include "chrome/browser/chromeos/power/ml/user_activity_ukm_logger_helpers.h"
+#include "chrome/browser/ash/power/ml/user_activity_ukm_logger_helpers.h"
 
 namespace app_list {
 
diff --git a/chrome/browser/ui/ash/chrome_new_window_client.cc b/chrome/browser/ui/ash/chrome_new_window_client.cc
index a139666..9991062 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client.cc
@@ -122,8 +122,6 @@
       chromeos::settings::mojom::kExternalStorageSubpagePath},
      {ChromePage::HELP, chromeos::settings::mojom::kAboutChromeOsSectionPath},
      {ChromePage::INTERNET, chromeos::settings::mojom::kNetworkSectionPath},
-     {ChromePage::KERBEROSACCOUNTS,
-      chromeos::settings::mojom::kKerberosAccountsSubpagePath},
      {ChromePage::KEYBOARDOVERLAY,
       chromeos::settings::mojom::kKeyboardSubpagePath},
      {ChromePage::KNOWNNETWORKS,
@@ -201,11 +199,12 @@
      {ChromePage::ABOUTHISTORY, "chrome://history/"}};
 
 constexpr arc::mojom::ChromePage kDeprecatedPages[] = {
-    ChromePage::DEPRECATED_DOWNLOADEDCONTENT,
-    ChromePage::DEPRECATED_PLUGINVMDETAILS,
     ChromePage::DEPRECATED_CROSTINIDISKRESIZE,
+    ChromePage::DEPRECATED_DOWNLOADEDCONTENT,
+    ChromePage::DEPRECATED_KERBEROSACCOUNTS,
     ChromePage::DEPRECATED_OSLANGUAGESDETAILS,
     ChromePage::DEPRECATED_OSLANGUAGESINPUTMETHODS,
+    ChromePage::DEPRECATED_PLUGINVMDETAILS,
 };
 
 // mojom::ChromePage::LAST returns the amount of valid entries - 1.
diff --git a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
index 2cdf4e4c..b605c9f0 100644
--- a/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
+++ b/chrome/browser/ui/ash/chrome_new_window_client_browsertest.cc
@@ -395,10 +395,6 @@
       ChromePage::INTERNET,
       base_url.Resolve(chromeos::settings::mojom::kNetworkSectionPath));
   TestOpenOSSettingsChromePage(
-      ChromePage::KERBEROSACCOUNTS,
-      base_url.Resolve(
-          chromeos::settings::mojom::kKerberosAccountsSubpagePath));
-  TestOpenOSSettingsChromePage(
       ChromePage::KNOWNNETWORKS,
       base_url.Resolve(chromeos::settings::mojom::kKnownNetworksSubpagePath));
   TestOpenOSSettingsChromePage(
diff --git a/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc b/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc
index 0e55cfd..ec2cabe 100644
--- a/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc
+++ b/chrome/browser/ui/ash/shelf/extension_shelf_context_menu.cc
@@ -19,8 +19,6 @@
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller.h"
 #include "chrome/browser/ui/ash/shelf/chrome_shelf_controller_util.h"
 #include "chrome/browser/ui/browser_commands.h"
-#include "chrome/browser/web_applications/system_web_apps/system_web_app_manager.h"
-#include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/common/extensions/extension_constants.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -56,16 +54,9 @@
   extension_items_ = std::make_unique<extensions::ContextMenuMatcher>(
       profile, this, menu_model.get(),
       base::BindRepeating(MenuItemHasLauncherContext));
-  // V1 apps can be started from the menu - but V2 apps and system web apps
-  // should not.
-  bool is_system_web_app = web_app::WebAppProvider::Get(profile)
-                               ->system_web_app_manager()
-                               .IsSystemWebApp(app_id);
-  const bool is_platform_app = controller()->IsPlatformApp(item().id);
 
   if (item().type == ash::TYPE_PINNED_APP || item().type == ash::TYPE_APP) {
-    if (!is_platform_app && !is_system_web_app)
-      CreateOpenNewSubmenu(menu_model.get());
+    CreateOpenNewSubmenu(menu_model.get());
     AddPinMenu(menu_model.get());
 
     if (controller()->IsOpen(item().id)) {
@@ -91,8 +82,7 @@
   }
   if (app_id != extension_misc::kChromeAppId) {
     AddContextMenuOption(menu_model.get(), ash::UNINSTALL,
-                         is_platform_app ? IDS_APP_LIST_UNINSTALL_ITEM
-                                         : IDS_APP_LIST_EXTENSIONS_UNINSTALL);
+                         IDS_APP_LIST_EXTENSIONS_UNINSTALL);
   }
 
   if (controller()->CanDoShowAppInfoFlow(profile, app_id)) {
diff --git a/chrome/browser/ui/popup_browsertest.cc b/chrome/browser/ui/popup_browsertest.cc
index 2a29de7..d931b4b 100644
--- a/chrome/browser/ui/popup_browsertest.cc
+++ b/chrome/browser/ui/popup_browsertest.cc
@@ -12,14 +12,23 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/embedder_support/switches.h"
+#include "components/permissions/permission_request_manager.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/browser_test.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/display/screen_base.h"
+#include "ui/display/test/scoped_screen_override.h"
+#include "ui/gfx/native_widget_types.h"
 #include "ui/views/widget/widget.h"
 #include "ui/views/widget/widget_observer.h"
 #include "url/gurl.h"
 
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ash/shell.h"
+#include "ui/display/test/display_manager_test_api.h"
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+
 namespace {
 
 // Tests of window placement for popup browser windows. Test fixtures are run
@@ -222,4 +231,78 @@
   }
 }
 
+// TODO(crbug.com/1183791): Disabled on non-ChromeOS because of races with
+// SetScreenInstance and observers not being notified.
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#define MAYBE_AboutBlankCrossScreenPlacement \
+  DISABLED_AboutBlankCrossScreenPlacement
+#else
+#define MAYBE_AboutBlankCrossScreenPlacement AboutBlankCrossScreenPlacement
+#endif
+// Tests that an about:blank popup can be moved across screens with permission.
+IN_PROC_BROWSER_TEST_P(PopupBrowserTest, MAYBE_AboutBlankCrossScreenPlacement) {
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  display::test::DisplayManagerTestApi(ash::Shell::Get()->display_manager())
+      .UpdateDisplay("100+100-801x802,901+100-802x802");
+#else
+  display::ScreenBase test_screen;
+  test_screen.display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
+                                        display::DisplayList::Type::PRIMARY);
+  test_screen.display_list().AddDisplay(
+      {2, gfx::Rect(901, 100, 802, 802)},
+      display::DisplayList::Type::NOT_PRIMARY);
+  display::test::ScopedScreenOverride screen_override(&test_screen);
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+  display::Screen* screen = display::Screen::GetScreen();
+  ASSERT_EQ(2, screen->GetNumDisplays());
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const GURL url(embedded_test_server()->GetURL("/empty.html"));
+  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
+  auto* opener = browser()->tab_strip_model()->GetActiveWebContents();
+
+  // TODO(crbug.com/1119974): this test could be in content_browsertests
+  // and not browser_tests if permission controls were supported.
+
+  if (GetParam()) {  // Check whether the WindowPlacement feature is enabled.
+    // Request and auto-accept the Window Placement permission request.
+    permissions::PermissionRequestManager* permission_request_manager =
+        permissions::PermissionRequestManager::FromWebContents(opener);
+    permission_request_manager->set_auto_response_for_test(
+        permissions::PermissionRequestManager::ACCEPT_ALL);
+    constexpr char kGetScreensLength[] = R"(
+      (async () => {
+        try { return (await getScreens()).screens.length; } catch { return 0; }
+      })();
+    )";
+    EXPECT_EQ(2, EvalJs(opener, kGetScreensLength));
+    // Do not auto-accept any other permission requests.
+    permission_request_manager->set_auto_response_for_test(
+        permissions::PermissionRequestManager::NONE);
+  }
+
+  // Open an about:blank popup. It should start on the same screen as browser().
+  Browser* popup = OpenPopup(
+      browser(), "w = open('about:blank', '', 'width=200,height=200');");
+  const auto opener_display = GetDisplayNearestBrowser(browser());
+  auto original_popup_display = GetDisplayNearestBrowser(popup);
+  EXPECT_EQ(opener_display, original_popup_display);
+
+  // Have the opener try to move the popup to the second screen.
+  content::ExecuteScriptAsync(opener, "w.moveTo(999, 199);");
+
+  // Wait for the substantial move, widgets may move during initialization.
+  auto* widget = views::Widget::GetWidgetForNativeWindow(
+      popup->window()->GetNativeWindow());
+  WidgetBoundsChangeWaiter(widget, /*move_by=*/40, /*resize_by=*/0).Wait();
+  auto new_popup_display = GetDisplayNearestBrowser(popup);
+  // The popup only moves to the second screen with Window Placement permission.
+  EXPECT_EQ(GetParam(), original_popup_display != new_popup_display);
+  // The popup is always constrained to the bounds of the target display.
+  auto popup_bounds = popup->window()->GetBounds();
+  EXPECT_TRUE(new_popup_display.work_area().Contains(popup_bounds))
+      << " work_area: " << new_popup_display.work_area().ToString()
+      << " popup: " << popup_bounds.ToString();
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/views/bubble/webui_bubble_manager.cc b/chrome/browser/ui/views/bubble/webui_bubble_manager.cc
index 462cc4d6..353a77d 100644
--- a/chrome/browser/ui/views/bubble/webui_bubble_manager.cc
+++ b/chrome/browser/ui/views/bubble/webui_bubble_manager.cc
@@ -35,7 +35,11 @@
     bubble_view_->SetAnchorRect(*anchor_rect_);
   }
   bubble_widget_observation_.Observe(bubble_view_->GetWidget());
-  if (!disable_close_bubble_helper_) {
+  // Some bubbles can be triggered when there is no active browser (e.g. emoji
+  // picker in Chrome OS launcher). In that case, the close bubble helper isn't
+  // needed.
+  if ((!disable_close_bubble_helper_) &&
+      BrowserList::GetInstance()->GetLastActive()) {
     close_bubble_helper_ = std::make_unique<CloseBubbleOnTabActivationHelper>(
         bubble_view_.get(), BrowserList::GetInstance()->GetLastActive());
   }
diff --git a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.cc b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.cc
index a6a9e4c0..f762141f 100644
--- a/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.cc
+++ b/chrome/browser/ui/views/frame/top_controls_slide_controller_chromeos.cc
@@ -201,7 +201,7 @@
   void DidFailLoad(content::RenderFrameHost* render_frame_host,
                    const GURL& validated_url,
                    int error_code) override {
-    if (render_frame_host->IsCurrent() &&
+    if (render_frame_host->IsActive() &&
         (render_frame_host == web_contents()->GetMainFrame())) {
       UpdateBrowserControlsStateShown(/*animate=*/true);
     }
diff --git a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
index 3ff71714..9b21b86 100644
--- a/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
+++ b/chrome/browser/ui/views/payments/payment_request_browsertest_base.cc
@@ -498,7 +498,7 @@
     mojo::PendingReceiver<payments::mojom::PaymentRequest> receiver,
     content::RenderFrameHost* render_frame_host) {
   DCHECK(render_frame_host);
-  DCHECK(render_frame_host->IsCurrent());
+  DCHECK(render_frame_host->IsActive());
   std::unique_ptr<TestChromePaymentRequestDelegate> delegate =
       std::make_unique<TestChromePaymentRequestDelegate>(
           render_frame_host, /*observer=*/this, &prefs_, is_incognito_,
diff --git a/chrome/browser/ui/views/payments/test_secure_payment_confirmation_payment_request_delegate.cc b/chrome/browser/ui/views/payments/test_secure_payment_confirmation_payment_request_delegate.cc
index d47a1cc..eb1e5d58 100644
--- a/chrome/browser/ui/views/payments/test_secure_payment_confirmation_payment_request_delegate.cc
+++ b/chrome/browser/ui/views/payments/test_secure_payment_confirmation_payment_request_delegate.cc
@@ -32,7 +32,7 @@
 void TestSecurePaymentConfirmationPaymentRequestDelegate::ShowDialog(
     base::WeakPtr<PaymentRequest> request) {
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  if (rfh && rfh->IsCurrent()) {
+  if (rfh && rfh->IsActive()) {
     dialog_view_->ShowDialog(content::WebContents::FromRenderFrameHost(rfh),
                              model_->GetWeakPtr(), base::DoNothing(),
                              base::DoNothing());
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index 1ebaac5d..b80a9dc170 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -301,9 +301,6 @@
   home_ = AddChildView(std::move(home));
   location_bar_ = AddChildView(std::move(location_bar));
 
-  if (read_later_button)
-    read_later_button_ = AddChildView(std::move(read_later_button));
-
   if (extensions_container)
     extensions_container_ = AddChildView(std::move(extensions_container));
 
@@ -335,6 +332,9 @@
   if (send_tab_to_self_button)
     send_tab_to_self_button_ = AddChildView(std::move(send_tab_to_self_button));
 
+  if (read_later_button)
+    read_later_button_ = AddChildView(std::move(read_later_button));
+
   if (toolbar_account_icon_container) {
     toolbar_account_icon_container_ =
         AddChildView(std::move(toolbar_account_icon_container));
diff --git a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
index 4ba2fe6..ff1fb00 100644
--- a/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
+++ b/chrome/browser/ui/web_applications/system_web_app_ui_utils.cc
@@ -78,7 +78,7 @@
 
 absl::optional<SystemAppType> GetSystemWebAppTypeForAppId(Profile* profile,
                                                           AppId app_id) {
-  auto* provider = WebAppProvider::Get(profile);
+  auto* provider = WebAppProvider::GetForSystemWebApps(profile);
   return provider ? provider->system_web_app_manager().GetSystemAppTypeForAppId(
                         app_id)
                   : absl::optional<SystemAppType>();
@@ -86,7 +86,7 @@
 
 absl::optional<AppId> GetAppIdForSystemWebApp(Profile* profile,
                                               SystemAppType app_type) {
-  auto* provider = WebAppProvider::Get(profile);
+  auto* provider = WebAppProvider::GetForSystemWebApps(profile);
   return provider
              ? provider->system_web_app_manager().GetAppIdForSystemApp(app_type)
              : absl::optional<AppId>();
@@ -101,7 +101,7 @@
   if (!app_id)
     return absl::nullopt;
 
-  auto* provider = WebAppProvider::Get(profile);
+  auto* provider = WebAppProvider::GetForSystemWebApps(profile);
   DCHECK(provider);
 
   DisplayMode display_mode =
@@ -215,7 +215,7 @@
     return nullptr;
   }
 
-  auto* provider = WebAppProvider::Get(profile);
+  auto* provider = WebAppProvider::GetForSystemWebApps(profile);
   if (!provider)
     return nullptr;
 
@@ -305,7 +305,7 @@
   if (!app_id)
     return nullptr;
 
-  auto* provider = WebAppProvider::Get(profile);
+  auto* provider = WebAppProvider::GetForSystemWebApps(profile);
   DCHECK(provider);
 
   if (!provider->registrar().IsInstalled(app_id.value()))
@@ -336,7 +336,7 @@
 
 absl::optional<SystemAppType> GetCapturingSystemAppForURL(Profile* profile,
                                                           const GURL& url) {
-  auto* provider = WebAppProvider::Get(profile);
+  auto* provider = WebAppProvider::GetForSystemWebApps(profile);
 
   if (!provider)
     return absl::nullopt;
@@ -353,7 +353,7 @@
   if (!app_controller->HasAppId())
     return gfx::Size();
 
-  auto* provider = WebAppProvider::Get(browser->profile());
+  auto* provider = WebAppProvider::GetForSystemWebApps(browser->profile());
   if (!provider)
     return gfx::Size();
 
diff --git a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
index d87bb29..0f4363da 100644
--- a/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
+++ b/chrome/browser/ui/web_applications/test/system_web_app_interactive_uitest.cc
@@ -56,6 +56,9 @@
  public:
   SystemWebAppLinkCaptureBrowserTest()
       : SystemWebAppManagerBrowserTest(/*install_mock*/ false) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    WebAppProvider::EnableSystemWebAppsInLacrosForTesting();
+#endif
     maybe_installation_ =
         TestSystemWebAppInstallation::SetUpAppThatCapturesNavigation();
   }
@@ -504,7 +507,7 @@
 
   void WaitForSystemWebAppInstall(Profile* profile) {
     base::RunLoop run_loop;
-    web_app::WebAppProvider::Get(profile)
+    web_app::WebAppProvider::GetForSystemWebApps(profile)
         ->system_web_app_manager()
         .on_apps_synchronized()
         .Post(FROM_HERE, base::BindLambdaForTesting([&]() {
@@ -518,7 +521,8 @@
 
   AppId GetAppId(Profile* profile) {
     SystemWebAppManager& manager =
-        web_app::WebAppProvider::Get(profile)->system_web_app_manager();
+        web_app::WebAppProvider::GetForSystemWebApps(profile)
+            ->system_web_app_manager();
 
     absl::optional<AppId> app_id =
         manager.GetAppIdForSystemApp(installation_->GetType());
diff --git a/chrome/browser/ui/webui/chromeos/emoji/emoji_ui.h b/chrome/browser/ui/webui/chromeos/emoji/emoji_ui.h
index 4712fe4..7bbbfd0a 100644
--- a/chrome/browser/ui/webui/chromeos/emoji/emoji_ui.h
+++ b/chrome/browser/ui/webui/chromeos/emoji/emoji_ui.h
@@ -17,6 +17,7 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "ui/webui/mojo_bubble_web_ui_controller.h"
 
+#include "chrome/browser/ui/views/bubble/webui_bubble_manager.h"
 class Profile;
 
 namespace chromeos {
diff --git a/chrome/browser/ui/webui/cr_components/most_visited/OWNERS b/chrome/browser/ui/webui/cr_components/most_visited/OWNERS
new file mode 100644
index 0000000..a8e0f5e
--- /dev/null
+++ b/chrome/browser/ui/webui/cr_components/most_visited/OWNERS
@@ -0,0 +1 @@
+file://chrome/browser/ui/webui/new_tab_page/OWNERS
diff --git a/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc b/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc
new file mode 100644
index 0000000..1518c9ac
--- /dev/null
+++ b/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.cc
@@ -0,0 +1,210 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h"
+
+#include <map>
+#include <vector>
+
+#include "base/feature_list.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/search/instant_service.h"
+#include "chrome/browser/search/instant_service_factory.h"
+#include "chrome/browser/search/search.h"
+#include "chrome/browser/search_engines/template_url_service_factory.h"
+#include "components/ntp_tiles/constants.h"
+#include "components/ntp_tiles/most_visited_sites.h"
+#include "components/search/ntp_features.h"
+#include "components/search_engines/template_url_service.h"
+#include "content/public/browser/web_contents.h"
+
+namespace {
+
+ntp_tiles::NTPTileImpression MakeNTPTileImpression(
+    const most_visited::mojom::MostVisitedTile& tile,
+    uint32_t index) {
+  return ntp_tiles::NTPTileImpression(
+      /*index=*/index,
+      /*source=*/static_cast<ntp_tiles::TileSource>(tile.source),
+      /*title_source=*/
+      static_cast<ntp_tiles::TileTitleSource>(tile.title_source),
+      /*visual_type=*/
+      ntp_tiles::TileVisualType::ICON_REAL /* unused on desktop */,
+      /*icon_type=*/favicon_base::IconType::kInvalid /* unused on desktop */,
+      /*data_generation_time=*/tile.data_generation_time,
+      /*url_for_rappor=*/GURL() /* unused */);
+}
+
+}  // namespace
+
+MostVisitedHandler::MostVisitedHandler(
+    mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandler>
+        pending_page_handler,
+    mojo::PendingRemote<most_visited::mojom::MostVisitedPage> pending_page,
+    Profile* profile,
+    content::WebContents* web_contents,
+    const GURL& ntp_url,
+    const base::Time& ntp_navigation_start_time)
+    : profile_(profile),
+      instant_service_(InstantServiceFactory::GetForProfile(profile)),
+      web_contents_(web_contents),
+      logger_(profile, ntp_url),
+      ntp_navigation_start_time_(ntp_navigation_start_time),
+      page_handler_(this, std::move(pending_page_handler)),
+      page_(std::move(pending_page)) {
+  instant_service_->AddObserver(this);
+}
+
+MostVisitedHandler::~MostVisitedHandler() {
+  instant_service_->RemoveObserver(this);
+}
+
+void MostVisitedHandler::AddMostVisitedTile(
+    const GURL& url,
+    const std::string& title,
+    AddMostVisitedTileCallback callback) {
+  bool success = instant_service_->AddCustomLink(url, title);
+  std::move(callback).Run(success);
+  logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_ADD, base::TimeDelta() /* unused */);
+}
+
+void MostVisitedHandler::DeleteMostVisitedTile(const GURL& url) {
+  if (instant_service_->IsCustomLinksEnabled()) {
+    instant_service_->DeleteCustomLink(url);
+    logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_REMOVE,
+                     base::TimeDelta() /* unused */);
+  } else {
+    instant_service_->DeleteMostVisitedItem(url);
+    last_blocklisted_ = url;
+  }
+}
+
+void MostVisitedHandler::RestoreMostVisitedDefaults() {
+  if (instant_service_->IsCustomLinksEnabled()) {
+    instant_service_->ResetCustomLinks();
+    logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_RESTORE_ALL,
+                     base::TimeDelta() /* unused */);
+  } else {
+    instant_service_->UndoAllMostVisitedDeletions();
+  }
+}
+
+void MostVisitedHandler::ReorderMostVisitedTile(const GURL& url,
+                                                uint8_t new_pos) {
+  instant_service_->ReorderCustomLink(url, new_pos);
+}
+
+void MostVisitedHandler::UndoMostVisitedTileAction() {
+  if (instant_service_->IsCustomLinksEnabled()) {
+    instant_service_->UndoCustomLinkAction();
+    logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_UNDO,
+                     base::TimeDelta() /* unused */);
+  } else if (last_blocklisted_.is_valid()) {
+    instant_service_->UndoMostVisitedDeletion(last_blocklisted_);
+    last_blocklisted_ = GURL();
+  }
+}
+
+void MostVisitedHandler::UpdateMostVisitedInfo() {
+  // OnNewTabPageOpened refreshes the most visited entries while
+  // UpdateMostVisitedInfo triggers a call to MostVisitedInfoChanged.
+  instant_service_->OnNewTabPageOpened();
+  instant_service_->UpdateMostVisitedInfo();
+}
+
+void MostVisitedHandler::UpdateMostVisitedTile(
+    const GURL& url,
+    const GURL& new_url,
+    const std::string& new_title,
+    UpdateMostVisitedTileCallback callback) {
+  bool success = instant_service_->UpdateCustomLink(
+      url, new_url != url ? new_url : GURL(), new_title);
+  std::move(callback).Run(success);
+  logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_UPDATE,
+                   base::TimeDelta() /* unused */);
+}
+
+void MostVisitedHandler::OnMostVisitedTilesRendered(
+    std::vector<most_visited::mojom::MostVisitedTilePtr> tiles,
+    double time) {
+  for (size_t i = 0; i < tiles.size(); i++) {
+    logger_.LogMostVisitedImpression(MakeNTPTileImpression(*tiles[i], i));
+  }
+  // This call flushes all most visited impression logs to UMA histograms.
+  // Therefore, it must come last.
+  logger_.LogEvent(NTP_ALL_TILES_LOADED,
+                   base::Time::FromJsTime(time) - ntp_navigation_start_time_);
+}
+
+void MostVisitedHandler::OnMostVisitedTileNavigation(
+    most_visited::mojom::MostVisitedTilePtr tile,
+    uint32_t index,
+    uint8_t mouse_button,
+    bool alt_key,
+    bool ctrl_key,
+    bool meta_key,
+    bool shift_key) {
+  logger_.LogMostVisitedNavigation(MakeNTPTileImpression(*tile, index));
+
+  if (!base::FeatureList::IsEnabled(
+          ntp_features::kNtpHandleMostVisitedNavigationExplicitly))
+    return;
+
+  WindowOpenDisposition disposition = ui::DispositionFromClick(
+      /*middle_button=*/mouse_button == 1, alt_key, ctrl_key, meta_key,
+      shift_key);
+  // Clicks on the MV tiles should be treated as if the user clicked on a
+  // bookmark. This is consistent with Android's native implementation and
+  // ensures the visit count for the MV entry is updated.
+  // Use a link transition for query tiles, e.g., repeatable queries, so that
+  // their visit count is not updated by this navigation. Otherwise duplicate
+  // query tiles could also be offered as most visited.
+  // |is_query_tile| can be true only when ntp_features::kNtpRepeatableQueries
+  // is enabled.
+  web_contents_->OpenURL(content::OpenURLParams(
+      tile->url, content::Referrer(), disposition,
+      tile->is_query_tile ? ui::PAGE_TRANSITION_LINK
+                          : ui::PAGE_TRANSITION_AUTO_BOOKMARK,
+      false));
+}
+
+void MostVisitedHandler::NtpThemeChanged(const NtpTheme& theme) {}
+
+void MostVisitedHandler::MostVisitedInfoChanged(
+    const InstantMostVisitedInfo& info) {
+  auto* template_url_service =
+      TemplateURLServiceFactory::GetForProfile(profile_);
+  auto result = most_visited::mojom::MostVisitedInfo::New();
+  std::vector<most_visited::mojom::MostVisitedTilePtr> tiles;
+  for (auto& tile : info.items) {
+    auto value = most_visited::mojom::MostVisitedTile::New();
+    if (tile.title.empty()) {
+      value->title = tile.url.spec();
+      value->title_direction = base::i18n::LEFT_TO_RIGHT;
+    } else {
+      value->title = base::UTF16ToUTF8(tile.title);
+      value->title_direction =
+          base::i18n::GetFirstStrongCharacterDirection(tile.title);
+    }
+    value->url = tile.url;
+    value->source = static_cast<int32_t>(tile.source);
+    value->title_source = static_cast<int32_t>(tile.title_source);
+    value->data_generation_time = tile.data_generation_time;
+    value->is_query_tile =
+        base::FeatureList::IsEnabled(ntp_features::kNtpRepeatableQueries) &&
+        template_url_service &&
+        template_url_service->IsSearchResultsPageFromDefaultSearchProvider(
+            tile.url);
+    tiles.push_back(std::move(value));
+  }
+  result->tiles = std::move(tiles);
+  // Update the page with the up-to-date most visited settings. The most visited
+  // settings in |info| may not reflect the current default search provider.
+  auto pair = instant_service_->GetCurrentShortcutSettings();
+  result->custom_links_enabled =
+      !pair.first && search::DefaultSearchProviderIsGoogle(profile_);
+  result->visible = pair.second;
+  page_->SetMostVisitedInfo(std::move(result));
+}
diff --git a/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h b/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h
new file mode 100644
index 0000000..caf79274
--- /dev/null
+++ b/chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h
@@ -0,0 +1,88 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_MOST_VISITED_MOST_VISITED_HANDLER_H_
+#define CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_MOST_VISITED_MOST_VISITED_HANDLER_H_
+
+#include <memory>
+
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "chrome/browser/search/instant_service_observer.h"
+#include "chrome/browser/ui/search/ntp_user_data_logger.h"
+#include "chrome/common/search/instant_types.h"
+#include "chrome/common/search/ntp_logging_events.h"
+#include "components/ntp_tiles/ntp_tile.h"
+#include "components/ntp_tiles/section_type.h"
+#include "mojo/public/cpp/bindings/receiver.h"
+#include "mojo/public/cpp/bindings/remote.h"
+#include "ui/webui/resources/cr_components/most_visited/most_visited.mojom.h"
+
+class GURL;
+class InstantService;
+class Profile;
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+// Handles bidirectional communication between MV tiles and the browser.
+class MostVisitedHandler : public most_visited::mojom::MostVisitedPageHandler,
+                           public InstantServiceObserver {
+ public:
+  MostVisitedHandler(
+      mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandler>
+          pending_page_handler,
+      mojo::PendingRemote<most_visited::mojom::MostVisitedPage> pending_page,
+      Profile* profile,
+      content::WebContents* web_contents,
+      const GURL& ntp_url,
+      const base::Time& ntp_navigation_start_time);
+  MostVisitedHandler(const MostVisitedHandler&) = delete;
+  MostVisitedHandler& operator=(const MostVisitedHandler&) = delete;
+  ~MostVisitedHandler() override;
+
+  // most_visited::mojom::MostVisitedPageHandler:
+  void AddMostVisitedTile(const GURL& url,
+                          const std::string& title,
+                          AddMostVisitedTileCallback callback) override;
+  void DeleteMostVisitedTile(const GURL& url) override;
+  void RestoreMostVisitedDefaults() override;
+  void ReorderMostVisitedTile(const GURL& url, uint8_t new_pos) override;
+  void UndoMostVisitedTileAction() override;
+  void UpdateMostVisitedInfo() override;
+  void UpdateMostVisitedTile(const GURL& url,
+                             const GURL& new_url,
+                             const std::string& new_title,
+                             UpdateMostVisitedTileCallback callback) override;
+  void OnMostVisitedTilesRendered(
+      std::vector<most_visited::mojom::MostVisitedTilePtr> tiles,
+      double time) override;
+  void OnMostVisitedTileNavigation(most_visited::mojom::MostVisitedTilePtr tile,
+                                   uint32_t index,
+                                   uint8_t mouse_button,
+                                   bool alt_key,
+                                   bool ctrl_key,
+                                   bool meta_key,
+                                   bool shift_key) override;
+
+ private:
+  // InstantServiceObserver:
+  void NtpThemeChanged(const NtpTheme& theme) override;
+  void MostVisitedInfoChanged(const InstantMostVisitedInfo& info) override;
+
+  Profile* profile_;
+  InstantService* instant_service_;
+  content::WebContents* web_contents_;
+  NTPUserDataLogger logger_;
+  base::Time ntp_navigation_start_time_;
+  GURL last_blocklisted_;
+
+  mojo::Receiver<most_visited::mojom::MostVisitedPageHandler> page_handler_;
+  mojo::Remote<most_visited::mojom::MostVisitedPage> page_;
+
+  base::WeakPtrFactory<MostVisitedHandler> weak_ptr_factory_{this};
+};
+
+#endif  // CHROME_BROWSER_UI_WEBUI_CR_COMPONENTS_MOST_VISITED_MOST_VISITED_HANDLER_H_
diff --git a/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc b/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc
index f6bce33..a96fdb35 100644
--- a/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc
+++ b/chrome/browser/ui/webui/download_shelf/download_shelf_ui.cc
@@ -158,8 +158,11 @@
 
 std::vector<DownloadUIModel*> DownloadShelfUI::GetDownloads() {
   std::vector<DownloadUIModel*> downloads;
-  for (const auto& download_entry : items_)
-    downloads.push_back(download_entry.second.get());
+  for (const auto& download_entry : items_) {
+    DownloadUIModel* download_model = download_entry.second.get();
+    if (download_model->ShouldShowInShelf())
+      downloads.push_back(download_model);
+  }
 
   return downloads;
 }
@@ -187,6 +190,12 @@
   if (page_handler_) {
     DownloadUIModel* download_model = FindDownloadById(download->GetId());
     DCHECK(download_model);
+
+    if (!download_model->ShouldShowInShelf()) {
+      page_handler_->OnDownloadErased(download->GetId());
+      return;
+    }
+
     page_handler_->OnDownloadUpdated(download_model);
   }
 
diff --git a/chrome/browser/ui/webui/download_shelf/download_shelf_ui.h b/chrome/browser/ui/webui/download_shelf/download_shelf_ui.h
index d2b26d4b..252bf0d3 100644
--- a/chrome/browser/ui/webui/download_shelf/download_shelf_ui.h
+++ b/chrome/browser/ui/webui/download_shelf/download_shelf_ui.h
@@ -57,7 +57,9 @@
 
   void OpenDownload(uint32_t download_id);
 
+  // Get the downloads that should be shown on the shelf.
   std::vector<DownloadUIModel*> GetDownloads();
+
   base::TimeTicks GetShowDownloadTime(uint32_t download_id);
 
   void RemoveDownload(uint32_t download_id);
diff --git a/chrome/browser/ui/webui/download_shelf/download_shelf_ui_unittest.cc b/chrome/browser/ui/webui/download_shelf/download_shelf_ui_unittest.cc
index e9f47932..139a35b9 100644
--- a/chrome/browser/ui/webui/download_shelf/download_shelf_ui_unittest.cc
+++ b/chrome/browser/ui/webui/download_shelf/download_shelf_ui_unittest.cc
@@ -119,6 +119,7 @@
   EXPECT_CALL(*download_item, GetId()).Times(AtLeast(1));
   DownloadUIModel::DownloadUIModelPtr download_model =
       DownloadItemModel::Wrap(download_item.get());
+  DownloadUIModel* download_model_ptr = download_model.get();
   EXPECT_CALL(*handler_, DoShowDownload(_)).Times(1);
   download_shelf_ui()->DoShowDownload(std::move(download_model),
                                       base::TimeTicks::Now());
@@ -132,7 +133,13 @@
   mock_timer_->Fire();
   download_item->NotifyObserversDownloadUpdated();
 
-  // Assert handler onDownloadRemoved called on item removal notification.
+  // Assert handler onDownloadErased called when setting download to not show
+  // on shelf.
+  EXPECT_CALL(*handler_, OnDownloadErased(_)).Times(1);
+  download_model_ptr->SetShouldShowInShelf(false);
+  download_item->NotifyObserversDownloadUpdated();
+
+  // Assert handler onDownloadErased called on item removal notification.
   EXPECT_CALL(*handler_, OnDownloadErased(_)).Times(1);
   download_item->NotifyObserversDownloadRemoved();
 
diff --git a/chrome/browser/ui/webui/new_tab_page/BUILD.gn b/chrome/browser/ui/webui/new_tab_page/BUILD.gn
index d4db0e36..f31b7c41 100644
--- a/chrome/browser/ui/webui/new_tab_page/BUILD.gn
+++ b/chrome/browser/ui/webui/new_tab_page/BUILD.gn
@@ -9,8 +9,8 @@
 
   public_deps = [
     "//chrome/browser/ui/webui/realbox:mojo_bindings",
-    "//mojo/public/mojom/base",
     "//skia/public/mojom",
+    "//ui/webui/resources/cr_components/most_visited:mojom",
     "//url/mojom:url_mojom_gurl",
   ]
 }
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 35ce8c9..da20cc8 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
@@ -5,34 +5,10 @@
 module new_tab_page.mojom;
 
 import "chrome/browser/ui/webui/realbox/realbox.mojom";
-import "mojo/public/mojom/base/string16.mojom";
-import "mojo/public/mojom/base/text_direction.mojom";
-import "mojo/public/mojom/base/time.mojom";
 import "skia/public/mojom/skcolor.mojom";
+import "ui/webui/resources/cr_components/most_visited/most_visited.mojom";
 import "url/mojom/url.mojom";
 
-struct MostVisitedTile {
-  string title;
-  mojo_base.mojom.TextDirection title_direction;
-  url.mojom.Url url;
-  bool is_query_tile;
-
-  // ======= METRICS =======
-  // Identifier of most visited entry source (e.g. top sites).
-  int32 source;
-  // Identifier of most visited entry title source (e.g. page's title tag).
-  int32 title_source;
-  // Time the most visited entry was generated (e.g. received by a suggestion
-  // server).
-  mojo_base.mojom.Time data_generation_time;
-};
-
-struct MostVisitedInfo {
-  bool custom_links_enabled;
-  bool visible;
-  array<MostVisitedTile> tiles;
-};
-
 // A collection of background images.
 struct BackgroundCollection {
   // Collection identifier.
@@ -74,13 +50,8 @@
 
 // A generic theme.
 struct Theme {
+  skia.mojom.SkColor text_color;
   skia.mojom.SkColor background_color;
-  skia.mojom.SkColor shortcut_background_color;
-  skia.mojom.SkColor shortcut_text_color;
-  // True if |shortcut_background_color| is dark.
-  bool shortcut_use_white_add_icon;
-  // True if the shortcuts titles should be wrapped in a pill.
-  bool shortcut_use_title_pill;
   // True if the theme is default.
   bool is_default;
   // True if the theme is dark (e.g. NTP background color is dark).
@@ -97,6 +68,9 @@
   // URL associated with the background image. Used for href.
   url.mojom.Url? background_image_attribution_url;
 
+  // Theme settings for the NTP MV tiles.
+  most_visited.mojom.MostVisitedTheme most_visited;
+
   // Theme settings for the NTP realbox. Matching those of the Omnibox.
   realbox.mojom.SearchBoxTheme search_box;
 };
@@ -238,14 +212,6 @@
 
 // Browser-side handler for requests from WebUI page.
 interface PageHandler {
-  // Adds tile.
-  AddMostVisitedTile(url.mojom.Url url, string title) => (bool success);
-  // Deletes tile by |url|.
-  DeleteMostVisitedTile(url.mojom.Url url);
-  // Moves tile identified by url to a new position at index |new_pos|.
-  ReorderMostVisitedTile(url.mojom.Url url, uint8 new_pos);
-  // Replaces the custom and most-visited tiles with the default tile set.
-  RestoreMostVisitedDefaults();
   // Sets the background image and notifies all NTPs of the change.
   SetBackgroundImage(string attribution_1, string attribution_2,
       url.mojom.Url attribution_url, url.mojom.Url image_url);
@@ -254,17 +220,11 @@
   SetDailyRefreshCollectionId(string collection_id);
   // Clears the background and daily refresh settings.
   SetNoBackgroundImage();
-  // Set the visibility and whether custom links are enabled.
-  SetMostVisitedSettings(bool customLinksEnabled, bool visible);
-  // Undoes the last action done to the tiles (add, delete, reorder, restore or
-  // update). Note that only the last action can be undone.
-  UndoMostVisitedTileAction();
-  // Called to update the tiles.
-  UpdateMostVisitedInfo();
-  // Updates a tile by url.
-  UpdateMostVisitedTile(url.mojom.Url url, url.mojom.Url new_url,
-                        string new_title)
-      => (bool success);
+  // Sets the visibility of NTP tiles and whether custom links are enabled.
+  SetMostVisitedSettings(bool custom_links_enabled, bool shortcuts_visible);
+  // Returns the visibility of NTP tiles and whether custom links are enabled.
+  GetMostVisitedSettings() => (bool custom_links_enabled,
+                               bool shortcuts_visible);
   // Returns the collections of background images.
   GetBackgroundCollections() => (array<BackgroundCollection> collections);
   // Returns the images of a collection identified by |collection_id|.
@@ -289,26 +249,12 @@
   OnModulesLoadedWithData();
 
   // ======= METRICS =======
-  // Logs that |tiles| were displayed / updated at |time|. The first instance of
-  // this event is used as a proxy for when the NTP has finished loading.
-  OnMostVisitedTilesRendered(array<MostVisitedTile> tiles, double time);
   // Logs that the One Google Bar was added to the DOM / loaded in an iframe at
   // |time|.
   OnOneGoogleBarRendered(double time);
   // Logs that the promo iframe was loaded at |time| and pings |log_url| for
   // promo metrics logging.
   OnPromoRendered(double time, url.mojom.Url? log_url);
-  // Logs that |tile| at position |index| was triggered to navigate to that
-  // most visited entry.
-  // |mouse_button| indicates which mouse button was pressed on the entry. See
-  // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
-  OnMostVisitedTileNavigation(MostVisitedTile tile,
-                        uint32 index,
-                        uint8 mouse_button,
-                        bool alt_key,
-                        bool ctrl_key,
-                        bool meta_key,
-                        bool shift_key);
   // Logs an action performed in the customize dialog.
   OnCustomizeDialogAction(CustomizeDialogAction action);
   // Logs that the |type| image of an image doodle has been clicked. If set, the
@@ -337,9 +283,6 @@
 
 // WebUI-side handler for requests from the browser.
 interface Page {
-  // Updates the page with most-visited info which includes whether the
-  // tiles should be shown, if links can be customized and the tiles.
-  SetMostVisitedInfo(MostVisitedInfo info);
   // Sets the current theme.
   SetTheme(Theme theme);
   // Disables the modules in |ids|. If |all|, disables all modules and passes an
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 553401a..9fca6ac8 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
@@ -57,13 +57,15 @@
 
 new_tab_page::mojom::ThemePtr MakeTheme(const NtpTheme& ntp_theme) {
   auto theme = new_tab_page::mojom::Theme::New();
+  auto most_visited = most_visited::mojom::MostVisitedTheme::New();
   theme->is_default = ntp_theme.using_default_theme;
   theme->background_color = ntp_theme.background_color;
-  theme->shortcut_background_color = ntp_theme.shortcut_color;
-  theme->shortcut_text_color = ntp_theme.text_color;
-  theme->shortcut_use_white_add_icon =
-      color_utils::IsDark(ntp_theme.shortcut_color);
-  theme->shortcut_use_title_pill = false;
+  most_visited->background_color = ntp_theme.shortcut_color;
+  most_visited->use_white_tile_icon =
+      color_utils::IsDark(most_visited->background_color);
+  most_visited->is_dark = !color_utils::IsDark(ntp_theme.text_color);
+  most_visited->use_title_pill = false;
+  theme->text_color = ntp_theme.text_color;
   theme->is_dark = !color_utils::IsDark(ntp_theme.text_color);
   if (ntp_theme.logo_alternate) {
     theme->logo_color = ntp_theme.logo_color;
@@ -72,7 +74,7 @@
   if (!ntp_theme.custom_background_url.is_empty()) {
     background_image->url = ntp_theme.custom_background_url;
   } else if (ntp_theme.has_theme_image) {
-    theme->shortcut_use_title_pill = true;
+    most_visited->use_title_pill = true;
     background_image->url =
         GURL(base::StrCat({"chrome-untrusted://theme/IDR_THEME_NTP_BACKGROUND?",
                            ntp_theme.theme_id}));
@@ -152,6 +154,8 @@
     theme->daily_refresh_collection_id = ntp_theme.collection_id;
   }
 
+  theme->most_visited = std::move(most_visited);
+
   auto search_box = realbox::mojom::SearchBoxTheme::New();
   search_box->bg = ntp_theme.search_box.bg;
   search_box->icon = ntp_theme.search_box.icon;
@@ -173,21 +177,6 @@
   return theme;
 }
 
-ntp_tiles::NTPTileImpression MakeNTPTileImpression(
-    const new_tab_page::mojom::MostVisitedTile& tile,
-    uint32_t index) {
-  return ntp_tiles::NTPTileImpression(
-      /*index=*/index,
-      /*source=*/static_cast<ntp_tiles::TileSource>(tile.source),
-      /*title_source=*/
-      static_cast<ntp_tiles::TileTitleSource>(tile.title_source),
-      /*visual_type=*/
-      ntp_tiles::TileVisualType::ICON_REAL /* unused on desktop */,
-      /*icon_type=*/favicon_base::IconType::kInvalid /* unused on desktop */,
-      /*data_generation_time=*/tile.data_generation_time,
-      /*url_for_rappor=*/GURL() /* unused */);
-}
-
 SkColor ParseHexColor(const std::string& color) {
   SkColor result;
   if (color.size() == 7 && color[0] == '#' &&
@@ -364,41 +353,6 @@
   registry->RegisterListPref(prefs::kNtpDisabledModules, true);
 }
 
-void NewTabPageHandler::AddMostVisitedTile(
-    const GURL& url,
-    const std::string& title,
-    AddMostVisitedTileCallback callback) {
-  bool success = instant_service_->AddCustomLink(url, title);
-  std::move(callback).Run(success);
-  logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_ADD, base::TimeDelta() /* unused */);
-}
-
-void NewTabPageHandler::DeleteMostVisitedTile(const GURL& url) {
-  if (instant_service_->IsCustomLinksEnabled()) {
-    instant_service_->DeleteCustomLink(url);
-    logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_REMOVE,
-                     base::TimeDelta() /* unused */);
-  } else {
-    instant_service_->DeleteMostVisitedItem(url);
-    last_blocklisted_ = url;
-  }
-}
-
-void NewTabPageHandler::RestoreMostVisitedDefaults() {
-  if (instant_service_->IsCustomLinksEnabled()) {
-    instant_service_->ResetCustomLinks();
-    logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_RESTORE_ALL,
-                     base::TimeDelta() /* unused */);
-  } else {
-    instant_service_->UndoAllMostVisitedDeletions();
-  }
-}
-
-void NewTabPageHandler::ReorderMostVisitedTile(const GURL& url,
-                                               uint8_t new_pos) {
-  instant_service_->ReorderCustomLink(url, new_pos);
-}
-
 void NewTabPageHandler::SetMostVisitedSettings(bool custom_links_enabled,
                                                bool visible) {
   auto pair = instant_service_->GetCurrentShortcutSettings();
@@ -423,15 +377,13 @@
   }
 }
 
-void NewTabPageHandler::UndoMostVisitedTileAction() {
-  if (instant_service_->IsCustomLinksEnabled()) {
-    instant_service_->UndoCustomLinkAction();
-    logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_UNDO,
-                     base::TimeDelta() /* unused */);
-  } else if (last_blocklisted_.is_valid()) {
-    instant_service_->UndoMostVisitedDeletion(last_blocklisted_);
-    last_blocklisted_ = GURL();
-  }
+void NewTabPageHandler::GetMostVisitedSettings(
+    GetMostVisitedSettingsCallback callback) {
+  auto pair = instant_service_->GetCurrentShortcutSettings();
+  // The first of the pair is true if most-visited tiles are being used.
+  bool custom_links_enabled = !pair.first;
+  bool visible = pair.second;
+  std::move(callback).Run(custom_links_enabled, visible);
 }
 
 void NewTabPageHandler::SetBackgroundImage(const std::string& attribution_1,
@@ -463,25 +415,6 @@
   LogEvent(NTP_BACKGROUND_IMAGE_RESET);
 }
 
-void NewTabPageHandler::UpdateMostVisitedInfo() {
-  // OnNewTabPageOpened refreshes the most visited entries while
-  // UpdateMostVisitedInfo triggers a call to MostVisitedInfoChanged.
-  instant_service_->OnNewTabPageOpened();
-  instant_service_->UpdateMostVisitedInfo();
-}
-
-void NewTabPageHandler::UpdateMostVisitedTile(
-    const GURL& url,
-    const GURL& new_url,
-    const std::string& new_title,
-    UpdateMostVisitedTileCallback callback) {
-  bool success = instant_service_->UpdateCustomLink(
-      url, new_url != url ? new_url : GURL(), new_title);
-  std::move(callback).Run(success);
-  logger_.LogEvent(NTP_CUSTOMIZE_SHORTCUT_UPDATE,
-                   base::TimeDelta() /* unused */);
-}
-
 void NewTabPageHandler::GetBackgroundCollections(
     GetBackgroundCollectionsCallback callback) {
   if (!ntp_background_service_ || background_collections_callback_) {
@@ -690,18 +623,6 @@
                    base::Time::FromJsTime(time) - ntp_navigation_start_time_);
 }
 
-void NewTabPageHandler::OnMostVisitedTilesRendered(
-    std::vector<new_tab_page::mojom::MostVisitedTilePtr> tiles,
-    double time) {
-  for (size_t i = 0; i < tiles.size(); i++) {
-    logger_.LogMostVisitedImpression(MakeNTPTileImpression(*tiles[i], i));
-  }
-  // This call flushes all most visited impression logs to UMA histograms.
-  // Therefore, it must come last.
-  logger_.LogEvent(NTP_ALL_TILES_LOADED,
-                   base::Time::FromJsTime(time) - ntp_navigation_start_time_);
-}
-
 void NewTabPageHandler::OnOneGoogleBarRendered(double time) {
   logger_.LogEvent(NTP_ONE_GOOGLE_BAR_SHOWN,
                    base::Time::FromJsTime(time) - ntp_navigation_start_time_);
@@ -716,38 +637,6 @@
   }
 }
 
-void NewTabPageHandler::OnMostVisitedTileNavigation(
-    new_tab_page::mojom::MostVisitedTilePtr tile,
-    uint32_t index,
-    uint8_t mouse_button,
-    bool alt_key,
-    bool ctrl_key,
-    bool meta_key,
-    bool shift_key) {
-  logger_.LogMostVisitedNavigation(MakeNTPTileImpression(*tile, index));
-
-  if (!base::FeatureList::IsEnabled(
-          ntp_features::kNtpHandleMostVisitedNavigationExplicitly))
-    return;
-
-  WindowOpenDisposition disposition = ui::DispositionFromClick(
-      /*middle_button=*/mouse_button == 1, alt_key, ctrl_key, meta_key,
-      shift_key);
-  // Clicks on the MV tiles should be treated as if the user clicked on a
-  // bookmark. This is consistent with Android's native implementation and
-  // ensures the visit count for the MV entry is updated.
-  // Use a link transition for query tiles, e.g., repeatable queries, so that
-  // their visit count is not updated by this navigation. Otherwise duplicate
-  // query tiles could also be offered as most visited.
-  // |is_query_tile| can be true only when ntp_features::kNtpRepeatableQueries
-  // is enabled.
-  web_contents_->OpenURL(content::OpenURLParams(
-      tile->url, content::Referrer(), disposition,
-      tile->is_query_tile ? ui::PAGE_TRANSITION_LINK
-                          : ui::PAGE_TRANSITION_AUTO_BOOKMARK,
-      false));
-}
-
 void NewTabPageHandler::OnCustomizeDialogAction(
     new_tab_page::mojom::CustomizeDialogAction action) {
   NTPLoggingEventType event;
@@ -890,37 +779,7 @@
 }
 
 void NewTabPageHandler::MostVisitedInfoChanged(
-    const InstantMostVisitedInfo& info) {
-  auto* template_url_service =
-      TemplateURLServiceFactory::GetForProfile(profile_);
-  std::vector<new_tab_page::mojom::MostVisitedTilePtr> list;
-  auto result = new_tab_page::mojom::MostVisitedInfo::New();
-  for (auto& tile : info.items) {
-    auto value = new_tab_page::mojom::MostVisitedTile::New();
-    if (tile.title.empty()) {
-      value->title = tile.url.spec();
-      value->title_direction = base::i18n::LEFT_TO_RIGHT;
-    } else {
-      value->title = base::UTF16ToUTF8(tile.title);
-      value->title_direction =
-          base::i18n::GetFirstStrongCharacterDirection(tile.title);
-    }
-    value->url = tile.url;
-    value->source = static_cast<int32_t>(tile.source);
-    value->title_source = static_cast<int32_t>(tile.title_source);
-    value->data_generation_time = tile.data_generation_time;
-    value->is_query_tile =
-        base::FeatureList::IsEnabled(ntp_features::kNtpRepeatableQueries) &&
-        template_url_service &&
-        template_url_service->IsSearchResultsPageFromDefaultSearchProvider(
-            tile.url);
-    list.push_back(std::move(value));
-  }
-  result->custom_links_enabled = !info.use_most_visited;
-  result->tiles = std::move(list);
-  result->visible = info.is_visible;
-  page_->SetMostVisitedInfo(std::move(result));
-}
+    const InstantMostVisitedInfo& info) {}
 
 void NewTabPageHandler::OnCollectionInfoAvailable() {
   if (!background_collections_callback_) {
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
index f6b6c89..22667f3e 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h
@@ -65,19 +65,8 @@
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
   // new_tab_page::mojom::PageHandler:
-  void AddMostVisitedTile(const GURL& url,
-                          const std::string& title,
-                          AddMostVisitedTileCallback callback) override;
-  void DeleteMostVisitedTile(const GURL& url) override;
-  void RestoreMostVisitedDefaults() override;
-  void ReorderMostVisitedTile(const GURL& url, uint8_t new_pos) override;
   void SetMostVisitedSettings(bool custom_links_enabled, bool visible) override;
-  void UndoMostVisitedTileAction() override;
-  void UpdateMostVisitedInfo() override;
-  void UpdateMostVisitedTile(const GURL& url,
-                             const GURL& new_url,
-                             const std::string& new_title,
-                             UpdateMostVisitedTileCallback callback) override;
+  void GetMostVisitedSettings(GetMostVisitedSettingsCallback callback) override;
   void SetBackgroundImage(const std::string& attribution_1,
                           const std::string& attribution_2,
                           const GURL& attribution_url,
@@ -99,19 +88,9 @@
   void UpdateDisabledModules() override;
   void OnModulesLoadedWithData() override;
   void OnAppRendered(double time) override;
-  void OnMostVisitedTilesRendered(
-      std::vector<new_tab_page::mojom::MostVisitedTilePtr> tiles,
-      double time) override;
   void OnOneGoogleBarRendered(double time) override;
   void OnPromoRendered(double time,
                        const absl::optional<GURL>& log_url) override;
-  void OnMostVisitedTileNavigation(new_tab_page::mojom::MostVisitedTilePtr tile,
-                                   uint32_t index,
-                                   uint8_t mouse_button,
-                                   bool alt_key,
-                                   bool ctrl_key,
-                                   bool meta_key,
-                                   bool shift_key) override;
   void OnCustomizeDialogAction(
       new_tab_page::mojom::CustomizeDialogAction action) override;
   void OnDoodleImageClicked(new_tab_page::mojom::DoodleImageType type,
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
index b1b06e6a..9b23647 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc
@@ -40,8 +40,6 @@
     return receiver_.BindNewPipeAndPassRemote();
   }
 
-  MOCK_METHOD1(SetMostVisitedInfo,
-               void(new_tab_page::mojom::MostVisitedInfoPtr));
   MOCK_METHOD1(SetTheme, void(new_tab_page::mojom::ThemePtr));
   MOCK_METHOD2(SetDisabledModules, void(bool, const std::vector<std::string>&));
 
@@ -84,12 +82,6 @@
   InstantServiceObserver* instant_service_observer_;
 };
 
-TEST_F(NewTabPageHandlerTest, SetMostVisitedInfo) {
-  EXPECT_CALL(mock_page_, SetMostVisitedInfo(testing::_));
-  InstantMostVisitedInfo info;
-  instant_service_observer_->MostVisitedInfoChanged(info);
-}
-
 TEST_F(NewTabPageHandlerTest, SetTheme) {
   EXPECT_CALL(mock_page_, SetTheme(testing::_));
   NtpTheme theme;
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 267c9f6a..93ef644 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
@@ -19,6 +19,7 @@
 #include "chrome/browser/search/task_module/task_module_handler.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/ui/webui/cr_components/most_visited/most_visited_handler.h"
 #include "chrome/browser/ui/webui/customize_themes/chrome_customize_themes_handler.h"
 #include "chrome/browser/ui/webui/favicon_source.h"
 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_handler.h"
@@ -316,6 +317,7 @@
       content::WebContentsObserver(web_ui->GetWebContents()),
       page_factory_receiver_(this),
       customize_themes_factory_receiver_(this),
+      most_visited_page_factory_receiver_(this),
       profile_(Profile::FromWebUI(web_ui)),
       instant_service_(InstantServiceFactory::GetForProfile(profile_)),
       web_contents_(web_ui->GetWebContents()),
@@ -398,6 +400,15 @@
 }
 
 void NewTabPageUI::BindInterface(
+    mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandlerFactory>
+        pending_receiver) {
+  if (most_visited_page_factory_receiver_.is_bound()) {
+    most_visited_page_factory_receiver_.reset();
+  }
+  most_visited_page_factory_receiver_.Bind(std::move(pending_receiver));
+}
+
+void NewTabPageUI::BindInterface(
     mojo::PendingReceiver<task_module::mojom::TaskModuleHandler>
         pending_receiver) {
   task_module_handler_ = std::make_unique<TaskModuleHandler>(
@@ -445,6 +456,17 @@
       profile_);
 }
 
+void NewTabPageUI::CreatePageHandler(
+    mojo::PendingRemote<most_visited::mojom::MostVisitedPage> pending_page,
+    mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandler>
+        pending_page_handler) {
+  DCHECK(pending_page.is_valid());
+  most_visited_page_handler_ = std::make_unique<MostVisitedHandler>(
+      std::move(pending_page_handler), std::move(pending_page), profile_,
+      web_contents_, GURL(chrome::kChromeUINewTabPageThirdPartyURL),
+      navigation_start_time_);
+}
+
 void NewTabPageUI::NtpThemeChanged(const NtpTheme& theme) {
   // Load time data is cached across page reloads. Update the background color
   // here to prevent a white flicker on page reload.
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
index 2a56e26..8d71068 100644
--- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
+++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -23,6 +23,7 @@
 #include "ui/base/resource/scale_factor.h"
 #include "ui/webui/mojo_web_ui_controller.h"
 #include "ui/webui/resources/cr_components/customize_themes/customize_themes.mojom.h"
+#include "ui/webui/resources/cr_components/most_visited/most_visited.mojom.h"
 
 namespace base {
 class RefCountedMemory;
@@ -40,6 +41,7 @@
 #endif
 class GURL;
 class InstantService;
+class MostVisitedHandler;
 class NewTabPageHandler;
 class PrefRegistrySimple;
 class Profile;
@@ -53,6 +55,7 @@
     : public ui::MojoWebUIController,
       public new_tab_page::mojom::PageHandlerFactory,
       public customize_themes::mojom::CustomizeThemesHandlerFactory,
+      public most_visited::mojom::MostVisitedPageHandlerFactory,
       public InstantServiceObserver,
       content::WebContentsObserver {
  public:
@@ -88,6 +91,13 @@
                          pending_receiver);
 
   // Instantiates the implementor of the
+  // most_visited::mojom::MostVisitedPageHandlerFactory mojo interface passing
+  // the pending receiver that will be internally bound.
+  void BindInterface(
+      mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandlerFactory>
+          pending_receiver);
+
+  // Instantiates the implementor of the
   // shopping_tasks::mojom::ShoppingTasksHandler mojo interface passing the
   // pending receiver that will be internally bound.
   void BindInterface(
@@ -128,6 +138,12 @@
       mojo::PendingReceiver<customize_themes::mojom::CustomizeThemesHandler>
           pending_handler) override;
 
+  // most_visited::mojom::MostVisitedPageHandlerFactory:
+  void CreatePageHandler(
+      mojo::PendingRemote<most_visited::mojom::MostVisitedPage> pending_page,
+      mojo::PendingReceiver<most_visited::mojom::MostVisitedPageHandler>
+          pending_page_handler) override;
+
   // InstantServiceObserver:
   void NtpThemeChanged(const NtpTheme& theme) override;
   void MostVisitedInfoChanged(const InstantMostVisitedInfo& info) override;
@@ -147,6 +163,9 @@
   std::unique_ptr<ChromeCustomizeThemesHandler> customize_themes_handler_;
   mojo::Receiver<customize_themes::mojom::CustomizeThemesHandlerFactory>
       customize_themes_factory_receiver_;
+  std::unique_ptr<MostVisitedHandler> most_visited_page_handler_;
+  mojo::Receiver<most_visited::mojom::MostVisitedPageHandlerFactory>
+      most_visited_page_factory_receiver_;
   std::unique_ptr<PromoBrowserCommandHandler> promo_browser_command_handler_;
   std::unique_ptr<RealboxHandler> realbox_handler_;
 #if !defined(OFFICIAL_BUILD)
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
index 34f389f..a7a30ba 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom
@@ -60,7 +60,8 @@
   kSecurityAndSignIn = 304,
   kFingerprint = 305,
   kManageOtherPeople = 306,
-  kKerberosAccounts = 307,
+  // Note: Value 307 was for Kerberos Accounts Subpage, which has been migrated
+  // to a separate section - see https://crbug.com/1186190. Do not reuse.
 
   // Device section.
   kPointers = 400,
@@ -174,7 +175,6 @@
 const string kSecurityAndSignInSubpagePath = "lockScreen";
 const string kFingerprintSubpagePath = "lockScreen/fingerprint";
 const string kManageOtherPeopleSubpagePath = "accounts";
-const string kKerberosAccountsSubpagePath = "kerberosAccounts";
 
 // Device section.
 const string kDeviceSectionPath = "device";
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc b/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
index b387dc7..fc264836 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc
@@ -46,7 +46,6 @@
       chromeos::settings::mojom::kSecurityAndSignInSubpagePath,
       chromeos::settings::mojom::kFingerprintSubpagePath,
       chromeos::settings::mojom::kManageOtherPeopleSubpagePath,
-      chromeos::settings::mojom::kKerberosAccountsSubpagePath,
 
       // Device section.
       chromeos::settings::mojom::kDeviceSectionPath,
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
index 666ed61..9520c72 100644
--- a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
+++ b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
@@ -79,9 +79,10 @@
   kRestrictSignIn = 307,
   kAddToUserAllowlist = 308,
   kRemoveFromUserAllowlist = 309,
-  kAddKerberosTicket = 310,
-  kRemoveKerberosTicket = 311,
-  kSetActiveKerberosTicket = 312,
+  // Note: Values 310, 311 and 312 were for kAddKerberosTicket,
+  // kRemoveKerberosTicket and kSetActiveKerberosTicket, respectively. These
+  // settings have been moved to a separate Kerberos section - see
+  // https://crbug.com/1186190. Do not reuse.
   kAddFingerprint = 313,
   kRemoveFingerprint = 314,
   kSetUpParentalControls = 315,
diff --git a/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc b/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc
index 1679131..9c6d969 100644
--- a/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc
@@ -65,7 +65,7 @@
 
   auto people_section = std::make_unique<PeopleSection>(
       profile, search_tag_registry, sync_service, supervised_user_service,
-      kerberos_credentials_manager, identity_manager, profile->GetPrefs());
+      identity_manager, profile->GetPrefs());
   sections_map_[mojom::Section::kPeople] = people_section.get();
   sections_.push_back(std::move(people_section));
 
diff --git a/chrome/browser/ui/webui/settings/chromeos/people_section.cc b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
index e01d41e..f1f2629 100644
--- a/chrome/browser/ui/webui/settings/chromeos/people_section.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/people_section.cc
@@ -28,7 +28,6 @@
 #include "chrome/browser/ui/webui/chromeos/sync/os_sync_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/account_manager_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.h"
-#include "chrome/browser/ui/webui/settings/chromeos/kerberos_accounts_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_features_util.h"
 #include "chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.h"
 #include "chrome/browser/ui/webui/settings/chromeos/quick_unlock_handler.h"
@@ -264,36 +263,6 @@
   return *tags;
 }
 
-const std::vector<SearchConcept>& GetKerberosSearchConcepts() {
-  static const base::NoDestructor<std::vector<SearchConcept>> tags({
-      {IDS_OS_SETTINGS_TAG_KERBEROS_ADD,
-       mojom::kKerberosAccountsSubpagePath,
-       mojom::SearchResultIcon::kAvatar,
-       mojom::SearchResultDefaultRank::kMedium,
-       mojom::SearchResultType::kSetting,
-       {.setting = mojom::Setting::kAddKerberosTicket}},
-      {IDS_OS_SETTINGS_TAG_KERBEROS_REMOVE,
-       mojom::kKerberosAccountsSubpagePath,
-       mojom::SearchResultIcon::kAvatar,
-       mojom::SearchResultDefaultRank::kMedium,
-       mojom::SearchResultType::kSetting,
-       {.setting = mojom::Setting::kRemoveKerberosTicket}},
-      {IDS_OS_SETTINGS_TAG_KERBEROS,
-       mojom::kKerberosAccountsSubpagePath,
-       mojom::SearchResultIcon::kAvatar,
-       mojom::SearchResultDefaultRank::kMedium,
-       mojom::SearchResultType::kSubpage,
-       {.subpage = mojom::Subpage::kKerberosAccounts}},
-      {IDS_OS_SETTINGS_TAG_KERBEROS_ACTIVE,
-       mojom::kKerberosAccountsSubpagePath,
-       mojom::SearchResultIcon::kAvatar,
-       mojom::SearchResultDefaultRank::kMedium,
-       mojom::SearchResultType::kSetting,
-       {.setting = mojom::Setting::kSetActiveKerberosTicket}},
-  });
-  return *tags;
-}
-
 const std::vector<SearchConcept>& GetFingerprintSearchConcepts() {
   static const base::NoDestructor<std::vector<SearchConcept>> tags({
       {IDS_OS_SETTINGS_TAG_FINGERPRINT_ADD,
@@ -732,13 +701,11 @@
     SearchTagRegistry* search_tag_registry,
     syncer::SyncService* sync_service,
     SupervisedUserService* supervised_user_service,
-    KerberosCredentialsManager* kerberos_credentials_manager,
     signin::IdentityManager* identity_manager,
     PrefService* pref_service)
     : OsSettingsSection(profile, search_tag_registry),
       sync_service_(sync_service),
       supervised_user_service_(supervised_user_service),
-      kerberos_credentials_manager_(kerberos_credentials_manager),
       identity_manager_(identity_manager),
       pref_service_(pref_service) {
   // No search tags are registered if in guest mode.
@@ -763,15 +730,6 @@
     FetchAccounts();
   }
 
-  // No Kerberos search tags are registered here if Kerberos settings are in a
-  // separate section.
-  if (kerberos_credentials_manager_ &&
-      !chromeos::features::IsKerberosSettingsSectionEnabled()) {
-    // Kerberos search tags are added/removed dynamically.
-    kerberos_credentials_manager_->AddObserver(this);
-    OnKerberosEnabledStateChanged();
-  }
-
   if (chromeos::features::IsSplitSettingsSyncEnabled()) {
     if (sync_service_) {
       updater.AddSearchTags(GetSplitSyncSearchConcepts());
@@ -805,11 +763,6 @@
 }
 
 PeopleSection::~PeopleSection() {
-  if (kerberos_credentials_manager_ &&
-      !chromeos::features::IsKerberosSettingsSectionEnabled()) {
-    kerberos_credentials_manager_->RemoveObserver(this);
-  }
-
   if (chromeos::features::IsSplitSettingsSyncEnabled() && sync_service_)
     sync_service_->RemoveObserver(this);
 }
@@ -901,8 +854,6 @@
       base::FeatureList::IsEnabled(omnibox::kDocumentProvider));
 
   AddAccountManagerPageStrings(html_source, profile());
-  KerberosAccountsHandler::AddLoadTimeKerberosStrings(
-      html_source, kerberos_credentials_manager_);
   AddLockScreenPageStrings(html_source, profile()->GetPrefs());
   AddFingerprintListStrings(html_source);
   AddFingerprintResources(html_source, AreFingerprintSettingsAllowed());
@@ -948,18 +899,6 @@
         std::make_unique<chromeos::settings::ParentalControlsHandler>(
             profile()));
   }
-
-  // No Kerberos handler is created/added here if Kerberos settings are in a
-  // separate section.
-  if (!chromeos::features::IsKerberosSettingsSectionEnabled()) {
-    std::unique_ptr<chromeos::settings::KerberosAccountsHandler>
-        kerberos_accounts_handler =
-            KerberosAccountsHandler::CreateIfKerberosEnabled(profile());
-    if (kerberos_accounts_handler) {
-      // Note that the UI is enabled only if Kerberos is enabled.
-      web_ui->AddMessageHandler(std::move(kerberos_accounts_handler));
-    }
-  }
 }
 
 int PeopleSection::GetSectionNameMessageId() const {
@@ -1075,20 +1014,6 @@
   };
   RegisterNestedSettingBulk(mojom::Subpage::kManageOtherPeople,
                             kManageOtherPeopleSettings, generator);
-
-  // Kerberos.
-  generator->RegisterTopLevelSubpage(IDS_SETTINGS_KERBEROS_ACCOUNTS_PAGE_TITLE,
-                                     mojom::Subpage::kKerberosAccounts,
-                                     mojom::SearchResultIcon::kAvatar,
-                                     mojom::SearchResultDefaultRank::kMedium,
-                                     mojom::kKerberosAccountsSubpagePath);
-  static constexpr mojom::Setting kKerberosAccountsSettings[] = {
-      mojom::Setting::kAddKerberosTicket,
-      mojom::Setting::kRemoveKerberosTicket,
-      mojom::Setting::kSetActiveKerberosTicket,
-  };
-  RegisterNestedSettingBulk(mojom::Subpage::kKerberosAccounts,
-                            kKerberosAccountsSettings, generator);
 }
 
 void PeopleSection::FetchAccounts() {
@@ -1144,15 +1069,6 @@
   }
 }
 
-void PeopleSection::OnKerberosEnabledStateChanged() {
-  SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
-
-  if (kerberos_credentials_manager_->IsKerberosEnabled())
-    updater.AddSearchTags(GetKerberosSearchConcepts());
-  else
-    updater.RemoveSearchTags(GetKerberosSearchConcepts());
-}
-
 bool PeopleSection::AreFingerprintSettingsAllowed() {
   return chromeos::quick_unlock::IsFingerprintEnabled(profile());
 }
diff --git a/chrome/browser/ui/webui/settings/chromeos/people_section.h b/chrome/browser/ui/webui/settings/chromeos/people_section.h
index cddb87a..1fd2d03 100644
--- a/chrome/browser/ui/webui/settings/chromeos/people_section.h
+++ b/chrome/browser/ui/webui/settings/chromeos/people_section.h
@@ -9,7 +9,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "base/values.h"
-#include "chrome/browser/ash/kerberos/kerberos_credentials_manager.h"
 #include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h"
 #include "components/account_manager_core/account.h"
 #include "components/account_manager_core/account_manager_facade.h"
@@ -41,19 +40,17 @@
 // Provides UI strings and search tags for People settings. Search tags are only
 // added for non-guest sessions.
 //
-// Kerberos, Fingerprint, and Parental Controls search tags are only shown if
-// they are allowed by policy/flags. Different sets of Sync tags are shown
-// depending on whether the feature is enabed or disabled.
+// Fingerprint and Parental Controls search tags are only shown if they are
+// allowed by policy/flags. Different sets of Sync tags are shown depending on
+// whether the feature is enabed or disabled.
 class PeopleSection : public OsSettingsSection,
                       public account_manager::AccountManagerFacade::Observer,
-                      public syncer::SyncServiceObserver,
-                      public KerberosCredentialsManager::Observer {
+                      public syncer::SyncServiceObserver {
  public:
   PeopleSection(Profile* profile,
                 SearchTagRegistry* search_tag_registry,
                 syncer::SyncService* sync_service,
                 SupervisedUserService* supervised_user_service,
-                KerberosCredentialsManager* kerberos_credentials_manager,
                 signin::IdentityManager* identity_manager,
                 PrefService* pref_service);
   ~PeopleSection() override;
@@ -76,9 +73,6 @@
   // syncer::SyncServiceObserver:
   void OnStateChanged(syncer::SyncService* sync_service) override;
 
-  // KerberosCredentialsManager::Observer:
-  void OnKerberosEnabledStateChanged() override;
-
   bool AreFingerprintSettingsAllowed();
   void FetchAccounts();
   void UpdateAccountManagerSearchTags(
@@ -89,7 +83,6 @@
   account_manager::AccountManagerFacade* account_manager_facade_ = nullptr;
   syncer::SyncService* sync_service_;
   SupervisedUserService* supervised_user_service_;
-  KerberosCredentialsManager* kerberos_credentials_manager_;
   signin::IdentityManager* identity_manager_;
   PrefService* pref_service_;
   PrefChangeRegistrar fingerprint_pref_change_registrar_;
diff --git a/chrome/browser/web_applications/BUILD.gn b/chrome/browser/web_applications/BUILD.gn
index c8dd970..1dba26ec 100644
--- a/chrome/browser/web_applications/BUILD.gn
+++ b/chrome/browser/web_applications/BUILD.gn
@@ -106,6 +106,7 @@
     "//services/metrics/public/cpp:ukm_builders",
     "//skia",
     "//ui/base/idle",
+    "//ui/events/devices:devices",
   ]
 
   if (is_chromeos_ash) {
@@ -355,6 +356,7 @@
     "//extensions/browser:test_support",
     "//ui/base/idle:idle",
     "//ui/base/idle:test_support",
+    "//ui/events/devices:test_support",
   ]
 
   if (is_chromeos_ash) {
diff --git a/chrome/browser/web_applications/app_service/BUILD.gn b/chrome/browser/web_applications/app_service/BUILD.gn
index 7eb2427..b9be0b9 100644
--- a/chrome/browser/web_applications/app_service/BUILD.gn
+++ b/chrome/browser/web_applications/app_service/BUILD.gn
@@ -25,6 +25,7 @@
     "//chrome/common",
     "//components/content_settings/core/browser",
     "//components/services/app_service/public/cpp:publisher",
+    "//components/sessions",
     "//components/webapps/browser",
     "//url",
   ]
@@ -42,7 +43,6 @@
       "//components/arc:arc_base",
       "//components/full_restore",
       "//components/services/app_service/public/cpp:instance_update",
-      "//components/sessions:session_id",
     ]
   }
 
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
index a348d61..c2fc108 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.cc
@@ -8,27 +8,40 @@
 #include "base/callback_helpers.h"
 #include "base/containers/contains.h"
 #include "base/feature_list.h"
+#include "base/metrics/histogram_macros.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/intent_util.h"
+#include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/web_applications/web_app_launch_manager.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
+#include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_helpers.h"
+#include "chrome/browser/web_applications/components/web_app_ui_manager.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/common/chrome_features.h"
+#include "chrome/common/extensions/extension_constants.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/services/app_service/public/cpp/publisher_base.h"
 #include "content/public/browser/clear_site_data_utils.h"
+#include "third_party/blink/public/mojom/manifest/capture_links.mojom.h"
+#include "ui/base/window_open_disposition.h"
+#include "ui/display/types/display_constants.h"
 #include "url/origin.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chrome/browser/ash/arc/arc_web_contents_data.h"
 #include "chrome/browser/chromeos/extensions/gfx_utils.h"
+#include "components/full_restore/app_launch_info.h"
+#include "components/full_restore/full_restore_utils.h"
+#include "components/sessions/core/session_id.h"
 #endif
 
 using apps::IconEffects;
@@ -73,7 +86,9 @@
     : profile_(profile),
       app_type_(app_type),
       delegate_(delegate),
-      provider_(WebAppProvider::Get(profile)) {}
+      provider_(WebAppProvider::Get(profile)) {
+  web_app_launch_manager_ = std::make_unique<WebAppLaunchManager>(profile_);
+}
 
 WebAppPublisherHelper::~WebAppPublisherHelper() = default;
 
@@ -370,6 +385,138 @@
   std::move(callback).Run(apps::mojom::IconValue::New());
 }
 
+content::WebContents* WebAppPublisherHelper::Launch(
+    const std::string& app_id,
+    int32_t event_flags,
+    apps::mojom::LaunchSource launch_source,
+    apps::mojom::WindowInfoPtr window_info) {
+  if (!profile_) {
+    return nullptr;
+  }
+
+  const WebApp* web_app = GetWebApp(app_id);
+  if (!web_app) {
+    return nullptr;
+  }
+
+  switch (launch_source) {
+    case apps::mojom::LaunchSource::kUnknown:
+    case apps::mojom::LaunchSource::kFromParentalControls:
+      break;
+    case apps::mojom::LaunchSource::kFromAppListGrid:
+    case apps::mojom::LaunchSource::kFromAppListGridContextMenu:
+      UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunch",
+                                extension_misc::APP_LAUNCH_APP_LIST_MAIN,
+                                extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
+
+      break;
+    case apps::mojom::LaunchSource::kFromAppListQuery:
+    case apps::mojom::LaunchSource::kFromAppListQueryContextMenu:
+      UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunch",
+                                extension_misc::APP_LAUNCH_APP_LIST_SEARCH,
+                                extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
+      break;
+    case apps::mojom::LaunchSource::kFromAppListRecommendation:
+    case apps::mojom::LaunchSource::kFromShelf:
+    case apps::mojom::LaunchSource::kFromFileManager:
+    case apps::mojom::LaunchSource::kFromLink:
+    case apps::mojom::LaunchSource::kFromOmnibox:
+    case apps::mojom::LaunchSource::kFromChromeInternal:
+    case apps::mojom::LaunchSource::kFromKeyboard:
+    case apps::mojom::LaunchSource::kFromOtherApp:
+    case apps::mojom::LaunchSource::kFromMenu:
+    case apps::mojom::LaunchSource::kFromInstalledNotification:
+    case apps::mojom::LaunchSource::kFromTest:
+    case apps::mojom::LaunchSource::kFromArc:
+    case apps::mojom::LaunchSource::kFromSharesheet:
+    case apps::mojom::LaunchSource::kFromReleaseNotesNotification:
+    case apps::mojom::LaunchSource::kFromFullRestore:
+    case apps::mojom::LaunchSource::kFromSmartTextContextMenu:
+    case apps::mojom::LaunchSource::kFromDiscoverTabNotification:
+      break;
+  }
+
+  DisplayMode display_mode = registrar().GetAppEffectiveDisplayMode(app_id);
+
+  apps::AppLaunchParams params = apps::CreateAppIdLaunchParamsWithEventFlags(
+      web_app->app_id(), event_flags, apps::GetAppLaunchSource(launch_source),
+      window_info ? window_info->display_id : display::kInvalidDisplayId,
+      /*fallback_container=*/
+      ConvertDisplayModeToAppLaunchContainer(display_mode));
+
+  // The app will be launched for the currently active profile.
+  return LaunchAppWithParams(std::move(params));
+}
+
+content::WebContents* WebAppPublisherHelper::LaunchAppWithFiles(
+    const std::string& app_id,
+    apps::mojom::LaunchContainer container,
+    int32_t event_flags,
+    apps::mojom::LaunchSource launch_source,
+    apps::mojom::FilePathsPtr file_paths) {
+  apps::AppLaunchParams params(
+      app_id, container, ui::DispositionFromEventFlags(event_flags),
+      apps::GetAppLaunchSource(launch_source), display::kDefaultDisplayId);
+  for (const auto& file_path : file_paths->file_paths) {
+    params.launch_files.push_back(file_path);
+  }
+
+  // The app will be launched for the currently active profile.
+  return LaunchAppWithParams(std::move(params));
+}
+
+content::WebContents* WebAppPublisherHelper::LaunchAppWithIntent(
+    const std::string& app_id,
+    int32_t event_flags,
+    apps::mojom::IntentPtr intent,
+    apps::mojom::LaunchSource launch_source,
+    apps::mojom::WindowInfoPtr window_info) {
+  content::WebContents* web_contents = LaunchAppWithIntentImpl(
+      app_id, event_flags, std::move(intent), launch_source,
+      window_info ? window_info->display_id : display::kInvalidDisplayId);
+
+// TODO(crbug.com/1214763): Set ArcWebContentsData for Lacros.
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  if (launch_source == apps::mojom::LaunchSource::kFromArc && web_contents) {
+    // Add a flag to remember this tab originated in the ARC context.
+    web_contents->SetUserData(&arc::ArcWebContentsData::kArcTransitionFlag,
+                              std::make_unique<arc::ArcWebContentsData>());
+  }
+#endif
+
+  return web_contents;
+}
+
+content::WebContents* WebAppPublisherHelper::LaunchAppWithParams(
+    apps::AppLaunchParams params) {
+  apps::AppLaunchParams params_for_restore(
+      params.app_id, params.container, params.disposition, params.source,
+      params.display_id, params.launch_files, params.intent);
+
+  content::WebContents* const web_contents =
+      web_app_launch_manager_->OpenApplication(std::move(params));
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+  // Save all launch information for system web apps, because the browser
+  // session restore can't restore system web apps.
+  const WebApp* web_app = GetWebApp(params_for_restore.app_id);
+  const bool is_system_web_app = web_app && web_app->IsSystemApp();
+  int session_id = apps::GetSessionIdForRestoreFromWebContents(web_contents);
+  if (is_system_web_app && SessionID::IsValidValue(session_id)) {
+    std::unique_ptr<full_restore::AppLaunchInfo> launch_info =
+        std::make_unique<full_restore::AppLaunchInfo>(
+            params_for_restore.app_id, session_id, params_for_restore.container,
+            params_for_restore.disposition, params_for_restore.display_id,
+            std::move(params_for_restore.launch_files),
+            std::move(params_for_restore.intent));
+    full_restore::SaveAppLaunchInfo(profile()->GetPath(),
+                                    std::move(launch_info));
+  }
+#endif
+
+  return web_contents;
+}
+
 WebAppRegistrar& WebAppPublisherHelper::registrar() const {
   return *provider_->registrar().AsWebAppRegistrar();
 }
@@ -378,4 +525,33 @@
   return registrar().GetAppById(app_id);
 }
 
+content::WebContents* WebAppPublisherHelper::LaunchAppWithIntentImpl(
+    const std::string& app_id,
+    int32_t event_flags,
+    apps::mojom::IntentPtr intent,
+    apps::mojom::LaunchSource launch_source,
+    int64_t display_id) {
+  if (!profile_) {
+    return nullptr;
+  }
+
+  if (registrar().GetAppById(app_id)->capture_links() ==
+      blink::mojom::CaptureLinks::kExistingClientNavigate) {
+    content::WebContents* web_contents =
+        provider_->ui_manager().NavigateExistingWindow(
+            app_id, intent->url ? intent->url.value()
+                                : registrar().GetAppLaunchUrl(app_id));
+    if (web_contents) {
+      return web_contents;
+    }
+  }
+
+  auto params = apps::CreateAppLaunchParamsForIntent(
+      app_id, event_flags, apps::GetAppLaunchSource(launch_source), display_id,
+      ConvertDisplayModeToAppLaunchContainer(
+          registrar().GetAppEffectiveDisplayMode(app_id)),
+      std::move(intent));
+  return LaunchAppWithParams(std::move(params));
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
index 8e7cfa17..2f36f90 100644
--- a/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
+++ b/chrome/browser/web_applications/app_service/web_app_publisher_helper.h
@@ -5,10 +5,12 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_WEB_APP_PUBLISHER_HELPER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_APP_SERVICE_WEB_APP_PUBLISHER_HELPER_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
 #include "chrome/browser/apps/app_service/app_icon_factory.h"
+#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/icon_key_util.h"
 #include "chrome/browser/apps/app_service/paused_apps.h"
 #include "components/content_settings/core/common/content_settings_types.h"
@@ -17,11 +19,16 @@
 
 class Profile;
 
+namespace content {
+class WebContents;
+}
+
 namespace web_app {
 
 class WebApp;
 class WebAppProvider;
 class WebAppRegistrar;
+class WebAppLaunchManager;
 
 class WebAppPublisherHelper {
  public:
@@ -106,6 +113,27 @@
                 bool allow_placeholder_icon,
                 LoadIconCallback callback);
 
+  content::WebContents* Launch(const std::string& app_id,
+                               int32_t event_flags,
+                               apps::mojom::LaunchSource launch_source,
+                               apps::mojom::WindowInfoPtr window_info);
+
+  content::WebContents* LaunchAppWithFiles(
+      const std::string& app_id,
+      apps::mojom::LaunchContainer container,
+      int32_t event_flags,
+      apps::mojom::LaunchSource launch_source,
+      apps::mojom::FilePathsPtr file_paths);
+
+  content::WebContents* LaunchAppWithIntent(
+      const std::string& app_id,
+      int32_t event_flags,
+      apps::mojom::IntentPtr intent,
+      apps::mojom::LaunchSource launch_source,
+      apps::mojom::WindowInfoPtr window_info);
+
+  content::WebContents* LaunchAppWithParams(apps::AppLaunchParams params);
+
   Profile* profile() { return profile_; }
 
   apps::mojom::AppType app_type() const { return app_type_; }
@@ -118,6 +146,13 @@
 
   const WebApp* GetWebApp(const AppId& app_id) const;
 
+  content::WebContents* LaunchAppWithIntentImpl(
+      const std::string& app_id,
+      int32_t event_flags,
+      apps::mojom::IntentPtr intent,
+      apps::mojom::LaunchSource launch_source,
+      int64_t display_id);
+
   Profile* const profile_;
 
   // The app type of the publisher. The app type is kSystemWeb if the web apps
@@ -128,6 +163,8 @@
 
   WebAppProvider* const provider_;
 
+  std::unique_ptr<WebAppLaunchManager> web_app_launch_manager_;
+
   apps_util::IncrementingIconKeyFactory icon_key_factory_;
 
   apps::PausedApps paused_apps_;
diff --git a/chrome/browser/web_applications/app_service/web_apps_base.cc b/chrome/browser/web_applications/app_service/web_apps_base.cc
index a8ef02c..e7e6ee8 100644
--- a/chrome/browser/web_applications/app_service/web_apps_base.cc
+++ b/chrome/browser/web_applications/app_service/web_apps_base.cc
@@ -9,7 +9,6 @@
 
 #include "base/callback.h"
 #include "base/feature_list.h"
-#include "base/metrics/histogram_macros.h"
 #include "build/chromeos_buildflags.h"
 #include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/intent_util.h"
@@ -18,7 +17,6 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/web_applications/web_app_dialog_manager.h"
-#include "chrome/browser/ui/web_applications/web_app_launch_manager.h"
 #include "chrome/browser/ui/web_applications/web_app_ui_manager_impl.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
@@ -29,7 +27,6 @@
 #include "chrome/browser/web_applications/web_app_provider.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/common/chrome_features.h"
-#include "chrome/common/extensions/extension_constants.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
@@ -102,52 +99,6 @@
   Publish(publisher_helper().ConvertUninstalledWebApp(web_app), subscribers_);
 }
 
-IconEffects WebAppsBase::GetIconEffects(const WebApp* web_app) {
-  IconEffects icon_effects = IconEffects::kNone;
-  if (!web_app->is_locally_installed()) {
-    icon_effects =
-        static_cast<IconEffects>(icon_effects | IconEffects::kBlocked);
-  }
-  icon_effects =
-      static_cast<IconEffects>(icon_effects | IconEffects::kRoundCorners);
-  return icon_effects;
-}
-
-content::WebContents* WebAppsBase::LaunchAppWithIntentImpl(
-    const std::string& app_id,
-    int32_t event_flags,
-    apps::mojom::IntentPtr intent,
-    apps::mojom::LaunchSource launch_source,
-    int64_t display_id) {
-  if (!profile_) {
-    return nullptr;
-  }
-
-  const WebAppRegistrar& registrar = *WebAppsBase::GetRegistrar();
-  if (registrar.GetAppById(app_id)->capture_links() ==
-      blink::mojom::CaptureLinks::kExistingClientNavigate) {
-    content::WebContents* web_contents =
-        provider()->ui_manager().NavigateExistingWindow(
-            app_id, intent->url ? intent->url.value()
-                                : registrar.GetAppLaunchUrl(app_id));
-    if (web_contents) {
-      return web_contents;
-    }
-  }
-
-  auto params = apps::CreateAppLaunchParamsForIntent(
-      app_id, event_flags, apps::GetAppLaunchSource(launch_source), display_id,
-      ConvertDisplayModeToAppLaunchContainer(
-          registrar.GetAppEffectiveDisplayMode(app_id)),
-      std::move(intent));
-  return LaunchAppWithParams(std::move(params));
-}
-
-content::WebContents* WebAppsBase::LaunchAppWithParams(
-    apps::AppLaunchParams params) {
-  return web_app_launch_manager_->OpenApplication(std::move(params));
-}
-
 void WebAppsBase::Initialize(
     const mojo::Remote<apps::mojom::AppService>& app_service) {
   DCHECK(profile_);
@@ -162,8 +113,6 @@
   content_settings_observation_.Observe(
       HostContentSettingsMapFactory::GetForProfile(profile_));
 
-  web_app_launch_manager_ = std::make_unique<WebAppLaunchManager>(profile_);
-
   PublisherBase::Initialize(app_service, app_type_);
   app_service_ = app_service.get();
 }
@@ -198,62 +147,8 @@
                          int32_t event_flags,
                          apps::mojom::LaunchSource launch_source,
                          apps::mojom::WindowInfoPtr window_info) {
-  if (!profile_) {
-    return;
-  }
-
-  const WebApp* web_app = GetWebApp(app_id);
-  if (!web_app) {
-    return;
-  }
-
-  switch (launch_source) {
-    case apps::mojom::LaunchSource::kUnknown:
-    case apps::mojom::LaunchSource::kFromParentalControls:
-      break;
-    case apps::mojom::LaunchSource::kFromAppListGrid:
-    case apps::mojom::LaunchSource::kFromAppListGridContextMenu:
-      UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunch",
-                                extension_misc::APP_LAUNCH_APP_LIST_MAIN,
-                                extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
-
-      break;
-    case apps::mojom::LaunchSource::kFromAppListQuery:
-    case apps::mojom::LaunchSource::kFromAppListQueryContextMenu:
-      UMA_HISTOGRAM_ENUMERATION("Extensions.AppLaunch",
-                                extension_misc::APP_LAUNCH_APP_LIST_SEARCH,
-                                extension_misc::APP_LAUNCH_BUCKET_BOUNDARY);
-      break;
-    case apps::mojom::LaunchSource::kFromAppListRecommendation:
-    case apps::mojom::LaunchSource::kFromShelf:
-    case apps::mojom::LaunchSource::kFromFileManager:
-    case apps::mojom::LaunchSource::kFromLink:
-    case apps::mojom::LaunchSource::kFromOmnibox:
-    case apps::mojom::LaunchSource::kFromChromeInternal:
-    case apps::mojom::LaunchSource::kFromKeyboard:
-    case apps::mojom::LaunchSource::kFromOtherApp:
-    case apps::mojom::LaunchSource::kFromMenu:
-    case apps::mojom::LaunchSource::kFromInstalledNotification:
-    case apps::mojom::LaunchSource::kFromTest:
-    case apps::mojom::LaunchSource::kFromArc:
-    case apps::mojom::LaunchSource::kFromSharesheet:
-    case apps::mojom::LaunchSource::kFromReleaseNotesNotification:
-    case apps::mojom::LaunchSource::kFromFullRestore:
-    case apps::mojom::LaunchSource::kFromSmartTextContextMenu:
-    case apps::mojom::LaunchSource::kFromDiscoverTabNotification:
-      break;
-  }
-
-  DisplayMode display_mode = GetRegistrar()->GetAppEffectiveDisplayMode(app_id);
-
-  apps::AppLaunchParams params = apps::CreateAppIdLaunchParamsWithEventFlags(
-      web_app->app_id(), event_flags, apps::GetAppLaunchSource(launch_source),
-      window_info ? window_info->display_id : display::kInvalidDisplayId,
-      /*fallback_container=*/
-      ConvertDisplayModeToAppLaunchContainer(display_mode));
-
-  // The app will be launched for the currently active profile.
-  LaunchAppWithParams(std::move(params));
+  publisher_helper().Launch(app_id, event_flags, std::move(launch_source),
+                            std::move(window_info));
 }
 
 void WebAppsBase::LaunchAppWithFiles(const std::string& app_id,
@@ -261,15 +156,9 @@
                                      int32_t event_flags,
                                      apps::mojom::LaunchSource launch_source,
                                      apps::mojom::FilePathsPtr file_paths) {
-  apps::AppLaunchParams params(
-      app_id, container, ui::DispositionFromEventFlags(event_flags),
-      apps::GetAppLaunchSource(launch_source), display::kDefaultDisplayId);
-  for (const auto& file_path : file_paths->file_paths) {
-    params.launch_files.push_back(file_path);
-  }
-
-  // The app will be launched for the currently active profile.
-  LaunchAppWithParams(std::move(params));
+  publisher_helper().LaunchAppWithFiles(app_id, std::move(container),
+                                        event_flags, std::move(launch_source),
+                                        std::move(file_paths));
 }
 
 void WebAppsBase::LaunchAppWithIntent(const std::string& app_id,
@@ -277,9 +166,9 @@
                                       apps::mojom::IntentPtr intent,
                                       apps::mojom::LaunchSource launch_source,
                                       apps::mojom::WindowInfoPtr window_info) {
-  LaunchAppWithIntentImpl(
-      app_id, event_flags, std::move(intent), launch_source,
-      window_info ? window_info->display_id : display::kInvalidDisplayId);
+  publisher_helper().LaunchAppWithIntent(app_id, event_flags, std::move(intent),
+                                         std::move(launch_source),
+                                         std::move(window_info));
 }
 
 void WebAppsBase::SetPermission(const std::string& app_id,
diff --git a/chrome/browser/web_applications/app_service/web_apps_base.h b/chrome/browser/web_applications/app_service/web_apps_base.h
index ee08b55..f40f3da 100644
--- a/chrome/browser/web_applications/app_service/web_apps_base.h
+++ b/chrome/browser/web_applications/app_service/web_apps_base.h
@@ -26,14 +26,6 @@
 
 class Profile;
 
-namespace apps {
-struct AppLaunchParams;
-}  // namespace apps
-
-namespace content {
-class WebContents;
-}
-
 namespace webapps {
 enum class WebappUninstallSource;
 }  // namespace webapps
@@ -41,7 +33,6 @@
 namespace web_app {
 
 class WebApp;
-class WebAppLaunchManager;
 class WebAppProvider;
 class WebAppRegistrar;
 
@@ -70,18 +61,6 @@
       const std::string& app_id,
       const base::Time& last_launch_time) override;
 
-  apps::IconEffects GetIconEffects(const WebApp* web_app);
-
-  content::WebContents* LaunchAppWithIntentImpl(
-      const std::string& app_id,
-      int32_t event_flags,
-      apps::mojom::IntentPtr intent,
-      apps::mojom::LaunchSource launch_source,
-      int64_t display_id);
-
-  virtual content::WebContents* LaunchAppWithParams(
-      apps::AppLaunchParams params);
-
   const mojo::RemoteSet<apps::mojom::Subscriber>& subscribers() const {
     return subscribers_;
   }
@@ -162,8 +141,6 @@
 
   WebAppProvider* provider_ = nullptr;
 
-  std::unique_ptr<WebAppLaunchManager> web_app_launch_manager_;
-
   // app_service_ is owned by the object that owns this object.
   apps::mojom::AppService* app_service_;
 
diff --git a/chrome/browser/web_applications/app_service/web_apps_chromeos.cc b/chrome/browser/web_applications/app_service/web_apps_chromeos.cc
index 3cae3689..974db3b 100644
--- a/chrome/browser/web_applications/app_service/web_apps_chromeos.cc
+++ b/chrome/browser/web_applications/app_service/web_apps_chromeos.cc
@@ -17,8 +17,9 @@
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/apps/app_service/app_launch_params.h"
 #include "chrome/browser/apps/app_service/app_service_metrics.h"
+#include "chrome/browser/apps/app_service/app_service_proxy.h"
+#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
 #include "chrome/browser/apps/app_service/launch_utils.h"
 #include "chrome/browser/apps/app_service/menu_util.h"
 #include "chrome/browser/ash/arc/arc_util.h"
@@ -31,7 +32,6 @@
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/web_applications/web_app_dialog_manager.h"
-#include "chrome/browser/ui/web_applications/web_app_launch_manager.h"
 #include "chrome/browser/ui/web_applications/web_app_ui_manager_impl.h"
 #include "chrome/browser/web_applications/components/app_icon_manager.h"
 #include "chrome/browser/web_applications/components/install_finalizer.h"
@@ -51,11 +51,8 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
-#include "components/full_restore/app_launch_info.h"
-#include "components/full_restore/full_restore_utils.h"
 #include "components/services/app_service/public/cpp/intent_filter_util.h"
 #include "components/services/app_service/public/mojom/types.mojom-shared.h"
-#include "components/sessions/core/session_id.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "content/public/browser/clear_site_data_utils.h"
 #include "content/public/browser/web_contents.h"
@@ -149,25 +146,6 @@
   }
 }
 
-void WebAppsChromeOs::LaunchAppWithIntent(
-    const std::string& app_id,
-    int32_t event_flags,
-    apps::mojom::IntentPtr intent,
-    apps::mojom::LaunchSource launch_source,
-    apps::mojom::WindowInfoPtr window_info) {
-  auto* tab = WebAppsChromeOs::LaunchAppWithIntentImpl(
-      app_id, event_flags, std::move(intent), launch_source,
-      window_info ? window_info->display_id : display::kInvalidDisplayId);
-
-  if (launch_source != apps::mojom::LaunchSource::kFromArc || !tab) {
-    return;
-  }
-
-  // Add a flag to remember this tab originated in the ARC context.
-  tab->SetUserData(&arc::ArcWebContentsData::kArcTransitionFlag,
-                   std::make_unique<arc::ArcWebContentsData>());
-}
-
 void WebAppsChromeOs::Uninstall(const std::string& app_id,
                                 apps::mojom::UninstallSource uninstall_source,
                                 bool clear_site_data,
@@ -193,22 +171,31 @@
                                    apps::mojom::MenuType menu_type,
                                    int64_t display_id,
                                    GetMenuModelCallback callback) {
-  const WebApp* web_app = GetWebApp(app_id);
-  if (!web_app) {
-    std::move(callback).Run(apps::mojom::MenuItems::New());
-    return;
-  }
+  bool is_system_web_app = false;
+  bool can_use_uninstall = true;
+  apps::mojom::WindowMode display_mode;
+  apps::AppServiceProxyFactory::GetForProfile(profile())
+      ->AppRegistryCache()
+      .ForOneApp(app_id, [&is_system_web_app, &can_use_uninstall,
+                          &display_mode](const apps::AppUpdate& update) {
+        if (update.InstallSource() == apps::mojom::InstallSource::kSystem) {
+          is_system_web_app = true;
+        }
+        if (update.InstallSource() == apps::mojom::InstallSource::kSystem ||
+            update.InstallSource() == apps::mojom::InstallSource::kPolicy) {
+          can_use_uninstall = false;
+        }
+        display_mode = update.WindowMode();
+      });
 
-  const bool is_system_web_app = web_app->IsSystemApp();
   apps::mojom::MenuItemsPtr menu_items = apps::mojom::MenuItems::New();
 
   if (!is_system_web_app) {
-    apps::CreateOpenNewSubmenu(
-        menu_type,
-        web_app->user_display_mode() == DisplayMode::kStandalone
-            ? IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW
-            : IDS_APP_LIST_CONTEXT_MENU_NEW_TAB,
-        &menu_items);
+    apps::CreateOpenNewSubmenu(menu_type,
+                               display_mode == apps::mojom::WindowMode::kWindow
+                                   ? IDS_APP_LIST_CONTEXT_MENU_NEW_WINDOW
+                                   : IDS_APP_LIST_CONTEXT_MENU_NEW_TAB,
+                               &menu_items);
   }
 
   if (menu_type == apps::mojom::MenuType::kShelf &&
@@ -217,7 +204,7 @@
                          &menu_items);
   }
 
-  if (provider()->install_finalizer().CanUserUninstallWebApp(app_id)) {
+  if (can_use_uninstall) {
     apps::AddCommandItem(ash::UNINSTALL, IDS_APP_LIST_UNINSTALL_ITEM,
                          &menu_items);
   }
@@ -227,6 +214,21 @@
                          &menu_items);
   }
 
+  GetMenuModelFromWebAppProvider(app_id, menu_type, std::move(menu_items),
+                                 std::move(callback));
+}
+
+void WebAppsChromeOs::GetMenuModelFromWebAppProvider(
+    const std::string& app_id,
+    apps::mojom::MenuType menu_type,
+    apps::mojom::MenuItemsPtr menu_items,
+    GetMenuModelCallback callback) {
+  const WebApp* web_app = GetWebApp(app_id);
+  if (!web_app) {
+    std::move(callback).Run(apps::mojom::MenuItems::New());
+    return;
+  }
+
   // Read shortcuts menu item icons from disk, if any.
   if (base::FeatureList::IsEnabled(
           features::kDesktopPWAsAppIconShortcutsMenuUI) &&
@@ -338,7 +340,7 @@
         web_app->shortcuts_menu_item_infos()[menu_item_index].url;
   }
 
-  LaunchAppWithParams(std::move(params));
+  publisher_helper().LaunchAppWithParams(std::move(params));
 }
 
 void WebAppsChromeOs::OnWebAppInstalled(const AppId& app_id) {
@@ -671,36 +673,6 @@
   }
 }
 
-content::WebContents* WebAppsChromeOs::LaunchAppWithParams(
-    apps::AppLaunchParams params) {
-  apps::AppLaunchParams params_for_restore(
-      params.app_id, params.container, params.disposition, params.source,
-      params.display_id, params.launch_files, params.intent);
-
-  auto* web_contents = WebAppsBase::LaunchAppWithParams(std::move(params));
-
-  int session_id = apps::GetSessionIdForRestoreFromWebContents(web_contents);
-  if (!SessionID::IsValidValue(session_id)) {
-    return web_contents;
-  }
-
-  const WebApp* web_app = GetWebApp(params_for_restore.app_id);
-  std::unique_ptr<full_restore::AppLaunchInfo> launch_info;
-  if (web_app && web_app->IsSystemApp()) {
-    // Save all launch information for system web apps, because the browser
-    // session restore can't restore system web apps.
-    launch_info = std::make_unique<full_restore::AppLaunchInfo>(
-        params_for_restore.app_id, session_id, params_for_restore.container,
-        params_for_restore.disposition, params_for_restore.display_id,
-        std::move(params_for_restore.launch_files),
-        std::move(params_for_restore.intent));
-    full_restore::SaveAppLaunchInfo(profile()->GetPath(),
-                                    std::move(launch_info));
-  }
-
-  return web_contents;
-}
-
 bool WebAppsChromeOs::Accepts(const std::string& app_id) {
   // Crostini Terminal System App is handled by Crostini Apps.
   return app_id != crostini::kCrostiniTerminalSystemAppId;
diff --git a/chrome/browser/web_applications/app_service/web_apps_chromeos.h b/chrome/browser/web_applications/app_service/web_apps_chromeos.h
index e0e95e2..0b5cdce9 100644
--- a/chrome/browser/web_applications/app_service/web_apps_chromeos.h
+++ b/chrome/browser/web_applications/app_service/web_apps_chromeos.h
@@ -73,11 +73,6 @@
   void Initialize();
 
   // apps::mojom::Publisher overrides.
-  void LaunchAppWithIntent(const std::string& app_id,
-                           int32_t event_flags,
-                           apps::mojom::IntentPtr intent,
-                           apps::mojom::LaunchSource launch_source,
-                           apps::mojom::WindowInfoPtr window_info) override;
   void Uninstall(const std::string& app_id,
                  apps::mojom::UninstallSource uninstall_source,
                  bool clear_site_data,
@@ -88,6 +83,10 @@
                     apps::mojom::MenuType menu_type,
                     int64_t display_id,
                     GetMenuModelCallback callback) override;
+  void GetMenuModelFromWebAppProvider(const std::string& app_id,
+                                      apps::mojom::MenuType menu_type,
+                                      apps::mojom::MenuItemsPtr menu_items,
+                                      GetMenuModelCallback callback);
   // menu_type is stored as |shortcut_id|.
   void ExecuteContextMenuCommand(const std::string& app_id,
                                  int command_id,
@@ -155,11 +154,6 @@
   // remove the Chrome app badge.
   void ApplyChromeBadge(const std::string& arc_package_name);
 
-  // Launches an app in a way specified by |params|. If the app is a system web
-  // app, or not opened in tabs, saves the launch parameters.
-  content::WebContents* LaunchAppWithParams(
-      apps::AppLaunchParams params) override;
-
   bool Accepts(const std::string& app_id) override;
 
   // Returns whether the app should show a badge.
diff --git a/chrome/browser/web_applications/app_service/web_apps_publisher_host.cc b/chrome/browser/web_applications/app_service/web_apps_publisher_host.cc
index 5c40381..12e0e95 100644
--- a/chrome/browser/web_applications/app_service/web_apps_publisher_host.cc
+++ b/chrome/browser/web_applications/app_service/web_apps_publisher_host.cc
@@ -120,6 +120,37 @@
                               std::move(callback));
 }
 
+content::WebContents* WebAppsPublisherHost::Launch(
+    const std::string& app_id,
+    int32_t event_flags,
+    apps::mojom::LaunchSource launch_source,
+    apps::mojom::WindowInfoPtr window_info) {
+  return publisher_helper().Launch(
+      app_id, event_flags, std::move(launch_source), std::move(window_info));
+}
+
+content::WebContents* WebAppsPublisherHost::LaunchAppWithFiles(
+    const std::string& app_id,
+    apps::mojom::LaunchContainer container,
+    int32_t event_flags,
+    apps::mojom::LaunchSource launch_source,
+    apps::mojom::FilePathsPtr file_paths) {
+  return publisher_helper().LaunchAppWithFiles(
+      app_id, std::move(container), event_flags, std::move(launch_source),
+      std::move(file_paths));
+}
+
+content::WebContents* WebAppsPublisherHost::LaunchAppWithIntent(
+    const std::string& app_id,
+    int32_t event_flags,
+    apps::mojom::IntentPtr intent,
+    apps::mojom::LaunchSource launch_source,
+    apps::mojom::WindowInfoPtr window_info) {
+  return publisher_helper().LaunchAppWithIntent(
+      app_id, event_flags, std::move(intent), std::move(launch_source),
+      std::move(window_info));
+}
+
 void WebAppsPublisherHost::OnWebAppInstalled(const AppId& app_id) {
   const WebApp* web_app = GetWebApp(app_id);
   if (!web_app) {
diff --git a/chrome/browser/web_applications/app_service/web_apps_publisher_host.h b/chrome/browser/web_applications/app_service/web_apps_publisher_host.h
index 87f0264..15ca98c 100644
--- a/chrome/browser/web_applications/app_service/web_apps_publisher_host.h
+++ b/chrome/browser/web_applications/app_service/web_apps_publisher_host.h
@@ -74,6 +74,22 @@
                 int32_t size_hint_in_dip,
                 bool allow_placeholder_icon,
                 LoadIconCallback callback);
+  content::WebContents* Launch(const std::string& app_id,
+                               int32_t event_flags,
+                               apps::mojom::LaunchSource launch_source,
+                               apps::mojom::WindowInfoPtr window_info);
+  content::WebContents* LaunchAppWithFiles(
+      const std::string& app_id,
+      apps::mojom::LaunchContainer container,
+      int32_t event_flags,
+      apps::mojom::LaunchSource launch_source,
+      apps::mojom::FilePathsPtr file_paths);
+  content::WebContents* LaunchAppWithIntent(
+      const std::string& app_id,
+      int32_t event_flags,
+      apps::mojom::IntentPtr intent,
+      apps::mojom::LaunchSource launch_source,
+      apps::mojom::WindowInfoPtr window_info);
 
  private:
   void OnReady();
diff --git a/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc b/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
index 611309b..2261367a 100644
--- a/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
+++ b/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
@@ -45,6 +45,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
+#include "content/public/test/test_navigation_observer.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
 #include "url/gurl.h"
 
@@ -362,6 +363,24 @@
       apps::mojom::OptionalBool::kFalse);
 }
 
+IN_PROC_BROWSER_TEST_F(WebAppsPublisherHostBrowserTest, Launch) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const GURL app_url = embedded_test_server()->GetURL("/web_apps/basic.html");
+  AppId app_id = InstallWebAppFromManifest(browser(), app_url);
+
+  MockAppPublisher mock_app_publisher;
+  WebAppsPublisherHost web_apps_publisher_host(profile());
+  web_apps_publisher_host.SetPublisherForTesting(&mock_app_publisher);
+  web_apps_publisher_host.Init();
+  mock_app_publisher.Wait();
+
+  content::TestNavigationObserver navigation_observer(app_url);
+  navigation_observer.StartWatchingNewWebContents();
+  web_apps_publisher_host.Launch(app_id, 0,
+                                 apps::mojom::LaunchSource::kFromTest, nullptr);
+  navigation_observer.Wait();
+}
+
 IN_PROC_BROWSER_TEST_F(WebAppsPublisherHostBrowserTest, PauseUnpause) {
   ASSERT_TRUE(embedded_test_server()->Start());
   const GURL app_url = embedded_test_server()->GetURL("/web_apps/basic.html");
diff --git a/chrome/browser/web_applications/components/BUILD.gn b/chrome/browser/web_applications/components/BUILD.gn
index cd56b58..a6610ce1 100644
--- a/chrome/browser/web_applications/components/BUILD.gn
+++ b/chrome/browser/web_applications/components/BUILD.gn
@@ -107,11 +107,11 @@
     "web_app_icon_downloader.h",
   ]
 
-  if (is_chromeos_ash) {
+  if (is_chromeos_ash || is_chromeos_lacros) {
     sources += [ "web_app_shortcut_chromeos.cc" ]
   }
 
-  if (is_linux || is_chromeos_lacros) {
+  if (is_linux) {
     # Desktop linux, doesn't count ChromeOS.
     sources += [
       "web_app_file_handler_registration_linux.cc",
@@ -252,7 +252,7 @@
     ]
   }
 
-  if (is_linux || is_chromeos_lacros) {
+  if (is_linux) {
     # Desktop linux, doesn't count ChromeOS.
     sources += [
       "web_app_file_handler_registration_linux_unittest.cc",
@@ -314,7 +314,7 @@
     "web_app_url_loader_browsertest.cc",
   ]
 
-  if (is_linux || is_chromeos_lacros) {
+  if (is_linux) {
     sources += [ "web_app_file_handler_registration_linux_browsertest.cc" ]
   }
 
diff --git a/chrome/browser/web_applications/components/external_install_options.cc b/chrome/browser/web_applications/components/external_install_options.cc
index 17d56a99..328a4373 100644
--- a/chrome/browser/web_applications/components/external_install_options.cc
+++ b/chrome/browser/web_applications/components/external_install_options.cc
@@ -76,7 +76,8 @@
         options.only_use_app_info_factory,
         options.app_info_factory,
         options.system_app_type,
-        options.oem_installed
+        options.oem_installed,
+        options.disable_if_touchscreen_with_stylus_not_supported
         // clang-format on
     );
   };
@@ -166,7 +167,9 @@
          << (install_options.system_app_type.has_value()
                  ? static_cast<int32_t>(install_options.system_app_type.value())
                  : -1)
-         << "\n oem_installed: " << install_options.oem_installed;
+         << "\n oem_installed: " << install_options.oem_installed
+         << "\n disable_if_touchscreen_with_stylus_not_supported: "
+         << install_options.disable_if_touchscreen_with_stylus_not_supported;
 }
 
 InstallManager::InstallParams ConvertExternalInstallOptionsToParams(
diff --git a/chrome/browser/web_applications/components/external_install_options.h b/chrome/browser/web_applications/components/external_install_options.h
index 6115dfd..ec956054 100644
--- a/chrome/browser/web_applications/components/external_install_options.h
+++ b/chrome/browser/web_applications/components/external_install_options.h
@@ -179,6 +179,10 @@
   // Whether the app was installed by an OEM and should be placed in a special
   // OEM folder in the app launcher. Only used on Chrome OS.
   bool oem_installed = false;
+
+  // Whether this should be installed on devices without a touch screen with
+  // stylus support.
+  bool disable_if_touchscreen_with_stylus_not_supported = false;
 };
 
 std::ostream& operator<<(std::ostream& out,
diff --git a/chrome/browser/web_applications/components/web_app_file_handler_registration.cc b/chrome/browser/web_applications/components/web_app_file_handler_registration.cc
index 89e37a4..29ac8396 100644
--- a/chrome/browser/web_applications/components/web_app_file_handler_registration.cc
+++ b/chrome/browser/web_applications/components/web_app_file_handler_registration.cc
@@ -13,7 +13,7 @@
 // This block defines stub implementations of OS specific methods for
 // FileHandling. Currently, Windows, MacOSX and Desktop Linux (but not Chrome
 // OS) have their own implementations.
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
 bool ShouldRegisterFileHandlersWithOs() {
   return false;
 }
diff --git a/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc b/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc
index 3e302e5..e647dee 100644
--- a/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc
+++ b/chrome/browser/web_applications/components/web_app_install_utils_unittest.cc
@@ -38,17 +38,6 @@
 
 }  // namespace
 
-class WebAppInstallUtilsWithShortcutsMenu : public testing::Test {
- public:
-  WebAppInstallUtilsWithShortcutsMenu() {
-    scoped_feature_list.InitAndEnableFeature(
-        features::kDesktopPWAsAppIconShortcutsMenu);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list;
-};
-
 TEST(WebAppInstallUtils, UpdateWebAppInfoFromManifest) {
   WebApplicationInfo web_app_info;
   web_app_info.title = kAlternativeAppTitle;
@@ -295,8 +284,10 @@
 }
 
 // Tests that WebAppInfo is correctly updated when Manifest contains Shortcuts.
-TEST_F(WebAppInstallUtilsWithShortcutsMenu,
-       UpdateWebAppInfoFromManifestWithShortcuts) {
+TEST(WebAppInstallUtils, UpdateWebAppInfoFromManifestWithShortcuts) {
+  base::test::ScopedFeatureList feature_list(
+      features::kDesktopPWAsAppIconShortcutsMenu);
+
   WebApplicationInfo web_app_info;
   web_app_info.title = kAlternativeAppTitle;
   web_app_info.start_url = GURL("http://www.notchromium.org");
@@ -500,8 +491,10 @@
 }
 
 // Tests that we limit the number of shortcut icons declared by a site.
-TEST_F(WebAppInstallUtilsWithShortcutsMenu,
-       UpdateWebAppInfoFromManifestTooManyShortcutIcons) {
+TEST(WebAppInstallUtils, UpdateWebAppInfoFromManifestTooManyShortcutIcons) {
+  base::test::ScopedFeatureList feature_list(
+      features::kDesktopPWAsAppIconShortcutsMenu);
+
   blink::Manifest manifest;
   for (unsigned int i = 0; i < kNumTestIcons; ++i) {
     blink::Manifest::ShortcutItem shortcut_item;
@@ -554,8 +547,10 @@
 }
 
 // Tests that we limit the size of shortcut icons declared by a site.
-TEST_F(WebAppInstallUtilsWithShortcutsMenu,
-       UpdateWebAppInfoFromManifestShortcutIconsTooLarge) {
+TEST(WebAppInstallUtils, UpdateWebAppInfoFromManifestShortcutIconsTooLarge) {
+  base::test::ScopedFeatureList feature_list(
+      features::kDesktopPWAsAppIconShortcutsMenu);
+
   blink::Manifest manifest;
   for (int i = 1; i <= 20; ++i) {
     blink::Manifest::ShortcutItem shortcut_item;
@@ -660,7 +655,12 @@
 // Tests that when FilterAndResizeIconsGenerateMissing is called with no
 // app icon or shortcut icon data in web_app_info, web_app_info.icon_bitmaps_any
 // is correctly populated.
-TEST(WebAppInstallUtils, FilterAndResizeIconsGenerateMissingNoWebAppIconData) {
+TEST(WebAppInstallUtils,
+     FilterAndResizeIconsGenerateMissingNoWebAppIconData_WithoutShortcuts) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndDisableFeature(
+      features::kDesktopPWAsAppIconShortcutsMenu);
+
   WebApplicationInfo web_app_info;
   web_app_info.title = u"App Name";
 
@@ -670,6 +670,9 @@
   FilterAndResizeIconsGenerateMissing(&web_app_info, &icons_map);
 
   EXPECT_EQ(SizesToGenerate().size(), web_app_info.icon_bitmaps.any.size());
+  for (const auto& icon_bitmap : web_app_info.icon_bitmaps.any) {
+    EXPECT_EQ(SK_ColorWHITE, icon_bitmap.second.getColor(0, 0));
+  }
 }
 
 // Tests that when FilterAndResizeIconsGenerateMissing is called with maskable
@@ -748,8 +751,11 @@
 // Tests that when FilterAndResizeIconsGenerateMissing is called with no
 // app icon or shortcut icon data in web_app_info, and kDesktopPWAShortcutsMenu
 // feature enabled, web_app_info.icon_bitmaps_any is correctly populated.
-TEST_F(WebAppInstallUtilsWithShortcutsMenu,
-       FilterAndResizeIconsGenerateMissingNoWebAppIconData) {
+TEST(WebAppInstallUtils,
+     FilterAndResizeIconsGenerateMissingNoWebAppIconData_WithShortcuts) {
+  base::test::ScopedFeatureList feature_list(
+      features::kDesktopPWAsAppIconShortcutsMenu);
+
   WebApplicationInfo web_app_info;
   web_app_info.title = u"App Name";
 
@@ -768,8 +774,10 @@
 // Tests that when FilterAndResizeIconsGenerateMissing is called with both
 // app icon and shortcut icon bitmaps in icons_map,
 // web_app_info.icon_bitmaps_any is correctly populated.
-TEST_F(WebAppInstallUtilsWithShortcutsMenu,
-       FilterAndResizeIconsGenerateMissingWithShortcutIcons) {
+TEST(WebAppInstallUtils, FilterAndResizeIconsGenerateMissingWithShortcutIcons) {
+  base::test::ScopedFeatureList feature_list(
+      features::kDesktopPWAsAppIconShortcutsMenu);
+
   // Construct |icons_map| to pass to FilterAndResizeIconsGenerateMissing().
   IconsMap icons_map;
   const GURL kIconUrl1("http://www.chromium.org/shortcuts/icon1.png");
diff --git a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
index ed7eb44..8d2d1663 100644
--- a/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
+++ b/chrome/browser/web_applications/manifest_update_manager_browsertest.cc
@@ -1266,14 +1266,8 @@
 
 class ManifestUpdateManagerCaptureLinksBrowserTest
     : public ManifestUpdateManagerBrowserTest {
- public:
-  ManifestUpdateManagerCaptureLinksBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kWebAppEnableLinkCapturing);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::ScopedFeatureList scoped_feature_list_{
+      blink::features::kWebAppEnableLinkCapturing};
 };
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerCaptureLinksBrowserTest,
@@ -1455,14 +1449,8 @@
 // available in unit tests at ManifestUpdateTaskTest.
 class ManifestUpdateManagerBrowserTestWithFileHandling
     : public ManifestUpdateManagerBrowserTest {
- public:
-  ManifestUpdateManagerBrowserTestWithFileHandling() {
-    scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kFileHandlingAPI);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::ScopedFeatureList scoped_feature_list_{
+      blink::features::kFileHandlingAPI};
 };
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithFileHandling,
@@ -1867,14 +1855,8 @@
 
 class ManifestUpdateManagerBrowserTestWithShortcutsMenu
     : public ManifestUpdateManagerBrowserTest {
- public:
-  ManifestUpdateManagerBrowserTestWithShortcutsMenu() {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kDesktopPWAsAppIconShortcutsMenu);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::ScopedFeatureList scoped_feature_list_{
+      features::kDesktopPWAsAppIconShortcutsMenu};
 };
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithShortcutsMenu,
@@ -2222,14 +2204,8 @@
 
 class ManifestUpdateManagerIconUpdatingBrowserTest
     : public ManifestUpdateManagerBrowserTest {
- public:
-  ManifestUpdateManagerIconUpdatingBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        features::kWebAppManifestIconUpdating);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::ScopedFeatureList scoped_feature_list_{
+      features::kWebAppManifestIconUpdating};
 };
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerIconUpdatingBrowserTest,
@@ -2366,11 +2342,6 @@
 class ManifestUpdateManagerBrowserTest_UrlHandlers
     : public ManifestUpdateManagerBrowserTest {
  public:
-  ManifestUpdateManagerBrowserTest_UrlHandlers() {
-    scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kWebAppEnableUrlHandlers);
-  }
-
 #if defined(OS_WIN) || defined(OS_MAC) || \
     (defined(OS_LINUX) && !BUILDFLAG(IS_CHROMEOS_LACROS))
   void SetUpUrlHandlerManager() {
@@ -2400,8 +2371,8 @@
   }
 #endif
 
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::ScopedFeatureList scoped_feature_list_{
+      blink::features::kWebAppEnableUrlHandlers};
 };
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest_UrlHandlers,
@@ -2576,14 +2547,8 @@
 
 class ManifestUpdateManagerBrowserTestWithProtocolHandling
     : public ManifestUpdateManagerBrowserTest {
- public:
-  ManifestUpdateManagerBrowserTestWithProtocolHandling() {
-    scoped_feature_list_.InitAndEnableFeature(
-        blink::features::kWebAppEnableProtocolHandlers);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
+  base::test::ScopedFeatureList scoped_feature_list_{
+      blink::features::kWebAppEnableProtocolHandlers};
 };
 
 IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTestWithProtocolHandling,
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager.cc b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
index 296c6e8..a18b3ab 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager.cc
@@ -50,6 +50,8 @@
 #include "components/version_info/version_info.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/touchscreen_device.h"
 #include "url/gurl.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
@@ -274,6 +276,25 @@
     }
   }
 
+  // Only install if device has a built-in touch screen with stylus support.
+  if (options.disable_if_touchscreen_with_stylus_not_supported) {
+    DCHECK(ui::DeviceDataManager::GetInstance()->AreDeviceListsComplete());
+    bool have_touchscreen_with_stylus = false;
+    for (const ui::TouchscreenDevice& device :
+         ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices()) {
+      if (device.has_stylus &&
+          device.type == ui::InputDeviceType::INPUT_DEVICE_INTERNAL) {
+        have_touchscreen_with_stylus = true;
+        break;
+      }
+    }
+    if (!have_touchscreen_with_stylus) {
+      return options.install_url.spec() +
+             " disabled because the device does not have a built-in "
+             "touchscreen with stylus support.";
+    }
+  }
+
   return absl::nullopt;
 }
 
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc b/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
index e0a965c..ef4e551 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager_browsertest.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/web_applications/test/test_file_utils.h"
 #include "chrome/browser/web_applications/test/test_os_integration_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "components/prefs/pref_service.h"
@@ -33,6 +34,8 @@
 #include "net/ssl/ssl_info.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/devices/device_data_manager_test_api.h"
+#include "ui/events/devices/touchscreen_device.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/public/cpp/test/app_list_test_api.h"
@@ -76,6 +79,7 @@
     : public extensions::ExtensionBrowserTest {
  public:
   PreinstalledWebAppManagerBrowserTest() {
+    feature_list_.InitAndEnableFeature(features::kRecordWebAppDebugInfo);
     PreinstalledWebAppManager::SkipStartupForTesting();
   }
 
@@ -126,6 +130,11 @@
     return WebAppProvider::Get(browser()->profile())->registrar();
   }
 
+  const PreinstalledWebAppManager& manager() {
+    return WebAppProvider::Get(browser()->profile())
+        ->preinstalled_web_app_manager();
+  }
+
   void SyncEmptyConfigs() {
     std::vector<base::Value> app_configs;
     PreinstalledWebAppManager::SetConfigsForTesting(&app_configs);
@@ -200,6 +209,7 @@
  private:
   std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_;
   ScopedOsHooksSuppress os_hooks_suppress_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(PreinstalledWebAppManagerBrowserTest,
@@ -777,6 +787,73 @@
   EXPECT_TRUE(registrar().WasInstalledByOem(app_id));
 }
 
+namespace {
+ui::TouchscreenDevice CreateTouchDevice(ui::InputDeviceType type,
+                                        bool stylus_support) {
+  ui::TouchscreenDevice touch_device = ui::TouchscreenDevice();
+  touch_device.type = type;
+  touch_device.has_stylus = stylus_support;
+  return touch_device;
+}
+}  // namespace
+
+// Note that SetTouchscreenDevices() does not update the device list
+// if the number of displays don't change.
+IN_PROC_BROWSER_TEST_F(PreinstalledWebAppManagerBrowserTest,
+                       DisableIfTouchscreenWithStylusNotSupported) {
+  PreinstalledWebAppManager::BypassOfflineManifestRequirementForTesting();
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  const auto manifest = base::ReplaceStringPlaceholders(
+      R"({
+        "app_url": "$1",
+        "launch_container": "window",
+        "disable_if_touchscreen_with_stylus_not_supported": true,
+        "user_type": ["unmanaged"]
+      })",
+      {GetAppUrl().spec()}, nullptr);
+  AppId app_id = GenerateAppIdFromURL(GetAppUrl());
+  const auto& disabled_configs = manager().debug_info()->disabled_configs;
+  constexpr char kErrorMessage[] =
+      " disabled because the device does not have a built-in touchscreen with "
+      "stylus support.";
+
+  // Test Case: No touchscreen installed on device.
+  EXPECT_EQ(SyncPreinstalledAppConfig(GetAppUrl(), manifest), absl::nullopt);
+  EXPECT_FALSE(registrar().IsInstalled(app_id));
+  EXPECT_EQ(disabled_configs.size(), 1u);
+  EXPECT_EQ(disabled_configs.back().second, GetAppUrl().spec() + kErrorMessage);
+
+  // Test Case: Built-in touchscreen without stylus support installed on device.
+  ui::DeviceDataManagerTestApi().SetTouchscreenDevices({CreateTouchDevice(
+      ui::InputDeviceType::INPUT_DEVICE_INTERNAL, /* stylus_support =*/false)});
+  EXPECT_EQ(SyncPreinstalledAppConfig(GetAppUrl(), manifest), absl::nullopt);
+  EXPECT_FALSE(registrar().IsInstalled(app_id));
+  EXPECT_EQ(disabled_configs.size(), 2u);
+  EXPECT_EQ(disabled_configs.back().second, GetAppUrl().spec() + kErrorMessage);
+
+  // Test Case: Connected external touchscreen with stylus support connected to
+  // device.
+  ui::DeviceDataManagerTestApi().SetTouchscreenDevices(
+      {CreateTouchDevice(ui::InputDeviceType::INPUT_DEVICE_INTERNAL,
+                         /* stylus_support =*/false),
+       CreateTouchDevice(ui::InputDeviceType::INPUT_DEVICE_USB,
+                         /* stylus_support =*/true)});
+  EXPECT_EQ(SyncPreinstalledAppConfig(GetAppUrl(), manifest), absl::nullopt);
+  EXPECT_FALSE(registrar().IsInstalled(app_id));
+  EXPECT_EQ(disabled_configs.size(), 3u);
+  EXPECT_EQ(disabled_configs.back().second, GetAppUrl().spec() + kErrorMessage);
+
+  // Test Case: Create a built-in touchscreen device with stylus support and add
+  // it to the device.
+  ui::DeviceDataManagerTestApi().SetTouchscreenDevices(
+      {CreateTouchDevice(ui::InputDeviceType::INPUT_DEVICE_INTERNAL, true)});
+  EXPECT_EQ(SyncPreinstalledAppConfig(GetAppUrl(), manifest),
+            InstallResultCode::kSuccessNewInstall);
+  EXPECT_TRUE(registrar().IsInstalled(app_id));
+  EXPECT_EQ(disabled_configs.size(), 3u);
+}
+
 IN_PROC_BROWSER_TEST_F(PreinstalledWebAppManagerBrowserTest,
                        UninstallFromTwoItemAppListFolder) {
   GURL preinstalled_app_start_url("https://example.org/");
diff --git a/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc b/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
index 68dc51e..0d732a2 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_manager_unittest.cc
@@ -271,6 +271,7 @@
     install_options.add_to_desktop = true;
     install_options.add_to_quick_launch_bar = true;
     install_options.require_manifest = true;
+    install_options.disable_if_touchscreen_with_stylus_not_supported = false;
     test_install_options_list.push_back(std::move(install_options));
   }
   {
@@ -284,6 +285,7 @@
     install_options.add_to_desktop = false;
     install_options.add_to_quick_launch_bar = false;
     install_options.require_manifest = true;
+    install_options.disable_if_touchscreen_with_stylus_not_supported = false;
     install_options.uninstall_and_replace.push_back("migrationsourceappid");
     test_install_options_list.push_back(std::move(install_options));
   }
diff --git a/chrome/browser/web_applications/preinstalled_web_app_utils.cc b/chrome/browser/web_applications/preinstalled_web_app_utils.cc
index 2f01933..466498d 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_utils.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_utils.cc
@@ -164,6 +164,11 @@
 // the device OEM.
 constexpr char kOemInstalled[] = "oem_installed";
 
+// Contains boolean indicating weather the app should only be install on devices
+// with a built-in touchscreen with stylus support.
+constexpr char kDisableIfTouchScreenWithStylusNotSupported[] =
+    "disable_if_touchscreen_with_stylus_not_supported";
+
 }  // namespace
 
 OptionsOrError ParseConfig(FileUtilsWrapper& file_utils,
@@ -402,6 +407,16 @@
     options.oem_installed = value->GetBool();
   }
 
+  // disable_if_touchscreen_with_stylus_not_supported
+  value = app_config.FindKey(kDisableIfTouchScreenWithStylusNotSupported);
+  if (value) {
+    if (!value->is_bool()) {
+      return base::StrCat({file.AsUTF8Unsafe(), " had an invalid ",
+                           kDisableIfTouchScreenWithStylusNotSupported});
+    }
+    options.disable_if_touchscreen_with_stylus_not_supported = value->GetBool();
+  }
+
   return options;
 }
 
diff --git a/chrome/browser/web_applications/preinstalled_web_app_utils_unittest.cc b/chrome/browser/web_applications/preinstalled_web_app_utils_unittest.cc
index ffcfdab..00dd897 100644
--- a/chrome/browser/web_applications/preinstalled_web_app_utils_unittest.cc
+++ b/chrome/browser/web_applications/preinstalled_web_app_utils_unittest.cc
@@ -522,4 +522,38 @@
   EXPECT_TRUE(oem_set->oem_installed);
 }
 
+TEST_F(PreinstalledWebAppUtilsTest,
+       DisableIfTouchscreenWithStylusNotSupported) {
+  absl::optional<ExternalInstallOptions> non_bool = ParseConfig(R"(
+        {
+          "app_url": "https://www.test.org",
+          "launch_container": "window",
+          "disable_if_touchscreen_with_stylus_not_supported": "some string",
+          "user_type": ["test"]
+        }
+    )");
+  EXPECT_FALSE(non_bool.has_value());
+
+  absl::optional<ExternalInstallOptions> default_setting = ParseConfig(R"(
+        {
+          "app_url": "https://www.test.org",
+          "launch_container": "window",
+          "user_type": ["test"]
+        }
+    )");
+  EXPECT_FALSE(
+      default_setting->disable_if_touchscreen_with_stylus_not_supported);
+
+  absl::optional<ExternalInstallOptions> touchscreen_set = ParseConfig(R"(
+        {
+          "app_url": "https://www.test.org",
+          "launch_container": "window",
+          "disable_if_touchscreen_with_stylus_not_supported": true,
+          "user_type": ["test"]
+        }
+    )");
+  EXPECT_TRUE(
+      touchscreen_set->disable_if_touchscreen_with_stylus_not_supported);
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/system_web_apps/test/system_web_app_browsertest_base.cc b/chrome/browser/web_applications/system_web_apps/test/system_web_app_browsertest_base.cc
index 2d43bb6..9586454 100644
--- a/chrome/browser/web_applications/system_web_apps/test/system_web_app_browsertest_base.cc
+++ b/chrome/browser/web_applications/system_web_apps/test/system_web_app_browsertest_base.cc
@@ -18,12 +18,17 @@
 
 namespace web_app {
 
-SystemWebAppBrowserTestBase::SystemWebAppBrowserTestBase(bool install_mock) {}
+SystemWebAppBrowserTestBase::SystemWebAppBrowserTestBase(bool install_mock) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  WebAppProvider::EnableSystemWebAppsInLacrosForTesting();
+#endif
+}
 
 SystemWebAppBrowserTestBase::~SystemWebAppBrowserTestBase() = default;
 
 SystemWebAppManager& SystemWebAppBrowserTestBase::GetManager() {
-  return WebAppProvider::Get(browser()->profile())->system_web_app_manager();
+  return WebAppProvider::GetForSystemWebApps(browser()->profile())
+      ->system_web_app_manager();
 }
 
 SystemAppType SystemWebAppBrowserTestBase::GetMockAppType() {
@@ -110,7 +115,7 @@
     const apps::AppLaunchParams& params) {
   return params.override_url.is_valid()
              ? params.override_url
-             : WebAppProvider::Get(browser()->profile())
+             : WebAppProvider::GetForSystemWebApps(browser()->profile())
                    ->registrar()
                    .GetAppStartUrl(params.app_id);
 }
diff --git a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc
index 9faf54d..87b60cfc 100644
--- a/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/system_web_apps/test/system_web_app_manager_browsertest.cc
@@ -1050,7 +1050,7 @@
   // resets the OnAppsSynchronized signal, and starts a new synchronize request.
   void WaitForSystemAppsSynchronized() {
     base::RunLoop run_loop;
-    WebAppProvider::Get(browser()->profile())
+    WebAppProvider::GetForSystemWebApps(browser()->profile())
         ->system_web_app_manager()
         .on_apps_synchronized()
         .Post(FROM_HERE, run_loop.QuitClosure());
@@ -1427,7 +1427,7 @@
 
   void WaitForSystemAppsBackgroundTasksStart() {
     base::RunLoop run_loop;
-    WebAppProvider::Get(browser()->profile())
+    WebAppProvider::GetForSystemWebApps(browser()->profile())
         ->system_web_app_manager()
         .on_tasks_started()
         .Post(FROM_HERE, run_loop.QuitClosure());
diff --git a/chrome/browser/xsurface/BUILD.gn b/chrome/browser/xsurface/BUILD.gn
index 8f8f4b3..da1fce1 100644
--- a/chrome/browser/xsurface/BUILD.gn
+++ b/chrome/browser/xsurface/BUILD.gn
@@ -7,7 +7,9 @@
 android_library("java") {
   sources = [
     "android/java/src/org/chromium/chrome/browser/xsurface/FeedActionsHandler.java",
+    "android/java/src/org/chromium/chrome/browser/xsurface/FeedLaunchReliabilityLogger.java",
     "android/java/src/org/chromium/chrome/browser/xsurface/FeedLoggingDependencyProvider.java",
+    "android/java/src/org/chromium/chrome/browser/xsurface/FeedNetworkRequestReliabilityLogger.java",
     "android/java/src/org/chromium/chrome/browser/xsurface/HybridListRenderer.java",
     "android/java/src/org/chromium/chrome/browser/xsurface/ImageFetchClient.java",
     "android/java/src/org/chromium/chrome/browser/xsurface/ImagePrefetcher.java",
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedLaunchReliabilityLogger.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedLaunchReliabilityLogger.java
new file mode 100644
index 0000000..ff21d4d
--- /dev/null
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedLaunchReliabilityLogger.java
@@ -0,0 +1,128 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.xsurface;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Interface for logging latency and availability signals for feed launches. All timestamps are in
+ * terms of nanoseconds since system boot.
+ */
+public interface FeedLaunchReliabilityLogger {
+    /** Type of surface the feed is being launched on. */
+    @IntDef({SurfaceType.UNSPECIFIED, SurfaceType.NEW_TAB_PAGE, SurfaceType.START_SURFACE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SurfaceType {
+        int UNSPECIFIED = 0;
+        int NEW_TAB_PAGE = 1;
+        int START_SURFACE = 2;
+    }
+
+    /** Type of stream being launched (the "For you" or "Following" feed). */
+    @IntDef({StreamType.UNSPECIFIED, StreamType.FOR_YOU, StreamType.WEB_FEED})
+    @Retention(RetentionPolicy.SOURCE)
+    @interface StreamType {
+        int UNSPECIFIED = 0;
+        int FOR_YOU = 1;
+        int WEB_FEED = 2;
+    }
+
+    /**
+     * Sets details about the stream to be logged as metadata.
+     * @param surfaceType Whether the feed is on the new tab page or Start Surface.
+     * @param streamType Whether the feed is a "for you" feed or a "following" feed.
+     * @param streamId Identifier for the stream used to disambiguate events from concurrent
+     *         streams.
+     */
+    default void setMetadata(
+            @SurfaceType int surfaceType, @StreamType int streamType, int streamId) {}
+
+    /**
+     * Log when the feed is launched because the NTP or Start Surface was created.
+     * @param timestamp Time at which the surface began to be created.
+     */
+    default void logDiscoverUiStarting(long timestamp) {}
+
+    /**
+     * Log when the feed is launched because its surface was shown and cards needed to be
+     * re-rendered.
+     * @param timestamp Time at which the surface was shown.
+     */
+    default void logDiscoverFeedReloaded(long timestamp) {}
+
+    /**
+     * Log when the feed is launched in any case not already handled by logDiscoverUiStarting() or
+     * logDiscoverFeedReloaded().
+     * @param timestamp Time at which the feed stream was bound.
+     */
+    default void logDiscoverFeedLaunchOtherStart(long timestamp) {}
+
+    /**
+     * Log when cached feed content is about to be read.
+     * @param timestamp Event time.
+     */
+    default void logDiscoverCacheReadStart(long timestamp) {}
+
+    /**
+     * Log after finishing attempting to read cached feed content.
+     * @param timestamp Event time.
+     * @param result DiscoverCardReadCacheResult.
+     */
+    default void logDiscoverCacheReadEnd(long timestamp, int result) {}
+
+    /**
+     * Log when the loading spinner is shown.
+     * @param timestamp Time at which the spinner was shown.
+     */
+    default void logDiscoverLoadingIndicatorShown(long timestamp) {}
+
+    /**
+     * Log when rendering of above-the-fold feed content begins.
+     * @param timestamp Event time.
+     */
+    default void logDiscoverAtfRenderStart(long timestamp) {}
+
+    /**
+     * Log when rendering of above-the-fold feed content finishes.
+     * @param timestamp Event time.
+     * @param result DiscoverAboveTheFoldRenderResult.
+     */
+    default void logDiscoverAtfRenderEnd(long timestamp, int result) {}
+
+    /**
+     * Log when making a feed query request.
+     * @param timestamp Event time.
+     * @param requestId Unique ID for this network request.
+     * @return A logger for this network request.
+     */
+    @Nullable
+    default FeedNetworkRequestReliabilityLogger logFeedQueryRequestStart(
+            long timestamp, int requestId) {
+        return null;
+    }
+
+    /**
+     * Log just before making a feed actions upload request.
+     * @param timestamp Event time.
+     * @param requestId Unique ID for this network request.
+     * @return A logger for this network request.
+     */
+    @Nullable
+    default FeedNetworkRequestReliabilityLogger logActionsUploadRequestStart(
+            long timestamp, int requestId) {
+        return null;
+    }
+
+    /**
+     * Log to mark the end of the feed launch.
+     * @param timestamp Event time, possibly the same as one of the other events.
+     * @param result DiscoverLaunchResult.
+     */
+    default void logDiscoverLaunchFinished(long timestamp, int result) {}
+}
\ No newline at end of file
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedNetworkRequestReliabilityLogger.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedNetworkRequestReliabilityLogger.java
new file mode 100644
index 0000000..77b930c
--- /dev/null
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/FeedNetworkRequestReliabilityLogger.java
@@ -0,0 +1,44 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.xsurface;
+
+/**
+ * Interface for logging latency and availability signals for feed network requests. All timestamps
+ * are in terms of nanoseconds since system boot.
+ *
+ * See {@link FeedLaunchReliabilityLogger} for the network request start event methods: they start
+ * the network request flow and return FeedNetworkRequestReliabilityLogger instances.
+ */
+public interface FeedNetworkRequestReliabilityLogger {
+    /**
+     * Log after the request has been sent.
+     * @param timestamp Event time.
+     */
+    default void logRequestSent(long timestamp) {}
+
+    /**
+     * Log after the response is received and before it is parsed.
+     * @param serverRecvTimestamp Server-reported time (nanoseconds) at which the request arrived.
+     * @param serverSendTimestamp Server-reported time (nanoseconds) at which the response was sent.
+     * @param clientRecvTimestamp Client time at which the response was received.
+     */
+    default void logResponseReceived(
+            long serverRecvTimestamp, long serverSendTimestamp, long clientRecvTimestamp) {}
+
+    /** Special network request status: see {@link #logRequestFinished}. */
+    static final int NO_STATUS = 0;
+
+    /**
+     * Log after logResponseReceived() if there's a network error, or after parsing the response
+     * otherwise.
+     * @param timestamp Event time.
+     * @param cronetStatusCode Network error code from
+     *         components/cronet/android/api/src/org/chromium/net/NetworkException.java. Pass {@link
+     *         #NO_STATUS} if there's no error.
+     * @param httpStatusCode HTTP status code. If there is no HTTP status code because there was a
+     *         network error, pass {@link #NO_STATUS}.
+     */
+    default void logRequestFinished(long timestamp, int cronetStatusCode, int httpStatusCode) {}
+}
\ No newline at end of file
diff --git a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceScope.java b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceScope.java
index 0aa61d3f..202cf7d11 100644
--- a/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceScope.java
+++ b/chrome/browser/xsurface/android/java/src/org/chromium/chrome/browser/xsurface/SurfaceScope.java
@@ -25,4 +25,14 @@
 
     default void replaceDataStoreEntry(String key, byte[] data) {}
     default void removeDataStoreEntry(String key) {}
+
+    /**
+     * Return the FeedLaunchReliabilityLogger associated with the surface, creating it if it
+     * doesn't exist.
+     * @return The surface's FeedLaunchReliabilityLogger instance.
+     */
+    @Nullable
+    default FeedLaunchReliabilityLogger getFeedLaunchReliabilityLogger() {
+        return null;
+    }
 }
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index ddef0f6..e58cedf7 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-master-1622548311-ca747ddbe3d5cda88542993ec1ab0665dd902079.profdata
+chrome-win32-master-1622591710-c9a2180fb5e864d2ad017c54d17c7b8206fb50aa.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 6a4b74a..360b1353 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-master-1622559517-888d0ce4885aac3784e12cd03c7ecbcbaa85a2a9.profdata
+chrome-win64-master-1622591710-1a8c22323a1687d1394fe9d4d097bf004d4ba059.profdata
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json
index 5701a97..7c60823e 100644
--- a/chrome/common/extensions/api/_permission_features.json
+++ b/chrome/common/extensions/api/_permission_features.json
@@ -82,11 +82,22 @@
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"]
   },
-  "certificateProvider": {
+  "certificateProvider": [{
     "channel": "stable",
     "platforms": ["chromeos"],
     "extension_types": ["extension", "platform_app"]
-  },
+  }, {
+    // https://crbug.com/1194693
+    "channel": "stable",
+    "component_extensions_auto_granted": false,
+    "extension_types": ["login_screen_extension"],
+    "location": "policy",
+    "platforms": ["chromeos"],
+    "allowlist": [
+      "6B748A5C005F21B7CBCF4170C2F883E435DEB511",   // CSSI Smart Card Middleware
+      "075FF17D52ED6E3C2E5EC4D99F188E7A25AF47EA"    // Beta CSSI Smart Card Middleware
+    ]
+  }],
   "chromePrivate": {
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app"],
@@ -488,6 +499,17 @@
     "extension_types": ["extension"],
     "platforms": ["chromeos"]
   }, {
+    // https://crbug.com/1194693
+    "channel": "stable",
+    "component_extensions_auto_granted": false,
+    "extension_types": ["login_screen_extension"],
+    "location": "policy",
+    "platforms": ["chromeos"],
+    "allowlist": [
+      "6B748A5C005F21B7CBCF4170C2F883E435DEB511",   // CSSI Smart Card Middleware
+      "075FF17D52ED6E3C2E5EC4D99F188E7A25AF47EA"    // Beta CSSI Smart Card Middleware
+    ]
+  }, {
     "channel": "stable",
     "dependencies": ["behavior:imprivata_login_screen_extension"],
     "extension_types": ["login_screen_extension"],
@@ -586,7 +608,7 @@
     "channel": "stable",
     "extension_types": ["platform_app"]
   }],
-  "notifications": {
+  "notifications": [{
     // The chrome.notifications functionality listed in notifications.idl is
     // available only to extension/platform_app types. The implementation of
     // that functionality enforces that restriction in
@@ -597,7 +619,18 @@
     "extension_types": [
       "extension", "legacy_packaged_app", "hosted_app", "platform_app"
     ]
-  },
+  }, {
+    // https://crbug.com/1194693
+    "channel": "stable",
+    "component_extensions_auto_granted": false,
+    "extension_types": ["login_screen_extension"],
+    "location": "policy",
+    "platforms": ["chromeos"],
+    "allowlist": [
+      "6B748A5C005F21B7CBCF4170C2F883E435DEB511",   // CSSI Smart Card Middleware
+      "075FF17D52ED6E3C2E5EC4D99F188E7A25AF47EA"    // Beta CSSI Smart Card Middleware
+    ]
+  }],
   "echoPrivate": {
     "channel": "stable",
     "extension_types": ["extension", "legacy_packaged_app", "platform_app"],
diff --git a/chrome/installer/util/install_service_work_item_impl.cc b/chrome/installer/util/install_service_work_item_impl.cc
index cfa6d22..1d065cf 100644
--- a/chrome/installer/util/install_service_work_item_impl.cc
+++ b/chrome/installer/util/install_service_work_item_impl.cc
@@ -414,22 +414,25 @@
 InstallServiceWorkItemImpl::ServiceConfig
 InstallServiceWorkItemImpl::MakeUpgradeServiceConfig(
     const ServiceConfig& original_config) {
-  ServiceConfig new_config = original_config;
+  ServiceConfig new_config(
+      kServiceType, kServiceStartType, kServiceErrorControl,
+      service_cmd_line_.GetCommandLineString(), kServiceDependencies,
+      GetCurrentServiceDisplayName());
 
-  if (original_config.type == kServiceType)
+  if (original_config.type == new_config.type)
     new_config.type = SERVICE_NO_CHANGE;
-  if (original_config.start_type == kServiceStartType)
+  if (original_config.start_type == new_config.start_type)
     new_config.start_type = SERVICE_NO_CHANGE;
-  if (original_config.error_control == kServiceErrorControl)
+  if (original_config.error_control == new_config.error_control)
     new_config.error_control = SERVICE_NO_CHANGE;
   if (!_wcsicmp(original_config.cmd_line.c_str(),
-                service_cmd_line_.GetCommandLineString().c_str())) {
+                new_config.cmd_line.c_str())) {
     new_config.cmd_line.clear();
   }
-  if (original_config.dependencies == MultiSzToVector(kServiceDependencies))
+  if (original_config.dependencies == new_config.dependencies)
     new_config.dependencies.clear();
   if (!_wcsicmp(original_config.display_name.c_str(),
-                GetCurrentServiceDisplayName().c_str())) {
+                new_config.display_name.c_str())) {
     new_config.display_name.clear();
   }
 
diff --git a/chrome/installer/util/install_service_work_item_unittest.cc b/chrome/installer/util/install_service_work_item_unittest.cc
index 610c681..7b619dcc 100644
--- a/chrome/installer/util/install_service_work_item_unittest.cc
+++ b/chrome/installer/util/install_service_work_item_unittest.cc
@@ -204,6 +204,9 @@
 
   EXPECT_TRUE(InstallServiceWorkItem::DeleteService(
       kServiceName, kProductRegPath, kClsids, kIids));
+
+  // Check to make sure that the item shows that the service is deleted.
+  EXPECT_TRUE(IsServiceGone(item.get()));
 }
 
 TEST_F(InstallServiceWorkItemTest, Do_UpgradeNoChanges) {
@@ -222,10 +225,25 @@
       kClsids, kIids);
   EXPECT_TRUE(item_upgrade->Do());
 
+  // Check to make sure that no upgrade happened, and both the old and new items
+  // show that the service is correctly configured.
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item_upgrade.get()));
+
   item_upgrade->Rollback();
+
+  // Check to make sure that no rollback happened, and both the old and new
+  // items show that the service is correctly configured.
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item_upgrade.get()));
+
   EXPECT_TRUE(GetImpl(item_upgrade.get())->OpenService());
 
   EXPECT_TRUE(GetImpl(item_upgrade.get())->DeleteCurrentService());
+
+  // Check to make sure that both items show that the service is deleted.
+  EXPECT_TRUE(IsServiceGone(item.get()));
+  EXPECT_TRUE(IsServiceGone(item_upgrade.get()));
 }
 
 TEST_F(InstallServiceWorkItemTest, Do_UpgradeChangedCmdLine) {
@@ -244,13 +262,26 @@
       kClsids, kIids);
   EXPECT_TRUE(item_upgrade->Do());
 
+  // Check to make sure the upgrade happened, and the new item shows that the
+  // service is correctly configured, while the old item shows that the service
+  // is not correctly configured.
+  EXPECT_TRUE(IsServiceCorrectlyConfigured(item_upgrade.get()));
+  EXPECT_FALSE(IsServiceCorrectlyConfigured(item.get()));
+
   item_upgrade->Rollback();
   EXPECT_TRUE(GetImpl(item_upgrade.get())->OpenService());
 
+  // Check to make sure the rollback happened, and the old item shows that it is
+  // correctly configured, while the new item shows that the service is not
+  // correctly configured.
   EXPECT_TRUE(IsServiceCorrectlyConfigured(item.get()));
   EXPECT_FALSE(IsServiceCorrectlyConfigured(item_upgrade.get()));
 
   EXPECT_TRUE(GetImpl(item_upgrade.get())->DeleteCurrentService());
+
+  // Check to make sure that both items show that the service is deleted.
+  EXPECT_TRUE(IsServiceGone(item.get()));
+  EXPECT_TRUE(IsServiceGone(item_upgrade.get()));
 }
 
 TEST_F(InstallServiceWorkItemTest, Do_ServiceName) {
diff --git a/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js b/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js
index e9019a6..8874c23 100644
--- a/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js
+++ b/chrome/renderer/resources/extensions/chromeos_ime_service_bindings.js
@@ -227,6 +227,8 @@
 
   /**
    * Activates an input method based on its specification.
+   * If |activateIME| was called previously, this will call |onConnectionError|
+   * for the previous connection, because only one IME can be active at a time.
    *
    * @param {string} imeSpec The specification of an IME (e.g. the engine ID).
    * @param {!Uint8Array} extra The extra data (e.g. initial tasks to run).
@@ -260,6 +262,8 @@
             if (bound && onConnectionError) {
               this.activeEngine_.ptr.setConnectionErrorHandler(
                   onConnectionError);
+              this.clientChannel_.ptr.setConnectionErrorHandler(
+                  onConnectionError);
             };
             if (onConnection) {
               onConnection(bound);
diff --git a/chrome/services/file_util/public/cpp/zip_file_creator.cc b/chrome/services/file_util/public/cpp/zip_file_creator.cc
index cdf9ea9..cd4b006 100644
--- a/chrome/services/file_util/public/cpp/zip_file_creator.cc
+++ b/chrome/services/file_util/public/cpp/zip_file_creator.cc
@@ -13,7 +13,6 @@
 #include "components/services/filesystem/directory_impl.h"
 #include "components/services/filesystem/lock_table.h"
 #include "content/public/browser/browser_thread.h"
-#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 namespace {
 
@@ -22,14 +21,6 @@
   return base::File(zip_path, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
 }
 
-void BindDirectoryInBackground(
-    const base::FilePath& src_dir,
-    mojo::PendingReceiver<filesystem::mojom::Directory> receiver) {
-  auto directory_impl = std::make_unique<filesystem::DirectoryImpl>(
-      src_dir, /*temp_dir=*/nullptr, /*lock_table=*/nullptr);
-  mojo::MakeSelfOwnedReceiver(std::move(directory_impl), std::move(receiver));
-}
-
 }  // namespace
 
 ZipFileCreator::ZipFileCreator(ResultCallback callback,
@@ -63,7 +54,11 @@
 
 void ZipFileCreator::Stop() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  ReportDone(false);
+  directory_task_runner_->PostTaskAndReply(
+      FROM_HERE,
+      base::BindOnce(&ZipFileCreator::CloseDirectory, base::Unretained(this)),
+      base::BindOnce(&ZipFileCreator::ReportDone, base::Unretained(this),
+                     false));
 }
 
 void ZipFileCreator::CreateZipFile(
@@ -86,8 +81,9 @@
 
   mojo::PendingRemote<filesystem::mojom::Directory> directory;
   directory_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&BindDirectoryInBackground, src_dir_,
-                                directory.InitWithNewPipeAndPassReceiver()));
+      FROM_HERE,
+      base::BindOnce(&ZipFileCreator::BindDirectory, base::Unretained(this),
+                     directory.InitWithNewPipeAndPassReceiver()));
 
   service_.Bind(std::move(service));
   service_->BindZipFileCreator(
@@ -101,6 +97,19 @@
       base::BindOnce(&ZipFileCreator::ReportDone, base::Unretained(this)));
 }
 
+void ZipFileCreator::BindDirectory(
+    mojo::PendingReceiver<filesystem::mojom::Directory> receiver) {
+  src_dir_ref_ = mojo::MakeSelfOwnedReceiver(
+      std::make_unique<filesystem::DirectoryImpl>(
+          src_dir_, /*temp_dir=*/nullptr, /*lock_table=*/nullptr),
+      std::move(receiver));
+}
+
+void ZipFileCreator::CloseDirectory() {
+  if (src_dir_ref_)
+    src_dir_ref_->Close();
+}
+
 void ZipFileCreator::ReportDone(bool success) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
diff --git a/chrome/services/file_util/public/cpp/zip_file_creator.h b/chrome/services/file_util/public/cpp/zip_file_creator.h
index 30b2eac..93b58ec 100644
--- a/chrome/services/file_util/public/cpp/zip_file_creator.h
+++ b/chrome/services/file_util/public/cpp/zip_file_creator.h
@@ -15,6 +15,7 @@
 #include "chrome/services/file_util/public/mojom/zip_file_creator.mojom.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
 
 // ZipFileCreator creates a ZIP file from a specified list of files and
 // directories under a common parent directory. This is done in a sandboxed
@@ -40,11 +41,18 @@
 
  private:
   // Called after the dest_file |file| is opened on the blocking pool to
-  // create the zip file in it using a sandboxed utility process.
+  // create the ZIP file in it using a sandboxed utility process.
   void CreateZipFile(
       mojo::PendingRemote<chrome::mojom::FileUtilService> service,
       base::File file);
 
+  // Binds the Directory receiver to its implementation.
+  void BindDirectory(
+      mojo::PendingReceiver<filesystem::mojom::Directory> receiver);
+
+  // Closes the Directory implementation.
+  void CloseDirectory();
+
   // Notifies by calling |callback| specified in the constructor the end of the
   // ZIP operation. Deletes this.
   void ReportDone(bool success);
@@ -59,10 +67,15 @@
   // Entries are relative paths under directory |src_dir_|.
   const std::vector<base::FilePath> src_relative_paths_;
 
+  // The output ZIP file path.
+  const base::FilePath dest_file_;
+
+  // Task runner used by the Directory implementation.
   scoped_refptr<base::SequencedTaskRunner> directory_task_runner_;
 
-  // The output zip file.
-  const base::FilePath dest_file_;
+  // Weak ref to the self-owned Directory implementation.
+  using DirectoryRef = mojo::SelfOwnedReceiverRef<filesystem::mojom::Directory>;
+  DirectoryRef src_dir_ref_;
 
   // Remote interfaces to the file util service. Only used from the UI thread.
   mojo::Remote<chrome::mojom::FileUtilService> service_;
diff --git a/chrome/services/file_util/zip_file_creator.cc b/chrome/services/file_util/zip_file_creator.cc
index d841941b..d08b665 100644
--- a/chrome/services/file_util/zip_file_creator.cc
+++ b/chrome/services/file_util/zip_file_creator.cc
@@ -32,6 +32,8 @@
 
   bool Open(const zip::Paths paths,
             std::vector<base::File>* const files) override {
+    DCHECK(!paths.empty());
+
     std::vector<filesystem::mojom::FileOpenDetailsPtr> details;
     details.reserve(paths.size());
 
@@ -46,8 +48,11 @@
     }
 
     std::vector<filesystem::mojom::FileOpenResultPtr> results;
-    if (!source_dir_remote_->OpenFileHandles(std::move(details), &results))
+    if (!source_dir_remote_->OpenFileHandles(std::move(details), &results)) {
+      LOG(ERROR) << "Cannot open '" << paths.front() << "' and "
+                 << (paths.size() - 1) << " other files";
       return false;
+    }
 
     files->reserve(files->size() + results.size());
     for (const filesystem::mojom::FileOpenResultPtr& result : results)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 081cb42..509a220 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -7412,6 +7412,7 @@
     if (include_js_tests) {
       deps += [
         "//chrome/test/data/webui:interactive_ui_tests_js_mojo_lite_webui",
+        "//chrome/test/data/webui:interactive_ui_tests_js_mojo_webui",
         "//chrome/test/data/webui:interactive_ui_tests_js_webui",
       ]
     }
@@ -7419,6 +7420,7 @@
     # Runtime dependencies
     data_deps = [
       "//chrome:browser_tests_pak",
+      "//chrome/test/data:web_ui_test_mojom_js_module",
       "//ppapi:ppapi_tests",
       "//testing/buildbot/filters:interactive_ui_tests_filters",
       "//third_party/mesa_headers",
@@ -7524,7 +7526,7 @@
           "//components/media_router/browser:test_support",
         ]
       }
-      if (!is_android && !is_chromeos_lacros) {
+      if (!is_android) {
         sources += [ "../browser/ui/web_applications/test/system_web_app_interactive_uitest.cc" ]
       }
 
diff --git a/chrome/test/data/extensions/api_test/settings_private/manifest.json b/chrome/test/data/extensions/api_test/settings_private/manifest.json
index a8c3cfe..3e229ab 100644
--- a/chrome/test/data/extensions/api_test/settings_private/manifest.json
+++ b/chrome/test/data/extensions/api_test/settings_private/manifest.json
@@ -4,6 +4,10 @@
   "version": "0.1",
   "manifest_version": 2,
   "description": "Test of chrome.settingsPrivate interface",
+  "background": {
+    "scripts": ["test.js"],
+    "persistent": true
+  },
   "permissions": [
     "settingsPrivate"
   ]
diff --git a/chrome/test/data/extensions/api_test/settings_private/test.js b/chrome/test/data/extensions/api_test/settings_private/test.js
index 8c7fffa..67be546 100644
--- a/chrome/test/data/extensions/api_test/settings_private/test.js
+++ b/chrome/test/data/extensions/api_test/settings_private/test.js
@@ -168,7 +168,8 @@
   },
 ];
 
-var testToRun = window.location.search.substring(1);
-chrome.test.runTests(availableTests.filter(function(op) {
-  return op.name == testToRun;
-}));
+chrome.test.getConfig(function(config) {
+  chrome.test.runTests(availableTests.filter(function(op) {
+    return op.name == config.customArg;
+  }));
+});
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 45c24d5..151693a 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2052,13 +2052,7 @@
   },
 
   "LegacySameSiteCookieBehaviorEnabled": {
-    "os": ["win", "linux", "mac", "chromeos"],
-    "policy_pref_mapping_tests": [
-      {
-        "policies": { "LegacySameSiteCookieBehaviorEnabled": 1 },
-        "prefs": { "profile.managed_default_content_settings.legacy_cookie_access": {} }
-      }
-    ]
+    "note": "This policy is deprecated, https://crbug.com/1214078."
   },
 
   "LegacySameSiteCookieBehaviorEnabledForDomainList": {
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 727781d..8743675f 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -51,6 +51,31 @@
     defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
   }
 
+  js2gtest("interactive_ui_tests_js_mojo_webui") {
+    test_type = "mojo_webui"
+
+    sources = [ "cr_components/cr_components_mojo_interactive_test.js" ]
+
+    gen_include_files = [
+      "polymer_browser_test_base.js",
+      "polymer_interactive_ui_test.js",
+    ]
+
+    deps = [
+      ":modulize",
+      "//chrome/browser/ui",
+    ]
+
+    data = [
+      "$root_gen_dir/chrome/test/data/webui/test_browser_proxy.m.js",
+      "$root_gen_dir/chrome/test/data/webui/test_store.m.js",
+      "$root_gen_dir/chrome/test/data/webui/test_util.m.js",
+      "mojo_webui_test_support.js",
+    ]
+
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+  }
+
   js2gtest("browser_tests_js_webui") {
     test_type = "webui"
 
diff --git a/chrome/test/data/webui/cr_components/BUILD.gn b/chrome/test/data/webui/cr_components/BUILD.gn
index c8840a2..048d7e7 100644
--- a/chrome/test/data/webui/cr_components/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/BUILD.gn
@@ -18,6 +18,8 @@
     ":certificate_manager_test",
     ":customize_themes_test",
     ":managed_dialog_test",
+    ":most_visited_focus_test",
+    ":most_visited_test",
   ]
 }
 
@@ -71,3 +73,40 @@
   ]
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
+
+js_library("most_visited_focus_test") {
+  deps = [
+    ":most_visited_test_support",
+    "..:test_browser_proxy.m",
+    "..:test_util.m",
+    "//ui/webui/resources/cr_components/most_visited",
+    "//ui/webui/resources/cr_components/most_visited:browser_proxy",
+    "//ui/webui/resources/cr_components/most_visited:mojom_js_library_for_compile",
+    "//ui/webui/resources/js:load_time_data.m",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
+
+js_library("most_visited_test") {
+  deps = [
+    ":most_visited_test_support",
+    "..:chai_assert",
+    "..:test_browser_proxy.m",
+    "..:test_util.m",
+    "//ui/webui/resources/cr_components/most_visited",
+    "//ui/webui/resources/cr_components/most_visited:browser_proxy",
+    "//ui/webui/resources/cr_components/most_visited:mojom_js_library_for_compile",
+    "//ui/webui/resources/cr_components/most_visited:window_proxy",
+    "//ui/webui/resources/js:cr.m",
+    "//ui/webui/resources/js:load_time_data.m",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
+
+js_library("most_visited_test_support") {
+  deps = [
+    "..:chai_assert",
+    "//ui/webui/resources/js:util.m",
+  ]
+  externs_list = [ "$externs_path/mocha-2.5.js" ]
+}
diff --git a/chrome/test/data/webui/cr_components/cr_components_mojo_browsertest.js b/chrome/test/data/webui/cr_components/cr_components_mojo_browsertest.js
index ff4c7c40..d4bf9575 100644
--- a/chrome/test/data/webui/cr_components/cr_components_mojo_browsertest.js
+++ b/chrome/test/data/webui/cr_components/cr_components_mojo_browsertest.js
@@ -29,3 +29,15 @@
 TEST_F('CrComponentsCustomizeThemesTest', 'All', function() {
   mocha.run();
 });
+
+// eslint-disable-next-line no-var
+var CrComponentsMostVisitedTest = class extends CrComponentsMojoBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://new-tab-page/test_loader.html?module=cr_components/most_visited_test.js';
+  }
+};
+
+TEST_F('CrComponentsMostVisitedTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/cr_components/cr_components_mojo_interactive_test.js b/chrome/test/data/webui/cr_components/cr_components_mojo_interactive_test.js
new file mode 100644
index 0000000..12e41fe67
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/cr_components_mojo_interactive_test.js
@@ -0,0 +1,32 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Interactive tests for shared Polymer 3 components using Mojo.
+ */
+
+// Polymer BrowserTest fixture.
+GEN_INCLUDE(['//chrome/test/data/webui/polymer_interactive_ui_test.js']);
+
+GEN('#include "content/public/test/browser_test.h"');
+
+class CrComponentsMojoInteractiveTest extends PolymerInteractiveUITest {
+  /** @override */
+  get browsePreload() {
+    throw 'this is abstract and should be overriden by subclasses';
+  }
+}
+
+// eslint-disable-next-line no-var
+var CrComponentsMostVisitedFocusTest =
+    class extends CrComponentsMojoInteractiveTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://new-tab-page/test_loader.html?module=cr_components/most_visited_focus_test.js';
+  }
+};
+
+TEST_F('CrComponentsMostVisitedFocusTest', 'All', function() {
+  mocha.run();
+});
diff --git a/chrome/test/data/webui/new_tab_page/most_visited_focus_test.js b/chrome/test/data/webui/cr_components/most_visited_focus_test.js
similarity index 60%
rename from chrome/test/data/webui/new_tab_page/most_visited_focus_test.js
rename to chrome/test/data/webui/cr_components/most_visited_focus_test.js
index 24192a4..77bfebf 100644
--- a/chrome/test/data/webui/new_tab_page/most_visited_focus_test.js
+++ b/chrome/test/data/webui/cr_components/most_visited_focus_test.js
@@ -2,54 +2,41 @@
 // 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/lazy_load.js';
+import '../mojo_webui_test_support.js';
 
-import {NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
-import {assertFocus, keydown} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
-import {eventToPromise} from 'chrome://test/test_util.m.js';
+import {MostVisitedBrowserProxy} from 'chrome://resources/cr_components/most_visited/browser_proxy.js';
+import {MostVisitedElement} from 'chrome://resources/cr_components/most_visited/most_visited.js';
+import {MostVisitedPageCallbackRouter, MostVisitedPageHandlerRemote} from 'chrome://resources/cr_components/most_visited/most_visited.mojom-webui.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
 
-suite('NewTabPageMostVisitedFocusTest', () => {
+import {TestBrowserProxy} from '../test_browser_proxy.m.js';
+import {eventToPromise} from '../test_util.m.js';
+
+import {$$, assertFocus, keydown} from './most_visited_test_support.js';
+
+suite('CrComponentsMostVisitedFocusTest', () => {
   /** @type {!MostVisitedElement} */
   let mostVisited;
 
-  /**
-   * @implements {newTabPage.mojom.PageHandlerRemote}
-   * @extends {TestBrowserProxy}
-   */
-  let handler;
-
-  /** @type {newTabPage.mojom.PageHandlerRemote} */
+  /** @extends {TestBrowserProxy} */
   let callbackRouterRemote;
 
-  /**
-   * @param {string}
-   * @return {!Array<!HTMLElement>}
-   * @private
-   */
-  function queryAll(q) {
-    return Array.from(mostVisited.shadowRoot.querySelectorAll(q));
-  }
-
-  /**
-   * @return {!Array<!HTMLElement>}
-   * @private
-   */
+  /** @return {!Array<!Element>} */
   function queryTiles() {
-    return queryAll('.tile');
+    return Array.from(mostVisited.shadowRoot.querySelectorAll('.tile'));
   }
 
   /**
    * @param {number} n
-   * @return {!Promise}
-   * @private
+   * @return {!Promise<void>}
    */
   async function addTiles(n) {
     const tiles = Array(n).fill(0).map((x, i) => {
       const char = String.fromCharCode(i + /* 'a' */ 97);
       return {
         title: char,
-        titleDirection: mojoBase.mojom.TextDirection.LEFT_TO_RIGHT,
+        titleDirection: TextDirection.LEFT_TO_RIGHT,
         url: {url: `https://${char}/`},
         source: i,
         titleSource: i,
@@ -66,22 +53,16 @@
     await tilesRendered;
   }
 
-  setup(() => {
-    PolymerTest.clearBody();
+  setup(/** @suppress {checkTypes} */ () => {
+    document.innerHTML = '';
 
-    WindowProxy.setInstance(TestBrowserProxy.fromClass(WindowProxy));
-    WindowProxy.getInstance().setResultMapperFor(
-        'matchMedia', () => ({
-                        addListener() {},
-                        removeListener() {},
-                      }));
-
-    handler = TestBrowserProxy.fromClass(newTabPage.mojom.PageHandlerRemote);
-    const callbackRouter = new newTabPage.mojom.PageCallbackRouter();
-    NewTabPageProxy.setInstance(handler, callbackRouter);
+    const handler = TestBrowserProxy.fromClass(MostVisitedPageHandlerRemote);
+    const callbackRouter = new MostVisitedPageCallbackRouter();
+    MostVisitedBrowserProxy.setInstance(
+        new MostVisitedBrowserProxy(handler, callbackRouter));
     callbackRouterRemote = callbackRouter.$.bindNewPipeAndPassRemote();
 
-    mostVisited = document.createElement('ntp-most-visited');
+    mostVisited = new MostVisitedElement();
     document.body.appendChild(mostVisited);
   });
 
@@ -90,7 +71,7 @@
     const [tile] = queryTiles();
     tile.focus();
     keydown(tile, 'ArrowRight');
-    assertFocus(mostVisited.$.addShortcut);
+    assertFocus($$(mostVisited, '#addShortcut'));
   });
 
   test('right focuses on addShortcut when menu button focused', async () => {
@@ -98,7 +79,7 @@
     const [tile] = queryTiles();
     tile.querySelector('cr-icon-button').focus();
     keydown(tile, 'ArrowRight');
-    assertFocus(mostVisited.$.addShortcut);
+    assertFocus($$(mostVisited, '#addShortcut'));
   });
 
   test('right focuses next tile', async () => {
@@ -122,7 +103,7 @@
     const [tile] = queryTiles();
     tile.focus();
     keydown(tile, 'ArrowDown');
-    assertFocus(mostVisited.$.addShortcut);
+    assertFocus($$(mostVisited, '#addShortcut'));
   });
 
   test('down focuses next tile', async () => {
@@ -135,8 +116,8 @@
 
   test('up focuses on previous tile from addShortcut', async () => {
     await addTiles(1);
-    mostVisited.$.addShortcut.focus();
-    keydown(mostVisited.$.addShortcut, 'ArrowUp');
+    $$(mostVisited, '#addShortcut').focus();
+    keydown($$(mostVisited, '#addShortcut'), 'ArrowUp');
     assertFocus(queryTiles()[0]);
   });
 
@@ -159,10 +140,10 @@
 
   test('up/left/right/down addShortcut and no tiles', async () => {
     await addTiles(0);
-    mostVisited.$.addShortcut.focus();
-    ['ArrowUp', 'ArrowLeft', 'ArrowRight', 'ArrowDown'].forEach(key => {
-      keydown(mostVisited.$.addShortcut, key);
-      assertFocus(mostVisited.$.addShortcut);
-    });
+    $$(mostVisited, '#addShortcut').focus();
+    for (const key of ['ArrowUp', 'ArrowLeft', 'ArrowRight', 'ArrowDown']) {
+      keydown($$(mostVisited, '#addShortcut'), key);
+      assertFocus($$(mostVisited, '#addShortcut'));
+    }
   });
 });
diff --git a/chrome/test/data/webui/new_tab_page/most_visited_test.js b/chrome/test/data/webui/cr_components/most_visited_test.js
similarity index 74%
rename from chrome/test/data/webui/new_tab_page/most_visited_test.js
rename to chrome/test/data/webui/cr_components/most_visited_test.js
index 7443547..2ae30df0 100644
--- a/chrome/test/data/webui/new_tab_page/most_visited_test.js
+++ b/chrome/test/data/webui/cr_components/most_visited_test.js
@@ -2,72 +2,84 @@
 // 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/lazy_load.js';
+import '../mojo_webui_test_support.js';
 
-import {$$, NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import {MostVisitedBrowserProxy} from 'chrome://resources/cr_components/most_visited/browser_proxy.js';
+import {MostVisitedElement} from 'chrome://resources/cr_components/most_visited/most_visited.js';
+import {MostVisitedPageCallbackRouter, MostVisitedPageHandlerRemote} from 'chrome://resources/cr_components/most_visited/most_visited.mojom-webui.js';
+import {MostVisitedWindowProxy} from 'chrome://resources/cr_components/most_visited/window_proxy.js';
 import {isMac} from 'chrome://resources/js/cr.m.js';
-import {assertNotStyle, assertStyle, keydown} from 'chrome://test/new_tab_page/test_support.js';
-import {TestBrowserProxy} from 'chrome://test/test_browser_proxy.m.js';
-import {eventToPromise, flushTasks} from 'chrome://test/test_util.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
+import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
 
-suite('NewTabPageMostVisitedTest', () => {
+import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from '../chai_assert.js';
+import {TestBrowserProxy} from '../test_browser_proxy.m.js';
+import {eventToPromise, flushTasks} from '../test_util.m.js';
+
+import {$$, assertNotStyle, assertStyle, keydown} from './most_visited_test_support.js';
+
+suite('CrComponentsMostVisitedTest', () => {
   /** @type {!MostVisitedElement} */
   let mostVisited;
 
-  /**
-   * @implements {WindowProxy}
-   * @extends {TestBrowserProxy}
-   */
+  /** @extends {TestBrowserProxy} */
   let windowProxy;
 
-  /**
-   * @implements {newTabPage.mojom.PageHandlerRemote}
-   * @extends {TestBrowserProxy}
-   */
+  /** @extends {TestBrowserProxy} */
   let handler;
 
-  /** @type {newTabPage.mojom.PageHandlerRemote} */
+  /** @extends {TestBrowserProxy} */
   let callbackRouterRemote;
 
-  /** @type {!MediaListenerList} */
+  /** @type {!MediaQueryList} */
   let mediaListenerWideWidth;
 
-  /** @type {!MediaListenerList} */
+  /** @type {!MediaQueryList} */
   let mediaListenerMediumWidth;
 
   /** @type {!Function} */
   let mediaListener;
 
   /**
-   * @param {string}
-   * @return {!Array<!HTMLElement>}
-   * @private
+   * @param {string} q
+   * @return {!Array<!Element>}
    */
   function queryAll(q) {
     return Array.from(mostVisited.shadowRoot.querySelectorAll(q));
   }
 
-  /**
-   * @return {!Array<!HTMLElement>}
-   * @private
-   */
+  /** @return {!Array<!Element>} */
   function queryTiles() {
     return queryAll('.tile');
   }
 
+  /** @return {!Array<!Element>} */
+  function queryHiddenTiles() {
+    return queryAll('.tile[hidden]');
+  }
+
+  /** @param {number} length */
+  function assertTileLength(length) {
+    assertEquals(length, queryTiles().length);
+  }
+
+  /** @param {number} length */
+  function assertHiddenTileLength(length) {
+    assertEquals(length, queryHiddenTiles().length);
+  }
+
   /**
    * @param {number|!Array} n
    * @param {boolean=} customLinksEnabled
    * @param {boolean=} visible
-   * @return {!Promise}
-   * @private
+   * @return {!Promise<void>}
    */
   async function addTiles(n, customLinksEnabled = true, visible = true) {
     const tiles = Array.isArray(n) ? n : Array(n).fill(0).map((x, i) => {
       const char = String.fromCharCode(i + /* 'a' */ 97);
       return {
         title: char,
-        titleDirection: mojoBase.mojom.TextDirection.LEFT_TO_RIGHT,
+        titleDirection: TextDirection.LEFT_TO_RIGHT,
         url: {url: `https://${char}/`},
         source: i,
         titleSource: i,
@@ -77,22 +89,20 @@
     });
     const tilesRendered = eventToPromise('dom-change', mostVisited.$.tiles);
     callbackRouterRemote.setMostVisitedInfo({
-      customLinksEnabled: customLinksEnabled,
-      tiles: tiles,
-      visible: visible,
+      customLinksEnabled,
+      tiles,
+      visible,
     });
     await callbackRouterRemote.$.flushForTesting();
     await tilesRendered;
   }
 
-  /** @private */
   function assertAddShortcutHidden() {
-    assertTrue(mostVisited.$.addShortcut.hidden);
+    assertTrue($$(mostVisited, '#addShortcut').hidden);
   }
 
-  /** @private */
   function assertAddShortcutShown() {
-    assertFalse(mostVisited.$.addShortcut.hidden);
+    assertFalse($$(mostVisited, '#addShortcut').hidden);
   }
 
   /**
@@ -112,35 +122,46 @@
   }
 
   function leaveUrlInput() {
-    mostVisited.$.dialogInputUrl.dispatchEvent(new Event('blur'));
+    $$(mostVisited, '#dialogInputUrl').dispatchEvent(new Event('blur'));
   }
 
   suiteSetup(() => {
     loadTimeData.overrideValues({
-      linkRemovedMsg: '',
+      invalidUrl: 'Type a valid URL',
+      linkAddedMsg: 'Shortcut added',
+      linkCantCreate: 'Can\'t create shortcut',
+      linkEditedMsg: 'Shortcut edited',
+      restoreDefaultLinks: 'Restore default shortcuts',
+      shortcutAlreadyExists: 'Shortcut already exists',
     });
   });
 
-  setup(() => {
-    PolymerTest.clearBody();
+  setup(/** @suppress {checkTypes} */ () => {
+    document.innerHTML = '';
 
-    windowProxy = TestBrowserProxy.fromClass(WindowProxy);
-    handler = TestBrowserProxy.fromClass(newTabPage.mojom.PageHandlerRemote);
+    handler = TestBrowserProxy.fromClass(MostVisitedPageHandlerRemote);
+    const callbackRouter = new MostVisitedPageCallbackRouter();
+    MostVisitedBrowserProxy.setInstance(
+        new MostVisitedBrowserProxy(handler, callbackRouter));
+    callbackRouterRemote = callbackRouter.$.bindNewPipeAndPassRemote();
+
     handler.setResultFor('addMostVisitedTile', Promise.resolve({
       success: true,
     }));
     handler.setResultFor('updateMostVisitedTile', Promise.resolve({
       success: true,
     }));
+
+    windowProxy = TestBrowserProxy.fromClass(MostVisitedWindowProxy);
     windowProxy.setResultMapperFor('matchMedia', query => {
-      const mediaListenerList = {
+      const mediaListenerList = /** @type {!MediaQueryList} */ ({
         matches: false,  // Used to determine the screen width.
         media: query,
         addListener(listener) {
           mediaListener = listener;
         },
         removeListener() {},
-      };
+      });
       if (query === '(min-width: 672px)') {
         mediaListenerWideWidth = mediaListenerList;
       } else if (query === '(min-width: 560px)') {
@@ -150,12 +171,11 @@
       }
       return mediaListenerList;
     });
-    WindowProxy.setInstance(windowProxy);
-    const callbackRouter = new newTabPage.mojom.PageCallbackRouter();
-    NewTabPageProxy.setInstance(handler, callbackRouter);
-    callbackRouterRemote = callbackRouter.$.bindNewPipeAndPassRemote();
-    mostVisited = document.createElement('ntp-most-visited');
+    MostVisitedWindowProxy.setInstance(windowProxy);
+
+    mostVisited = new MostVisitedElement();
     document.body.appendChild(mostVisited);
+    assertEquals(1, handler.getCallCount('updateMostVisitedInfo'));
     assertEquals(2, windowProxy.getCallCount('matchMedia'));
     wide();
   });
@@ -168,26 +188,28 @@
   });
 
   test('clicking on add shortcut opens dialog', () => {
-    assertFalse(mostVisited.$.dialog.open);
-    mostVisited.$.addShortcut.click();
-    assertTrue(mostVisited.$.dialog.open);
+    assertFalse($$(mostVisited, '#dialog').open);
+    $$(mostVisited, '#addShortcut').click();
+    assertTrue($$(mostVisited, '#dialog').open);
   });
 
   test('pressing enter when add shortcut has focus opens dialog', () => {
-    mostVisited.$.addShortcut.focus();
-    assertFalse(mostVisited.$.dialog.open);
-    keydown(mostVisited.$.addShortcut, 'Enter');
-    assertTrue(mostVisited.$.dialog.open);
+    $$(mostVisited, '#addShortcut').focus();
+    assertFalse($$(mostVisited, '#dialog').open);
+    keydown($$(mostVisited, '#addShortcut'), 'Enter');
+    assertTrue($$(mostVisited, '#dialog').open);
   });
 
   test('pressing space when add shortcut has focus opens dialog', () => {
-    mostVisited.$.addShortcut.focus();
-    assertFalse(mostVisited.$.dialog.open);
-    mostVisited.$.addShortcut.dispatchEvent(
-        new KeyboardEvent('keydown', {key: ' '}));
-    mostVisited.$.addShortcut.dispatchEvent(
-        new KeyboardEvent('keyup', {key: ' '}));
-    assertTrue(mostVisited.$.dialog.open);
+    $$(mostVisited, '#addShortcut').focus();
+    assertFalse($$(mostVisited, '#dialog').open);
+    $$(mostVisited, '#addShortcut').dispatchEvent(new KeyboardEvent('keydown', {
+      key: ' '
+    }));
+    $$(mostVisited, '#addShortcut').dispatchEvent(new KeyboardEvent('keyup', {
+      key: ' '
+    }));
+    assertTrue($$(mostVisited, '#dialog').open);
   });
 
   test('four tiles fit on one line with addShortcut', async () => {
@@ -285,24 +307,24 @@
     await addTiles(1);
     assertEquals(1, queryTiles().length);
     assertEquals(0, queryAll('.tile[hidden]').length);
-    assertTrue(mostVisited.visible_);
-    assertFalse(mostVisited.$.container.hidden);
+    assertTrue(mostVisited.hasAttribute('visible_'));
+    assertFalse($$(mostVisited, '#container').hidden);
     await addTiles(1, /* customLinksEnabled */ true, /* visible */ false);
     assertEquals(1, queryTiles().length);
     assertEquals(0, queryAll('.tile[hidden]').length);
-    assertFalse(mostVisited.visible_);
-    assertTrue(mostVisited.$.container.hidden);
+    assertFalse(mostVisited.hasAttribute('visible_'));
+    assertTrue($$(mostVisited, '#container').hidden);
     await addTiles(1, /* customLinksEnabled */ true, /* visible */ true);
     assertEquals(1, queryTiles().length);
     assertEquals(0, queryAll('.tile[hidden]').length);
-    assertTrue(mostVisited.visible_);
-    assertFalse(mostVisited.$.container.hidden);
+    assertTrue(mostVisited.hasAttribute('visible_'));
+    assertFalse($$(mostVisited, '#container').hidden);
   });
 
   test('dialog opens when add shortcut clicked', () => {
-    const {dialog} = mostVisited.$;
+    const dialog = $$(mostVisited, '#dialog');
     assertFalse(dialog.open);
-    mostVisited.$.addShortcut.click();
+    $$(mostVisited, '#addShortcut').click();
     assertTrue(dialog.open);
   });
 
@@ -315,6 +337,45 @@
       updateScreenWidth(false, false);
     }
 
+    test('six is max for narrow', async () => {
+      await addTiles(7);
+      medium();
+      assertTileLength(7);
+      assertHiddenTileLength(0);
+      narrow();
+      assertTileLength(7);
+      assertHiddenTileLength(1);
+      medium();
+      assertTileLength(7);
+      assertHiddenTileLength(0);
+    });
+
+    test('eight is max for medium', async () => {
+      await addTiles(8);
+      narrow();
+      assertTileLength(8);
+      assertHiddenTileLength(2);
+      medium();
+      assertTileLength(8);
+      assertHiddenTileLength(0);
+      narrow();
+      assertTileLength(8);
+      assertHiddenTileLength(2);
+    });
+
+    test('eight is max for wide', async () => {
+      await addTiles(8);
+      narrow();
+      assertTileLength(8);
+      assertHiddenTileLength(2);
+      wide();
+      assertTileLength(8);
+      assertHiddenTileLength(0);
+      narrow();
+      assertTileLength(8);
+      assertHiddenTileLength(2);
+    });
+
     test('hide add shortcut if on third row (narrow)', async () => {
       await addTiles(6);
       medium();
@@ -345,24 +406,20 @@
   });
 
   suite('add dialog', () => {
-    /** @private {CrDialogElement} */
     let dialog;
-    /** @private {CrInputElement} */
     let inputName;
-    /** @private {CrInputElement} */
     let inputUrl;
-    /** @private {CrButtonElement} */
     let saveButton;
-    /** @private {CrButtonElement} */
     let cancelButton;
 
     setup(() => {
-      dialog = mostVisited.$.dialog;
-      inputName = mostVisited.$.dialogInputName;
-      inputUrl = mostVisited.$.dialogInputUrl;
+      dialog = $$(mostVisited, '#dialog');
+      inputName = $$(mostVisited, '#dialogInputName');
+      inputUrl = $$(mostVisited, '#dialogInputUrl');
       saveButton = dialog.querySelector('.action-button');
       cancelButton = dialog.querySelector('.cancel-button');
-      mostVisited.$.addShortcut.click();
+
+      $$(mostVisited, '#addShortcut').click();
     });
 
     test('inputs are initially empty', () => {
@@ -372,19 +429,14 @@
 
     test('saveButton is enabled with URL is not empty', () => {
       assertTrue(saveButton.disabled);
-
       inputName.value = 'name';
       assertTrue(saveButton.disabled);
-
       inputUrl.value = 'url';
       assertFalse(saveButton.disabled);
-
       inputUrl.value = '';
       assertTrue(saveButton.disabled);
-
       inputUrl.value = 'url';
       assertFalse(saveButton.disabled);
-
       inputUrl.value = '                                \n\n\n        ';
       assertTrue(saveButton.disabled);
     });
@@ -399,7 +451,7 @@
       inputName.value = 'name';
       inputUrl.value = 'url';
       cancelButton.click();
-      mostVisited.$.addShortcut.click();
+      $$(mostVisited, '#addShortcut').click();
       assertEquals('', inputName.value);
       assertEquals('', inputUrl.value);
     });
@@ -414,11 +466,11 @@
 
     test('toast shown on save', async () => {
       inputUrl.value = 'url';
-      assertFalse(mostVisited.$.toast.open);
+      assertFalse($$(mostVisited, '#toast').open);
       const addCalled = handler.whenCalled('addMostVisitedTile');
       saveButton.click();
       await addCalled;
-      assertTrue(mostVisited.$.toast.open);
+      assertTrue($$(mostVisited, '#toast').open);
     });
 
     test('toast has undo buttons when action successful', async () => {
@@ -524,46 +576,41 @@
 
   test('open edit dialog', async () => {
     await addTiles(2);
-    const {actionMenu, dialog} = mostVisited.$;
+    const actionMenu = $$(mostVisited, '#actionMenu');
+    const dialog = $$(mostVisited, '#dialog');
     assertFalse(actionMenu.open);
     queryTiles()[0].querySelector('#actionMenuButton').click();
     assertTrue(actionMenu.open);
     assertFalse(dialog.open);
-    mostVisited.$.actionMenuEdit.click();
+    $$(mostVisited, '#actionMenuEdit').click();
     assertFalse(actionMenu.open);
     assertTrue(dialog.open);
   });
 
   suite('edit dialog', () => {
-    /** @private {CrActionMenuElement} */
     let actionMenu;
-    /** @private {CrIconButtonElement} */
     let actionMenuButton;
-    /** @private {CrDialogElement} */
     let dialog;
-    /** @private {CrInputElement} */
     let inputName;
-    /** @private {CrInputElement} */
     let inputUrl;
-    /** @private {CrButtonElement} */
     let saveButton;
-    /** @private {CrButtonElement} */
     let cancelButton;
-    /** @private {HTMLElement} */
     let tile;
 
     setup(async () => {
-      actionMenu = mostVisited.$.actionMenu;
-      dialog = mostVisited.$.dialog;
-      inputName = mostVisited.$.dialogInputName;
-      inputUrl = mostVisited.$.dialogInputUrl;
+      actionMenu = $$(mostVisited, '#actionMenu');
+      dialog = $$(mostVisited, '#dialog');
+      inputName = $$(mostVisited, '#dialogInputName');
+      inputUrl = $$(mostVisited, '#dialogInputUrl');
       saveButton = dialog.querySelector('.action-button');
       cancelButton = dialog.querySelector('.cancel-button');
+
       await addTiles(2);
       tile = queryTiles()[1];
-      actionMenuButton = tile.querySelector('#actionMenuButton');
+      actionMenuButton = /**@type{CrActionMenuElement}*/ (
+          tile.querySelector('#actionMenuButton'));
       actionMenuButton.click();
-      mostVisited.$.actionMenuEdit.click();
+      $$(mostVisited, '#actionMenuEdit').click();
     });
 
     test('edit a tile URL', async () => {
@@ -577,17 +624,17 @@
 
     test('toast shown when tile editted', async () => {
       inputUrl.value = 'updated-url';
-      assertFalse(mostVisited.$.toast.open);
+      assertFalse($$(mostVisited, '#toast').open);
       saveButton.click();
       await handler.whenCalled('updateMostVisitedTile');
-      assertTrue(mostVisited.$.toast.open);
+      assertTrue($$(mostVisited, '#toast').open);
     });
 
     test('no toast when not editted', async () => {
-      assertFalse(mostVisited.$.toast.open);
+      assertFalse($$(mostVisited, '#toast').open);
       saveButton.click();
       await flushTasks();
-      assertFalse(mostVisited.$.toast.open);
+      assertFalse($$(mostVisited, '#toast').open);
     });
 
     test('edit a tile title', async () => {
@@ -604,13 +651,11 @@
       // url has changed.
       const updateCalled = handler.whenCalled('updateMostVisitedTile');
       saveButton.click();
-
       // Reopen dialog and edit URL.
       actionMenuButton.click();
-      mostVisited.$.actionMenuEdit.click();
+      $$(mostVisited, '#actionMenuEdit').click();
       inputUrl.value = 'updated-url';
       saveButton.click();
-
       const [url, newUrl, newTitle] = await updateCalled;
       assertEquals('https://updated-url/', newUrl.url);
     });
@@ -631,30 +676,31 @@
   });
 
   test('remove with action menu', async () => {
-    const {actionMenu, actionMenuRemove: removeButton} = mostVisited.$;
+    const actionMenu = $$(mostVisited, '#actionMenu');
+    const removeButton = $$(mostVisited, '#actionMenuRemove');
     await addTiles(2);
     const secondTile = queryTiles()[1];
     const actionMenuButton = secondTile.querySelector('#actionMenuButton');
-
     assertFalse(actionMenu.open);
     actionMenuButton.click();
     assertTrue(actionMenu.open);
     const deleteCalled = handler.whenCalled('deleteMostVisitedTile');
-    assertFalse(mostVisited.$.toast.open);
+    assertFalse($$(mostVisited, '#toast').open);
     removeButton.click();
     assertFalse(actionMenu.open);
     assertEquals('https://b/', (await deleteCalled).url);
-    assertTrue(mostVisited.$.toast.open);
+    assertTrue($$(mostVisited, '#toast').open);
     // Toast buttons are visible.
     assertTrue(!!$$(mostVisited, '#undo'));
     assertTrue(!!$$(mostVisited, '#restore'));
   });
 
   test('remove query with action menu', async () => {
-    const {actionMenu, actionMenuRemove: removeButton} = mostVisited.$;
+    const actionMenu = $$(mostVisited, '#actionMenu');
+    const removeButton = $$(mostVisited, '#actionMenuRemove');
     await addTiles([{
       title: 'title',
-      titleDirection: mojoBase.mojom.TextDirection.LEFT_TO_RIGHT,
+      titleDirection: TextDirection.LEFT_TO_RIGHT,
       url: {url: 'https://search-url/'},
       source: 0,
       titleSource: 0,
@@ -662,15 +708,14 @@
       dataGenerationTime: {internalValue: BigInt(0)},
     }]);
     const actionMenuButton = queryTiles()[0].querySelector('#actionMenuButton');
-
     assertFalse(actionMenu.open);
     actionMenuButton.click();
     assertTrue(actionMenu.open);
     const deleteCalled = handler.whenCalled('deleteMostVisitedTile');
-    assertFalse(mostVisited.$.toast.open);
+    assertFalse($$(mostVisited, '#toast').open);
     removeButton.click();
     assertEquals('https://search-url/', (await deleteCalled).url);
-    assertTrue(mostVisited.$.toast.open);
+    assertTrue($$(mostVisited, '#toast').open);
     // Toast buttons are visible.
     assertTrue(!!$$(mostVisited, '#undo'));
     assertTrue(!!$$(mostVisited, '#restore'));
@@ -680,10 +725,10 @@
     await addTiles(1, /* customLinksEnabled */ false);
     const removeButton = queryTiles()[0].querySelector('#removeButton');
     const deleteCalled = handler.whenCalled('deleteMostVisitedTile');
-    assertFalse(mostVisited.$.toast.open);
+    assertFalse($$(mostVisited, '#toast').open);
     removeButton.click();
     assertEquals('https://a/', (await deleteCalled).url);
-    assertTrue(mostVisited.$.toast.open);
+    assertTrue($$(mostVisited, '#toast').open);
     // Toast buttons are visible.
     assertTrue(!!$$(mostVisited, '#undo'));
     assertTrue(!!$$(mostVisited, '#restore'));
@@ -693,7 +738,7 @@
     await addTiles(
         [{
           title: 'title',
-          titleDirection: mojoBase.mojom.TextDirection.LEFT_TO_RIGHT,
+          titleDirection: TextDirection.LEFT_TO_RIGHT,
           url: {url: 'https://search-url/'},
           source: 0,
           titleSource: 0,
@@ -703,10 +748,10 @@
         /* customLinksEnabled */ false);
     const removeButton = queryTiles()[0].querySelector('#removeButton');
     const deleteCalled = handler.whenCalled('deleteMostVisitedTile');
-    assertFalse(mostVisited.$.toast.open);
+    assertFalse($$(mostVisited, '#toast').open);
     removeButton.click();
     assertEquals('https://search-url/', (await deleteCalled).url);
-    assertTrue(mostVisited.$.toast.open);
+    assertTrue($$(mostVisited, '#toast').open);
     // Toast buttons are not visible.
     assertFalse(!!$$(mostVisited, '#undo'));
     assertFalse(!!$$(mostVisited, '#restore'));
@@ -722,18 +767,23 @@
     await addTiles(1);
     const [tile] = queryTiles();
     const deleteCalled = handler.whenCalled('deleteMostVisitedTile');
-    assertFalse(mostVisited.$.toast.open);
+    assertFalse($$(mostVisited, '#toast').open);
     keydown(tile, 'Delete');
     assertEquals('https://a/', (await deleteCalled).url);
-    assertTrue(mostVisited.$.toast.open);
+    assertTrue($$(mostVisited, '#toast').open);
   });
 
   test('ctrl+z triggers undo and hides toast', async () => {
-    const {toast} = mostVisited.$;
+    const toast = $$(mostVisited, '#toast');
     assertFalse(toast.open);
-    mostVisited.toast_('linkRemovedMsg', /* showButtons= */ true);
-    await flushTasks();
+
+    // Add a tile and remove it to show the toast.
+    await addTiles(1);
+    const [tile] = queryTiles();
+    keydown(tile, 'Delete');
+    await handler.whenCalled('deleteMostVisitedTile');
     assertTrue(toast.open);
+
     const undoCalled = handler.whenCalled('undoMostVisitedTileAction');
     mostVisited.dispatchEvent(new KeyboardEvent('keydown', {
       bubbles: true,
@@ -746,10 +796,21 @@
   });
 
   test('ctrl+z does nothing if toast buttons are not showing', async () => {
-    const {toast} = mostVisited.$;
+    const toast = $$(mostVisited, '#toast');
     assertFalse(toast.open);
-    mostVisited.toast_('linkRemovedMsg', /* showButtons= */ false);
-    await flushTasks();
+
+    // A failed attempt at adding a shortcut to show the toast with no buttons.
+    handler.setResultFor('addMostVisitedTile', Promise.resolve({
+      success: false,
+    }));
+    $$(mostVisited, '#addShortcut').click();
+    const dialog = $$(mostVisited, '#dialog');
+    const inputUrl = $$(mostVisited, '#dialogInputUrl');
+    inputUrl.value = 'url';
+    const saveButton = dialog.querySelector('.action-button');
+    saveButton.click();
+    await handler.whenCalled('addMostVisitedTile');
+
     assertTrue(toast.open);
     mostVisited.dispatchEvent(new KeyboardEvent('keydown', {
       bubbles: true,
@@ -763,10 +824,15 @@
 
   test('toast restore defaults button', async () => {
     const wait = handler.whenCalled('restoreMostVisitedDefaults');
-    const {toast} = mostVisited.$;
+    const toast = $$(mostVisited, '#toast');
     assertFalse(toast.open);
-    mostVisited.toast_('linkRemovedMsg', /* showButtons= */ true);
-    await flushTasks();
+
+    // Add a tile and remove it to show the toast.
+    await addTiles(1);
+    const [tile] = queryTiles();
+    keydown(tile, 'Delete');
+    await handler.whenCalled('deleteMostVisitedTile');
+
     assertTrue(toast.open);
     toast.querySelector('#restore').click();
     await wait;
@@ -775,10 +841,15 @@
 
   test('toast undo button', async () => {
     const wait = handler.whenCalled('undoMostVisitedTileAction');
-    const {toast} = mostVisited.$;
+    const toast = $$(mostVisited, '#toast');
     assertFalse(toast.open);
-    mostVisited.toast_('linkRemovedMsg', /* showButtons= */ true);
-    await flushTasks();
+
+    // Add a tile and remove it to show the toast.
+    await addTiles(1);
+    const [tile] = queryTiles();
+    keydown(tile, 'Delete');
+    await handler.whenCalled('deleteMostVisitedTile');
+
     assertTrue(toast.open);
     toast.querySelector('#undo').click();
     await wait;
@@ -846,7 +917,6 @@
     assertTrue(first.draggable);
     assertEquals('https://b/', second.href);
     assertTrue(second.draggable);
-
     const firstRect = first.getBoundingClientRect();
     const secondRect = second.getBoundingClientRect();
     first.dispatchEvent(new DragEvent('dragstart', {
@@ -867,7 +937,7 @@
   test('RIGHT_TO_LEFT tile title text direction', async () => {
     await addTiles([{
       title: 'title',
-      titleDirection: mojoBase.mojom.TextDirection.RIGHT_TO_LEFT,
+      titleDirection: TextDirection.RIGHT_TO_LEFT,
       url: {url: 'https://url/'},
       source: 0,
       titleSource: 0,
@@ -882,7 +952,7 @@
   test('LEFT_TO_RIGHT tile title text direction', async () => {
     await addTiles([{
       title: 'title',
-      titleDirection: mojoBase.mojom.TextDirection.LEFT_TO_RIGHT,
+      titleDirection: TextDirection.LEFT_TO_RIGHT,
       url: {url: 'https://url/'},
       source: 0,
       titleSource: 0,
@@ -896,9 +966,10 @@
 
   test('setting color styles tile color', () => {
     // Act.
-    mostVisited.style.setProperty('--ntp-theme-text-color', 'blue');
-    mostVisited.style.setProperty(
-        '--ntp-theme-shortcut-background-color', 'red');
+    $$(mostVisited, '#container')
+        .style.setProperty('--most-visited-text-color', 'blue');
+    $$(mostVisited, '#container')
+        .style.setProperty('--tile-background-color', 'red');
 
     // Assert.
     queryAll('.tile-title').forEach(tile => {
@@ -911,22 +982,23 @@
 
   test('add shortcut white', () => {
     assertStyle(
-        mostVisited.$.addShortcutIcon, 'background-color', 'rgb(32, 33, 36)');
-    mostVisited.toggleAttribute('use-white-add-icon', true);
+        $$(mostVisited, '#addShortcutIcon'), 'background-color',
+        'rgb(32, 33, 36)');
+    mostVisited.toggleAttribute('use-white-tile-icon_', true);
     assertStyle(
-        mostVisited.$.addShortcutIcon, 'background-color',
+        $$(mostVisited, '#addShortcutIcon'), 'background-color',
         'rgb(255, 255, 255)');
   });
 
   test('add title pill', () => {
-    mostVisited.style.setProperty('--ntp-theme-text-shadow', '1px 2px');
+    mostVisited.style.setProperty('--most-visited-text-shadow', '1px 2px');
     queryAll('.tile-title').forEach(tile => {
       assertStyle(tile, 'background-color', 'rgba(0, 0, 0, 0)');
     });
     queryAll('.tile-title span').forEach(tile => {
       assertNotStyle(tile, 'text-shadow', 'none');
     });
-    mostVisited.toggleAttribute('use-title-pill', true);
+    mostVisited.toggleAttribute('use-title-pill_', true);
     queryAll('.tile-title').forEach(tile => {
       assertStyle(tile, 'background-color', 'rgb(255, 255, 255)');
     });
@@ -950,7 +1022,7 @@
     assertEquals(tiles.length, 2);
     assertDeepEquals(tiles[0], {
       title: 'a',
-      titleDirection: mojoBase.mojom.TextDirection.LEFT_TO_RIGHT,
+      titleDirection: TextDirection.LEFT_TO_RIGHT,
       url: {url: 'https://a/'},
       source: 0,
       titleSource: 0,
@@ -959,7 +1031,7 @@
     });
     assertDeepEquals(tiles[1], {
       title: 'b',
-      titleDirection: mojoBase.mojom.TextDirection.LEFT_TO_RIGHT,
+      titleDirection: TextDirection.LEFT_TO_RIGHT,
       url: {url: 'https://b/'},
       source: 1,
       titleSource: 1,
@@ -984,7 +1056,7 @@
     assertEquals(index, 0);
     assertDeepEquals(tile, {
       title: 'a',
-      titleDirection: mojoBase.mojom.TextDirection.LEFT_TO_RIGHT,
+      titleDirection: TextDirection.LEFT_TO_RIGHT,
       url: {url: 'https://a/'},
       source: 0,
       titleSource: 0,
diff --git a/chrome/test/data/webui/cr_components/most_visited_test_support.js b/chrome/test/data/webui/cr_components/most_visited_test_support.js
new file mode 100644
index 0000000..cacd0ce
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/most_visited_test_support.js
@@ -0,0 +1,57 @@
+// Copyright 2021 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/strings.m.js';
+
+import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
+import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
+
+import {assertEquals, assertNotEquals} from '../chai_assert.js';
+
+/**
+ * @param {!Element} element
+ * @param {string} query
+ * @return {!Element}
+ */
+export function $$(element, query) {
+  return element.shadowRoot.querySelector(query);
+}
+
+/**
+ * @param {!Element} element
+ * @param {string} key
+ */
+export function keydown(element, key) {
+  keyDownOn(element, '', [], key);
+}
+
+/**
+ * Asserts the computed style value for an element.
+ * @param {!Element} element The element.
+ * @param {string} name The name of the style to assert.
+ * @param {string} expected The expected style value.
+ */
+export function assertStyle(element, name, expected) {
+  const actual = window.getComputedStyle(element).getPropertyValue(name).trim();
+  assertEquals(expected, actual);
+}
+
+/**
+ * Asserts the computed style for an element is not value.
+ * @param {!Element} element The element.
+ * @param {string} name The name of the style to assert.
+ * @param {string} not The value the style should not be.
+ */
+export function assertNotStyle(element, name, not) {
+  const actual = window.getComputedStyle(element).getPropertyValue(name).trim();
+  assertNotEquals(not, actual);
+}
+
+/**
+ * Asserts that an element is focused.
+ * @param {!Element} element The element to test.
+ */
+export function assertFocus(element) {
+  assertEquals(element, getDeepActiveElement());
+}
diff --git a/chrome/test/data/webui/new_tab_page/app_test.js b/chrome/test/data/webui/new_tab_page/app_test.js
index 974d960..2708ead 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.js
+++ b/chrome/test/data/webui/new_tab_page/app_test.js
@@ -53,6 +53,10 @@
 
     windowProxy = TestBrowserProxy.fromClass(WindowProxy);
     handler = TestBrowserProxy.fromClass(newTabPage.mojom.PageHandlerRemote);
+    handler.setResultFor('getMostVisitedSettings', Promise.resolve({
+      customLinksEnabled: false,
+      shortcutsVisible: false,
+    }));
     handler.setResultFor('getBackgroundCollections', Promise.resolve({
       collections: [],
     }));
@@ -125,9 +129,6 @@
         0xffff0000 /* red */,
         (await backgroundManager.whenCalled('setBackgroundColor')).value);
     assertStyle(
-        $$(app, '#content'), '--ntp-theme-shortcut-background-color',
-        'rgba(0, 255, 0, 1)');
-    assertStyle(
         $$(app, '#content'), '--ntp-theme-text-color', 'rgba(0, 0, 255, 1)');
     assertEquals(1, backgroundManager.getCallCount('setShowBackgroundImage'));
     assertFalse(await backgroundManager.whenCalled('setShowBackgroundImage'));
@@ -372,22 +373,32 @@
 
   test('theme updates add shortcut color', async () => {
     const theme = createTheme();
-    theme.shortcutUseWhiteAddIcon = true;
+    theme.mostVisited.useWhiteTileIcon = true;
     callbackRouterRemote.setTheme(theme);
     const mostVisited = $$(app, '#mostVisited');
-    assertFalse(mostVisited.hasAttribute('use-white-add-icon'));
+    assertFalse(mostVisited.hasAttribute('use-white-tile-icon_'));
     await callbackRouterRemote.$.flushForTesting();
-    assertTrue(mostVisited.hasAttribute('use-white-add-icon'));
+    assertTrue(mostVisited.hasAttribute('use-white-tile-icon_'));
   });
 
   test('theme updates use title pill', async () => {
     const theme = createTheme();
-    theme.shortcutUseTitlePill = true;
+    theme.mostVisited.useTitlePill = true;
     callbackRouterRemote.setTheme(theme);
     const mostVisited = $$(app, '#mostVisited');
-    assertFalse(mostVisited.hasAttribute('use-title-pill'));
+    assertFalse(mostVisited.hasAttribute('use-title-pill_'));
     await callbackRouterRemote.$.flushForTesting();
-    assertTrue(mostVisited.hasAttribute('use-title-pill'));
+    assertTrue(mostVisited.hasAttribute('use-title-pill_'));
+  });
+
+  test('theme updates is dark', async () => {
+    const theme = createTheme();
+    theme.mostVisited.isDark = true;
+    callbackRouterRemote.setTheme(theme);
+    const mostVisited = $$(app, '#mostVisited');
+    assertFalse(mostVisited.hasAttribute('is-dark_'));
+    await callbackRouterRemote.$.flushForTesting();
+    assertTrue(mostVisited.hasAttribute('is-dark_'));
   });
 
   test('can show promo with browser command', async () => {
@@ -468,7 +479,7 @@
     [['#content', NtpElement.kBackground],
      ['ntp-logo', NtpElement.kLogo],
      ['ntp-realbox', NtpElement.kRealbox],
-     ['ntp-most-visited', NtpElement.kMostVisited],
+     ['cr-most-visited', NtpElement.kMostVisited],
      ['ntp-middle-slot-promo', NtpElement.kMiddleSlotPromo],
      ['ntp-modules', NtpElement.kModule],
     ].forEach(([selector, element]) => {
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 8dfb647..f219ba2b 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
@@ -22,6 +22,10 @@
     PolymerTest.clearBody();
 
     handler = TestBrowserProxy.fromClass(newTabPage.mojom.PageHandlerRemote);
+    handler.setResultFor('getMostVisitedSettings', Promise.resolve({
+      customLinksEnabled: false,
+      shortcutsVisible: false,
+    }));
     handler.setResultFor('getBackgroundCollections', Promise.resolve({
       collections: [],
     }));
diff --git a/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.js b/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.js
index 5bb9380..46bc8fd 100644
--- a/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.js
+++ b/chrome/test/data/webui/new_tab_page/customize_shortcuts_test.js
@@ -16,25 +16,12 @@
    */
   let handler;
 
-  /** @type {newTabPage.mojom.PageHandlerRemote} */
-  let callbackRouterRemote;
-
   setup(() => {
     PolymerTest.clearBody();
 
     handler = TestBrowserProxy.fromClass(newTabPage.mojom.PageHandlerRemote);
     const callbackRouter = new newTabPage.mojom.PageCallbackRouter();
-    handler.setResultFor('addMostVisitedTile', Promise.resolve({
-      success: true,
-    }));
-    handler.setResultFor('updateMostVisitedTile', Promise.resolve({
-      success: true,
-    }));
     NewTabPageProxy.setInstance(handler, callbackRouter);
-    callbackRouterRemote = callbackRouter.$.bindNewPipeAndPassRemote();
-
-    customizeShortcuts = document.createElement('ntp-customize-shortcuts');
-    document.body.appendChild(customizeShortcuts);
   });
 
   /**
@@ -43,13 +30,14 @@
    * @return {!Promise}
    * @private
    */
-  async function setInitialSettings(customLinksEnabled, visible) {
-    callbackRouterRemote.setMostVisitedInfo({
-      customLinksEnabled: customLinksEnabled,
-      tiles: [],
-      visible: visible,
-    });
-    await callbackRouterRemote.$.flushForTesting();
+  async function setInitialSettings(customLinksEnabled, shortcutsVisible) {
+    handler.setResultFor('getMostVisitedSettings', Promise.resolve({
+      customLinksEnabled,
+      shortcutsVisible,
+    }));
+    customizeShortcuts = document.createElement('ntp-customize-shortcuts');
+    document.body.appendChild(customizeShortcuts);
+    await handler.whenCalled('getMostVisitedSettings');
   }
 
   /**
@@ -97,7 +85,9 @@
         /* hidden= */ true);
   }
 
-  test('selections are mutually exclusive', () => {
+  test('selections are mutually exclusive', async () => {
+    await setInitialSettings(
+        /* customLinksEnabled= */ true, /* shortcutsVisible= */ false);
     assertIsSelected(false, customizeShortcuts.$.optionCustomLinks);
     customizeShortcuts.$.optionCustomLinksButton.click();
     assertCustomLinksEnabled();
@@ -111,8 +101,7 @@
 
   test('enable custom links calls setMostVisitedSettings', async () => {
     await setInitialSettings(
-        /* customLinksEnabled= */ false,
-        /* visible= */ false);
+        /* customLinksEnabled= */ false, /* shortcutsVisible= */ false);
     assertHidden();
     customizeShortcuts.$.optionCustomLinksButton.click();
     const setSettingsCalled = handler.whenCalled('setMostVisitedSettings');
@@ -124,8 +113,7 @@
 
   test('use most-visited calls setMostVisitedSettings', async () => {
     await setInitialSettings(
-        /* customLinksEnabled= */ true,
-        /* visible= */ false);
+        /* customLinksEnabled= */ true, /* shortcutsVisible= */ false);
     assertHidden();
     customizeShortcuts.$.optionMostVisitedButton.click();
     const setSettingsCalled = handler.whenCalled('setMostVisitedSettings');
@@ -137,8 +125,7 @@
 
   test('toggle hide calls setMostVisitedSettings', async () => {
     await setInitialSettings(
-        /* customLinksEnabled= */ true,
-        /* visible= */ true);
+        /* customLinksEnabled= */ true, /* shortcutsVisible= */ true);
     assertCustomLinksEnabled();
     customizeShortcuts.$.hideToggle.click();
     const setSettingsCalled = handler.whenCalled('setMostVisitedSettings');
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 e72a258b..c121eaac 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
@@ -28,18 +28,6 @@
 });
 
 // eslint-disable-next-line no-var
-var NewTabPageMostVisitedTest = class extends NewTabPageBrowserTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/most_visited_test.js';
-  }
-};
-
-TEST_F('NewTabPageMostVisitedTest', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
 var NewTabPageCustomizeDialogTest = 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 1a85d227..cccb6fd 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
@@ -16,18 +16,6 @@
 }
 
 // eslint-disable-next-line no-var
-var NewTabPageMostVisitedFocusTest = class extends NewTabPageInteractiveTest {
-  /** @override */
-  get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=new_tab_page/most_visited_focus_test.js';
-  }
-};
-
-TEST_F('NewTabPageMostVisitedFocusTest', 'All', function() {
-  mocha.run();
-});
-
-// eslint-disable-next-line no-var
 var NewTabPageCustomizeDialogFocusTest =
     class extends NewTabPageInteractiveTest {
   /** @override */
diff --git a/chrome/test/data/webui/new_tab_page/test_support.js b/chrome/test/data/webui/new_tab_page/test_support.js
index 9f332cc..34e3f66d 100644
--- a/chrome/test/data/webui/new_tab_page/test_support.js
+++ b/chrome/test/data/webui/new_tab_page/test_support.js
@@ -73,6 +73,12 @@
 
 /** @return {!newTabPage.mojom.Theme} */
 export function createTheme() {
+  const mostVisited = {
+    backgroundColor: {value: 0xff00ff00},
+    isDark: false,
+    useTitlePill: false,
+    useWhiteTileIcon: false,
+  };
   const searchBox = {
     bg: {value: 0xff000000},
     icon: {value: 0xff000001},
@@ -91,18 +97,16 @@
   };
   return {
     backgroundColor: {value: 0xffff0000},
-    shortcutBackgroundColor: {value: 0xff00ff00},
-    shortcutTextColor: {value: 0xff0000ff},
-    isDefault: true,
-    isDark: false,
-    logoColor: null,
     backgroundImage: null,
     backgroundImageAttribution1: '',
     backgroundImageAttribution2: '',
     backgroundImageAttributionUrl: null,
     dailyRefreshCollectionId: '',
+    isDark: false,
+    isDefault: true,
+    logoColor: null,
+    mostVisited: mostVisited,
     searchBox: searchBox,
-    shortcutUseWhiteAddIcon: false,
-    shortcutUseTitlePill: false,
+    textColor: {value: 0xff0000ff},
   };
 }
diff --git a/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js b/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js
index 0d48bc5..40a4020 100644
--- a/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js
+++ b/chrome/test/data/webui/settings/chromeos/esim_rename_dialog_test.js
@@ -314,4 +314,25 @@
         convertString16ToJSString_(profileProperties.nickname),
         '12345678901234567890');
   });
+
+  test('Done button is disabled when empty input', async function() {
+    eSimManagerRemote.addEuiccForTest(1);
+    addEsimCellularNetwork(TEST_CELLULAR_GUID, '1');
+    await flushAsync();
+    await init();
+
+    const inputBox = esimRenameDialog.$$('#eSimprofileName');
+    const doneBtn = esimRenameDialog.$$('#done');
+    assertTrue(!!inputBox);
+    assertTrue(!!doneBtn);
+
+    inputBox.value = 'test';
+    assertFalse(doneBtn.disabled);
+
+    inputBox.value = '';
+    assertTrue(doneBtn.disabled);
+
+    inputBox.value = 'test2';
+    assertFalse(doneBtn.disabled);
+  });
 });
\ No newline at end of file
diff --git a/chrome/test/payments/payment_request_test_controller_desktop.cc b/chrome/test/payments/payment_request_test_controller_desktop.cc
index d69cc61..aaca9a04 100644
--- a/chrome/test/payments/payment_request_test_controller_desktop.cc
+++ b/chrome/test/payments/payment_request_test_controller_desktop.cc
@@ -261,7 +261,7 @@
          mojo::PendingReceiver<payments::mojom::PaymentRequest> receiver,
          content::RenderFrameHost* render_frame_host) {
         DCHECK(render_frame_host);
-        DCHECK(render_frame_host->IsCurrent());
+        DCHECK(render_frame_host->IsActive());
         auto delegate = std::make_unique<ChromePaymentRequestTestDelegate>(
             render_frame_host, is_off_the_record, valid_ssl, prefs,
             twa_package_name, has_authenticator, observer_for_test);
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index facdcb3..f0f4501a 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-14006.0.0
\ No newline at end of file
+14007.0.0
\ No newline at end of file
diff --git a/chromeos/assistant/tools/send-audio.sh b/chromeos/assistant/tools/send-audio.sh
index d583f2fd..bed12d7 100755
--- a/chromeos/assistant/tools/send-audio.sh
+++ b/chromeos/assistant/tools/send-audio.sh
@@ -8,6 +8,7 @@
 
 TMP=$(mktemp /tmp/audio.XXXXXX)
 MP3_FILE="$TMP.mp3"
+PCM_FILE="$TMP.pcm"
 OUTPUT_FILE="/tmp/fake_audio.pcm"
 
 main() {
@@ -39,6 +40,11 @@
     - When running on Linux
     - When compiled with gn flag 'enable_fake_assistant_microphone'
     - after executing: sudo apt install httpie
+
+If you want to test hotword with this, please also add commandline flag when
+running chrome binary:
+    --assistant-force-default-audio-input
+this will turn on default listening on the fake device.
 END_OF_HELP
 
 }
@@ -50,10 +56,13 @@
     echo "Performing text-to-speech"
     http --download "https://www.google.com/speech-api/v1/synthesize?lang=en&text=$text" --output $MP3_FILE --body
     echo "Converting to $OUTPUT_FILE"
-    ffmpeg -y -i $MP3_FILE -acodec pcm_s16le -f s16le -ar 16000 $OUTPUT_FILE \
+    ffmpeg -y -i $MP3_FILE -acodec pcm_s16le -f s16le -ar 16000 $PCM_FILE \
          -loglevel error -hide_banner
 
     rm $MP3_FILE
+
+    # move file to make sure the operation is atomic
+    mv -f $PCM_FILE $OUTPUT_FILE
 }
 
 
diff --git a/chromeos/services/ime/ime_service.cc b/chromeos/services/ime/ime_service.cc
index 6a3f28af..d5e0b75 100644
--- a/chromeos/services/ime/ime_service.cc
+++ b/chromeos/services/ime/ime_service.cc
@@ -85,6 +85,17 @@
   std::move(callback).Run(bound);
 }
 
+void ImeService::ConnectToInputMethod(
+    const std::string& ime_spec,
+    mojo::PendingReceiver<mojom::InputChannel> to_engine,
+    mojo::PendingRemote<mojom::InputChannel> from_engine,
+    ConnectToInputMethodCallback callback) {
+  input_engine_ = std::make_unique<InputEngine>();
+  bool bound = input_engine_->BindRequest(ime_spec, std::move(to_engine),
+                                          std::move(from_engine), /*extra=*/{});
+  std::move(callback).Run(bound);
+}
+
 const char* ImeService::GetImeBundleDir() {
   return kBundledInputMethodsDirPath;
 }
diff --git a/chromeos/services/ime/ime_service.h b/chromeos/services/ime/ime_service.h
index 8d58c51c..f7141e8e 100644
--- a/chromeos/services/ime/ime_service.h
+++ b/chromeos/services/ime/ime_service.h
@@ -43,6 +43,11 @@
       mojo::PendingRemote<mojom::InputChannel> from_engine,
       const std::vector<uint8_t>& extra,
       ConnectToImeEngineCallback callback) override;
+  void ConnectToInputMethod(
+      const std::string& ime_spec,
+      mojo::PendingReceiver<mojom::InputChannel> to_engine,
+      mojo::PendingRemote<mojom::InputChannel> from_engine,
+      ConnectToInputMethodCallback callback) override;
 
   // ImeCrosPlatform overrides:
   const char* GetImeBundleDir() override;
diff --git a/chromeos/services/ime/ime_service_unittest.cc b/chromeos/services/ime/ime_service_unittest.cc
index 9fad81e..16a26d2 100644
--- a/chromeos/services/ime/ime_service_unittest.cc
+++ b/chromeos/services/ime/ime_service_unittest.cc
@@ -85,9 +85,9 @@
   MockInputChannel test_channel;
   mojo::Remote<mojom::InputChannel> to_engine_remote;
 
-  remote_manager_->ConnectToImeEngine(
+  remote_manager_->ConnectToInputMethod(
       "m17n:ar", to_engine_remote.BindNewPipeAndPassReceiver(),
-      test_channel.CreatePendingRemote(), extra,
+      test_channel.CreatePendingRemote(),
       base::BindOnce(&ConnectCallback, &success));
   remote_manager_.FlushForTesting();
   EXPECT_TRUE(success);
@@ -115,9 +115,9 @@
   MockInputChannel test_channel;
   mojo::Remote<mojom::InputChannel> to_engine_remote;
 
-  remote_manager_->ConnectToImeEngine(
+  remote_manager_->ConnectToInputMethod(
       "m17n:ar", to_engine_remote.BindNewPipeAndPassReceiver(),
-      test_channel.CreatePendingRemote(), extra,
+      test_channel.CreatePendingRemote(),
       base::BindOnce(&ConnectCallback, &success));
   remote_manager_.FlushForTesting();
   EXPECT_TRUE(success);
@@ -145,9 +145,9 @@
   MockInputChannel test_channel;
   mojo::Remote<mojom::InputChannel> to_engine_remote;
 
-  remote_manager_->ConnectToImeEngine(
+  remote_manager_->ConnectToInputMethod(
       "m17n:ar", to_engine_remote.BindNewPipeAndPassReceiver(),
-      test_channel.CreatePendingRemote(), extra,
+      test_channel.CreatePendingRemote(),
       base::BindOnce(&ConnectCallback, &success));
   remote_manager_.FlushForTesting();
   EXPECT_TRUE(success);
@@ -175,9 +175,9 @@
   MockInputChannel test_channel;
   mojo::Remote<mojom::InputChannel> to_engine_remote;
 
-  remote_manager_->ConnectToImeEngine(
+  remote_manager_->ConnectToInputMethod(
       "m17n:ar", to_engine_remote.BindNewPipeAndPassReceiver(),
-      test_channel.CreatePendingRemote(), extra,
+      test_channel.CreatePendingRemote(),
       base::BindOnce(&ConnectCallback, &success));
   remote_manager_.FlushForTesting();
   EXPECT_TRUE(success);
@@ -206,9 +206,9 @@
   MockInputChannel test_channel;
   mojo::Remote<mojom::InputChannel> to_engine_remote;
 
-  remote_manager_->ConnectToImeEngine(
+  remote_manager_->ConnectToInputMethod(
       "m17n:ar", to_engine_remote.BindNewPipeAndPassReceiver(),
-      test_channel.CreatePendingRemote(), extra,
+      test_channel.CreatePendingRemote(),
       base::BindOnce(&ConnectCallback, &success));
   remote_manager_.FlushForTesting();
   EXPECT_TRUE(success);
@@ -277,9 +277,9 @@
   MockInputChannel test_channel;
   mojo::Remote<mojom::InputChannel> to_engine_remote;
 
-  remote_manager_->ConnectToImeEngine(
+  remote_manager_->ConnectToInputMethod(
       "m17n:deva_phone", to_engine_remote.BindNewPipeAndPassReceiver(),
-      test_channel.CreatePendingRemote(), extra,
+      test_channel.CreatePendingRemote(),
       base::BindOnce(&ConnectCallback, &success));
   remote_manager_.FlushForTesting();
   EXPECT_TRUE(success);
@@ -355,9 +355,9 @@
   MockInputChannel test_channel;
   mojo::Remote<mojom::InputChannel> to_engine_remote;
 
-  remote_manager_->ConnectToImeEngine(
+  remote_manager_->ConnectToInputMethod(
       "m17n:deva_phone", to_engine_remote.BindNewPipeAndPassReceiver(),
-      test_channel.CreatePendingRemote(), extra,
+      test_channel.CreatePendingRemote(),
       base::BindOnce(&ConnectCallback, &success));
   remote_manager_.FlushForTesting();
   EXPECT_TRUE(success);
@@ -413,9 +413,9 @@
   MockInputChannel test_channel;
   mojo::Remote<mojom::InputChannel> to_engine_remote;
 
-  remote_manager_->ConnectToImeEngine(
+  remote_manager_->ConnectToInputMethod(
       "m17n:km", to_engine_remote.BindNewPipeAndPassReceiver(),
-      test_channel.CreatePendingRemote(), extra,
+      test_channel.CreatePendingRemote(),
       base::BindOnce(&ConnectCallback, &success));
   remote_manager_.FlushForTesting();
   EXPECT_TRUE(success);
diff --git a/chromeos/services/ime/public/mojom/ime_service.mojom b/chromeos/services/ime/public/mojom/ime_service.mojom
index 17af233..fbca777 100644
--- a/chromeos/services/ime/public/mojom/ime_service.mojom
+++ b/chromeos/services/ime/public/mojom/ime_service.mojom
@@ -33,6 +33,16 @@
                      pending_remote<InputChannel> from_engine,
                      array<uint8> extra)
                      => (bool success);
+
+  // Connects to the native input method identified by |ime_spec|.
+  // On success, return |success| as true, and |to_engine| can be used to
+  // interact with the input method. The interface |from_engine| is implemented
+  // on the client and used to receive data sent from the engine.
+  // On failure (e.g. |ime_spec| is not valid), |success| is false.
+  ConnectToInputMethod(string ime_spec,
+                       pending_receiver<InputChannel> to_engine,
+                       pending_remote<InputChannel> from_engine)
+                       => (bool success);
 };
 
 // Implemented in the browser process, used to perform network requests or
diff --git a/chromeos/services/libassistant/audio/audio_input_impl.cc b/chromeos/services/libassistant/audio/audio_input_impl.cc
index b8fe262..15ecccf 100644
--- a/chromeos/services/libassistant/audio/audio_input_impl.cc
+++ b/chromeos/services/libassistant/audio/audio_input_impl.cc
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/command_line.h"
 #include "base/logging.h"
 #include "base/memory/weak_ptr.h"
 #include "base/metrics/histogram_functions.h"
@@ -381,10 +382,29 @@
 }
 
 void AudioInputImpl::SetDeviceId(const absl::optional<std::string>& device_id) {
-  if (preferred_device_id_ == device_id)
+  DVLOG(1) << "Set audio input preferred_device_id to "
+           << device_id.value_or("<null>");
+  auto new_device_id = device_id;
+
+  constexpr char kAssistantForceDefaultAudioInput[] =
+      "assistant-force-default-audio-input";
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (command_line->HasSwitch(kAssistantForceDefaultAudioInput)) {
+    // Sometimes there may not be a preferred audio device,
+    // e.g. if the device does not have built-in mic and using a bluetooth
+    // microphone, in this case we do not want to open the bluetooth device by
+    // default to drain the battery; also if running linux chromeos chrome
+    // build, there won't be cras and we won't have a device id set. Force using
+    // default audio input in these cases to mimic the common Assistant hotword
+    // behaviors.
+    DVLOG(1) << "Force audio input preferred_device_id to default.";
+    new_device_id = media::AudioDeviceDescription::kDefaultDeviceId;
+  }
+
+  if (preferred_device_id_ == new_device_id)
     return;
 
-  preferred_device_id_ = device_id;
+  preferred_device_id_ = new_device_id;
 
   UpdateRecordingState();
   if (HasOpenAudioStream())
diff --git a/chromeos/services/libassistant/audio/fake_input_device.cc b/chromeos/services/libassistant/audio/fake_input_device.cc
index dc8317b3..bf58a79 100644
--- a/chromeos/services/libassistant/audio/fake_input_device.cc
+++ b/chromeos/services/libassistant/audio/fake_input_device.cc
@@ -28,7 +28,7 @@
 constexpr const char kFakeAudioFile[] = "/tmp/fake_audio.pcm";
 
 std::vector<uint8_t> ReadFileData(base::File* file) {
-  const int file_size = file->GetLength();
+  const std::vector<uint8_t>::size_type file_size = file->GetLength();
   std::vector<uint8_t> result(file_size);
 
   bool success = file->ReadAtCurrentPosAndCheck(result);
@@ -48,60 +48,55 @@
 }  // namespace
 
 // A fake audio input device (also known as a microphone).
-// This fake device will wait until the |audio_file| exists,
+// This fake device will wait until the `kFakeAudioFile` exists,
 // and it will then forward its data as microphone input.
-// Finally it will remove |audio_file| (so we do not keep responding the
+// Finally it will remove `kFakeAudioFile` (so we do not keep responding the
 // same thing over and over again).
-class FakeInputDevice : public media::AudioCapturerSource {
+class FakeInputDevice {
  public:
-  explicit FakeInputDevice(const std::string& audio_file)
-      : audio_file_(audio_file) {}
+  FakeInputDevice() = default;
+  ~FakeInputDevice() = default;
 
   // AudioCapturerSource implementation.
   void Initialize(const media::AudioParameters& params,
-                  CaptureCallback* callback) override {
+                  media::AudioCapturerSource::CaptureCallback* callback) {
     audio_parameters_ = params;
     callback_ = callback;
   }
 
-  void Start() override {
+  void Start() {
     LOG(INFO) << "Starting fake input device";
     PostDelayedTask(FROM_HERE,
-                    base::BindOnce(&FakeInputDevice::WaitForAudioFile, this),
+                    base::BindOnce(&FakeInputDevice::WaitForAudioFile,
+                                   weak_factory_.GetWeakPtr()),
                     base::TimeDelta::FromMilliseconds(100));
   }
 
-  void Stop() override {
+  void Stop() {
     LOG(INFO) << "Stopping fake input device";
-    task_runner_.reset();
+    callback_ = nullptr;
   }
-  void SetVolume(double volume) override {}
-  void SetAutomaticGainControl(bool enabled) override {}
-  void SetOutputDeviceForAec(const std::string& output_device_id) override {}
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner() {
+    return task_runner_;
+  }
 
  private:
-  ~FakeInputDevice() override = default;
-
   void WaitForAudioFile() {
     DCHECK(RunsTasksInCurrentSequence(task_runner_));
 
-    base::FilePath path{audio_file_};
+    base::FilePath path{kFakeAudioFile};
     base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
                               base::File::FLAG_DELETE_ON_CLOSE);
     if (!file.IsValid()) {
-      LOG(ERROR)
-          << "" << audio_file_
-          << " not found. Please run chromeos/assistant/tools/send-audio.sh";
-
-      PostDelayedTask(FROM_HERE,
-                      base::BindOnce(&FakeInputDevice::WaitForAudioFile, this),
-                      base::TimeDelta::FromSeconds(1));
+      SendSilence();
       return;
     }
 
-    LOG(INFO) << "Opening audio file " << audio_file_;
+    LOG(INFO) << "Opening audio file " << kFakeAudioFile;
     ReadAudioFile(&file);
-    SendSilence();
+    file.Close();
+    SendAudio();
   }
 
   void ReadAudioFile(base::File* file) {
@@ -119,20 +114,34 @@
     std::vector<uint8_t> data = ReadFileData(file);
 
     // Convert it to a list of blocks of the requested size.
-    media::AudioBlockFifo audio_blocks(audio_parameters_.channels(),
-                                       audio_parameters_.frames_per_buffer(),
-                                       blocks_count);
-    audio_blocks.Push(data.data(), frame_count, bytes_per_frame);
+    audio_blocks_ = std::make_unique<media::AudioBlockFifo>(
+        audio_parameters_.channels(), audio_parameters_.frames_per_buffer(),
+        blocks_count);
+    audio_blocks_->Push(data.data(), frame_count, bytes_per_frame);
     // Add silence so the last block is also complete.
-    audio_blocks.PushSilence(audio_blocks.GetUnfilledFrames());
+    audio_blocks_->PushSilence(audio_blocks_->GetUnfilledFrames());
+  }
 
+  void SendAudio() {
     // Send the blocks to the callback
-    while (audio_blocks.available_blocks()) {
-      const media::AudioBus* block = audio_blocks.Consume();
-      const base::TimeTicks time = base::TimeTicks::Now();
-      callback_->Capture(block, time, /*volume=*/0.5,
-                         /*key_pressed=*/false);
+    if (audio_blocks_->available_blocks() <= 0) {
+      audio_blocks_.reset();
+      SendSilence();
+      return;
     }
+
+    const media::AudioBus* block = audio_blocks_->Consume();
+    auto delay_in_microseconds =
+        audio_parameters_.GetMicrosecondsPerFrame() * block->frames();
+    PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&FakeInputDevice::SendAudio, weak_factory_.GetWeakPtr()),
+        base::TimeDelta::FromMicrosecondsD(delay_in_microseconds));
+
+    DVLOG(2) << "Send " << block->frames() << " audio frames";
+    const base::TimeTicks time = base::TimeTicks::Now();
+    if (callback_)
+      callback_->Capture(block, time, /*volume=*/0.5, /*key_pressed=*/false);
   }
 
   // LibAssistant doesn't expect the microphone to stop sending data.
@@ -142,41 +151,74 @@
     DCHECK(RunsTasksInCurrentSequence(task_runner_));
 
     auto audio_packet = media::AudioBus::Create(audio_parameters_);
+    auto delay_in_microseconds =
+        audio_parameters_.GetMicrosecondsPerFrame() * audio_packet->frames();
     const base::TimeTicks time = base::TimeTicks::Now();
-    callback_->Capture(audio_packet.get(), time, /*volume=*/0.5,
-                       /*key_pressed=*/false);
+    if (callback_) {
+      callback_->Capture(audio_packet.get(), time, /*volume=*/0.5,
+                         /*key_pressed=*/false);
+    }
 
     PostDelayedTask(FROM_HERE,
-                    base::BindOnce(&FakeInputDevice::SendSilence, this),
-                    base::TimeDelta::FromMilliseconds(100));
+                    base::BindOnce(&FakeInputDevice::WaitForAudioFile,
+                                   weak_factory_.GetWeakPtr()),
+                    base::TimeDelta::FromMicrosecondsD(delay_in_microseconds));
   }
 
   void PostDelayedTask(const base::Location& from_here,
                        base::OnceClosure task,
                        base::TimeDelta delay) {
-    // We use a local copy of the refcounted pointer to the task runner so it
-    // can not be deleted between the check and the invocation.
-    auto runner = task_runner_;
-    if (!runner)
-      return;  // This means Stop was called.
-    runner->PostDelayedTask(from_here, std::move(task), delay);
+    task_runner_->PostDelayedTask(from_here, std::move(task), delay);
   }
 
   bool RunsTasksInCurrentSequence(
       scoped_refptr<base::SequencedTaskRunner> runner) {
-    return (runner == nullptr) || runner->RunsTasksInCurrentSequence();
+    return runner->RunsTasksInCurrentSequence();
   }
 
-  std::string audio_file_;
   media::AudioParameters audio_parameters_;
-  CaptureCallback* callback_;
+  media::AudioCapturerSource::CaptureCallback* callback_;
+  std::unique_ptr<media::AudioBlockFifo> audio_blocks_;
 
   scoped_refptr<base::SequencedTaskRunner> task_runner_ =
       base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()});
+
+  base::WeakPtrFactory<FakeInputDevice> weak_factory_{this};
+};
+
+// This wrapper class runs on the caller sequence, `FakeInputDevice` runs on a
+// separate background sequence. This wrapper manages the life cycle of
+// `FakeInputDevice` and makes sure it's deleted on the right sequence.
+class FakeInputDeviceWrapper : public media::AudioCapturerSource {
+ public:
+  FakeInputDeviceWrapper()
+      : fake_input_device_(std::make_unique<FakeInputDevice>()) {}
+
+  // AudioCapturerSource implementation.
+  void Initialize(const media::AudioParameters& params,
+                  CaptureCallback* callback) override {
+    fake_input_device_->Initialize(params, callback);
+  }
+
+  void Start() override { fake_input_device_->Start(); }
+
+  void Stop() override { fake_input_device_->Stop(); }
+
+  void SetVolume(double volume) override {}
+  void SetAutomaticGainControl(bool enabled) override {}
+  void SetOutputDeviceForAec(const std::string& output_device_id) override {}
+
+ private:
+  ~FakeInputDeviceWrapper() override {
+    fake_input_device_->task_runner()->DeleteSoon(
+        FROM_HERE, std::move(fake_input_device_));
+  }
+
+  std::unique_ptr<FakeInputDevice> fake_input_device_;
 };
 
 scoped_refptr<media::AudioCapturerSource> CreateFakeInputDevice() {
-  return base::MakeRefCounted<FakeInputDevice>(kFakeAudioFile);
+  return base::MakeRefCounted<FakeInputDeviceWrapper>();
 }
 
 }  // namespace libassistant
diff --git a/chromeos/services/libassistant/display_connection_impl.cc b/chromeos/services/libassistant/display_connection_impl.cc
index b01e3ed..2cbeffe1 100644
--- a/chromeos/services/libassistant/display_connection_impl.cc
+++ b/chromeos/services/libassistant/display_connection_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/check.h"
 #include "base/logging.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 
 namespace chromeos {
 namespace libassistant {
@@ -18,7 +19,8 @@
     bool media_session_enabled)
     : observer_(observer),
       feedback_ui_enabled_(feedback_ui_enabled),
-      media_session_enabled_(media_session_enabled) {
+      media_session_enabled_(media_session_enabled),
+      task_runner_(base::SequencedTaskRunnerHandle::Get()) {
   DCHECK(observer_);
 }
 
@@ -40,7 +42,11 @@
   }
 
   if (event.has_speech_level_event()) {
-    observer_->OnSpeechLevelUpdated(event.speech_level_event().speech_level());
+    task_runner_->PostTask(
+        FROM_HERE,
+        base::BindOnce(&DisplayConnectionObserver::OnSpeechLevelUpdated,
+                       base::Unretained(observer_),
+                       event.speech_level_event().speech_level()));
   }
 }
 
diff --git a/chromeos/services/libassistant/display_connection_impl.h b/chromeos/services/libassistant/display_connection_impl.h
index cdca5f2..53095f9 100644
--- a/chromeos/services/libassistant/display_connection_impl.h
+++ b/chromeos/services/libassistant/display_connection_impl.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/sequenced_task_runner.h"
 #include "base/synchronization/lock.h"
 #include "chromeos/services/libassistant/public/cpp/android_app_info.h"
 #include "libassistant/display/proto/display_connection.pb.h"
@@ -53,6 +54,7 @@
 
   Delegate* delegate_ GUARDED_BY(update_display_request_mutex_) = nullptr;
 
+  // Owned by the parent which also owns `this`.
   DisplayConnectionObserver* const observer_;
 
   // Whether Assistant feedback UI is enabled.
@@ -78,6 +80,8 @@
   // Both LibAssistant and Chrome threads may update and send display request so
   // we always guard access with |update_display_request_mutex_|.
   base::Lock update_display_request_mutex_;
+
+  scoped_refptr<base::SequencedTaskRunner> task_runner_;
 };
 
 }  // namespace libassistant
diff --git a/components/android_autofill/browser/android_autofill_manager.cc b/components/android_autofill/browser/android_autofill_manager.cc
index 62a08cf..30ffb72 100644
--- a/components/android_autofill/browser/android_autofill_manager.cc
+++ b/components/android_autofill/browser/android_autofill_manager.cc
@@ -144,7 +144,7 @@
     return autofill_provider_for_testing_;
   if (auto* rfh =
           static_cast<ContentAutofillDriver*>(driver())->render_frame_host()) {
-    if (rfh->IsCurrent()) {
+    if (rfh->IsActive()) {
       if (auto* web_contents = content::WebContents::FromRenderFrameHost(rfh)) {
         return AutofillProvider::FromWebContents(web_contents);
       }
diff --git a/components/arc/mojom/intent_helper.mojom b/components/arc/mojom/intent_helper.mojom
index fb5c989..b02394a 100644
--- a/components/arc/mojom/intent_helper.mojom
+++ b/components/arc/mojom/intent_helper.mojom
@@ -170,7 +170,7 @@
   CROSTINIEXPORTIMPORT,
   EXTERNALSTORAGE,
   INTERNET,
-  KERBEROSACCOUNTS,
+  DEPRECATED_KERBEROSACCOUNTS,
   KNOWNNETWORKS,
   MANAGEACCESSIBILITYTTS,
   SMARTLOCKSETTINGS,
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index 67a605d..3159c019 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -135,10 +135,10 @@
 }
 
 bool ContentAutofillDriver::CanShowAutofillUi() const {
-  // Don't show AutofillUi for non-current RenderFrameHost. Here it is safe to
+  // Don't show AutofillUi for inactive RenderFrameHost. Here it is safe to
   // ignore the calls from inactive RFH as the renderer is not expecting a reply
   // and it doesn't lead to browser-renderer consistency issues.
-  return render_frame_host_->IsCurrent();
+  return render_frame_host_->IsActive();
 }
 
 ui::AXTreeID ContentAutofillDriver::GetAxTreeId() const {
diff --git a/components/back_forward_cache/OWNERS b/components/back_forward_cache/OWNERS
index e0d3cd7..e41f887 100644
--- a/components/back_forward_cache/OWNERS
+++ b/components/back_forward_cache/OWNERS
@@ -1 +1,5 @@
+file://content/OWNERS
+file://content/browser/OWNERS
+file://content/browser/renderer_host/OWNERS
 altimin@chromium.org
+fergal@chromium.org
diff --git a/components/back_forward_cache/back_forward_cache_disable.cc b/components/back_forward_cache/back_forward_cache_disable.cc
index 1100ab4e..084545a 100644
--- a/components/back_forward_cache/back_forward_cache_disable.cc
+++ b/components/back_forward_cache/back_forward_cache_disable.cc
@@ -35,8 +35,8 @@
       return "Extensions";
     case DisabledReasonId::kExtensionMessaging:
       return "ExtensionMessaging";
-    default:
-      return "Unknown (default)";
+    case DisabledReasonId::kOomInterventionTabHelper:
+      return "OomInterventionTabHelper";
   }
 }
 
diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java
index 83181ea..b9721ff 100644
--- a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java
@@ -23,7 +23,7 @@
     private View mView;
     /** The color resource ID for tinting of the view's background. */
     @ColorRes
-    private int mBackgroundColorRes;
+    private Integer mBackgroundColorRes;
 
     public ChromeSwitchPreference(Context context) {
         super(context);
@@ -72,13 +72,13 @@
      * @param colorRes
      */
     public void setBackgroundColor(@ColorRes int colorRes) {
-        if (mBackgroundColorRes == colorRes) return;
+        if (mBackgroundColorRes != null && mBackgroundColorRes == colorRes) return;
         mBackgroundColorRes = colorRes;
         updateBackground();
     }
 
     private void updateBackground() {
-        if (mView == null) return;
+        if (mView == null || mBackgroundColorRes == null) return;
         mView.setBackgroundColor(mBackgroundColorRes);
     }
 }
diff --git a/components/cast_streaming/browser/cast_streaming_session.cc b/components/cast_streaming/browser/cast_streaming_session.cc
index 0a0ac52..3251d90 100644
--- a/components/cast_streaming/browser/cast_streaming_session.cc
+++ b/components/cast_streaming/browser/cast_streaming_session.cc
@@ -6,17 +6,11 @@
 
 #include "base/bind.h"
 #include "base/time/time.h"
-#include "base/timer/timer.h"
-#include "components/cast_streaming/browser/cast_message_port_impl.h"
 #include "components/cast_streaming/browser/config_conversions.h"
 #include "components/cast_streaming/browser/stream_consumer.h"
-#include "components/openscreen_platform/network_util.h"
-#include "components/openscreen_platform/task_runner.h"
 #include "media/base/timestamp_constants.h"
 #include "media/mojo/common/mojo_decoder_buffer_converter.h"
 #include "mojo/public/cpp/system/data_pipe.h"
-#include "third_party/openscreen/src/cast/streaming/receiver.h"
-#include "third_party/openscreen/src/cast/streaming/receiver_session.h"
 
 namespace {
 
@@ -42,254 +36,229 @@
 
 namespace cast_streaming {
 
-// Owns the Open Screen ReceiverSession. The Cast Streaming Session is tied to
-// the lifespan of this object.
-class CastStreamingSession::Internal
-    : public openscreen::cast::ReceiverSession::Client {
- public:
-  Internal(CastStreamingSession::Client* client,
-           std::unique_ptr<cast_api_bindings::MessagePort> message_port,
-           scoped_refptr<base::SequencedTaskRunner> task_runner)
-      : task_runner_(task_runner),
-        environment_(&openscreen::Clock::now, &task_runner_),
-        cast_message_port_impl_(
-            std::move(message_port),
-            base::BindOnce(&CastStreamingSession::Internal::OnCastChannelClosed,
-                           base::Unretained(this))),
-        client_(client) {
-    DCHECK(task_runner);
-    DCHECK(client_);
+CastStreamingSession::ReceiverSessionClient::ReceiverSessionClient(
+    CastStreamingSession::Client* client,
+    std::unique_ptr<cast_api_bindings::MessagePort> message_port,
+    scoped_refptr<base::SequencedTaskRunner> task_runner)
+    : task_runner_(task_runner),
+      environment_(&openscreen::Clock::now, &task_runner_),
+      cast_message_port_impl_(
+          std::move(message_port),
+          base::BindOnce(
+              &CastStreamingSession::ReceiverSessionClient::OnCastChannelClosed,
+              base::Unretained(this))),
+      client_(client) {
+  DCHECK(task_runner);
+  DCHECK(client_);
 
-    // TODO(crbug.com/1087520): Add streaming session Constraints and
-    // DisplayDescription.
-    receiver_session_ = std::make_unique<openscreen::cast::ReceiverSession>(
-        this, &environment_, &cast_message_port_impl_,
-        openscreen::cast::ReceiverSession::Preferences(
-            {openscreen::cast::VideoCodec::kH264,
-             openscreen::cast::VideoCodec::kVp8},
-            {openscreen::cast::AudioCodec::kAac,
-             openscreen::cast::AudioCodec::kOpus}));
+  // TODO(crbug.com/1087520): Add streaming session Constraints and
+  // DisplayDescription.
+  receiver_session_ = std::make_unique<openscreen::cast::ReceiverSession>(
+      this, &environment_, &cast_message_port_impl_,
+      openscreen::cast::ReceiverSession::Preferences(
+          {openscreen::cast::VideoCodec::kH264,
+           openscreen::cast::VideoCodec::kVp8},
+          {openscreen::cast::AudioCodec::kAac,
+           openscreen::cast::AudioCodec::kOpus}));
 
-    init_timeout_timer_.Start(
-        FROM_HERE, kInitTimeout,
-        base::BindOnce(&CastStreamingSession::Internal::OnInitializationTimeout,
-                       base::Unretained(this)));
+  init_timeout_timer_.Start(
+      FROM_HERE, kInitTimeout,
+      base::BindOnce(
+          &CastStreamingSession::ReceiverSessionClient::OnInitializationTimeout,
+          base::Unretained(this)));
+}
+
+CastStreamingSession::ReceiverSessionClient::~ReceiverSessionClient() = default;
+
+void CastStreamingSession::ReceiverSessionClient::OnInitializationTimeout() {
+  DVLOG(1) << __func__;
+  DCHECK(!is_initialized_);
+  client_->OnSessionEnded();
+  is_initialized_ = true;
+}
+
+absl::optional<CastStreamingSession::AudioStreamInfo>
+CastStreamingSession::ReceiverSessionClient::InitializeAudioConsumer(
+    openscreen::cast::Receiver* audio_receiver,
+    const openscreen::cast::AudioCaptureConfig& audio_capture_config) {
+  DCHECK(audio_receiver);
+
+  // Create the audio data pipe.
+  mojo::ScopedDataPipeProducerHandle data_pipe_producer;
+  mojo::ScopedDataPipeConsumerHandle data_pipe_consumer;
+  if (!CreateDataPipeForStreamType(media::DemuxerStream::Type::AUDIO,
+                                   &data_pipe_producer, &data_pipe_consumer)) {
+    return absl::nullopt;
   }
 
-  ~Internal() final = default;
+  // We can use unretained pointers here because StreamConsumer is owned by
+  // this object and |client_| is guaranteed to outlive this object. Here,
+  // the duration is set to kNoTimestamp so the audio renderer does not block.
+  // Audio frames duration is not known ahead of time in mirroring.
+  audio_consumer_ = std::make_unique<StreamConsumer>(
+      audio_receiver, media::kNoTimestamp, std::move(data_pipe_producer),
+      base::BindRepeating(&CastStreamingSession::Client::OnAudioBufferReceived,
+                          base::Unretained(client_)),
+      base::BindRepeating(&base::OneShotTimer::Reset,
+                          base::Unretained(&data_timeout_timer_)));
 
-  Internal(const Internal&) = delete;
-  Internal& operator=(const Internal&) = delete;
+  return AudioStreamInfo{
+      AudioCaptureConfigToAudioDecoderConfig(audio_capture_config),
+      std::move(data_pipe_consumer)};
+}
 
- private:
-  void OnInitializationTimeout() {
-    DVLOG(1) << __func__;
-    DCHECK(!is_initialized_);
-    client_->OnSessionEnded();
-    is_initialized_ = true;
+absl::optional<CastStreamingSession::VideoStreamInfo>
+CastStreamingSession::ReceiverSessionClient::InitializeVideoConsumer(
+    openscreen::cast::Receiver* video_receiver,
+    const openscreen::cast::VideoCaptureConfig& video_capture_config) {
+  DCHECK(video_receiver);
+
+  // Create the video data pipe.
+  mojo::ScopedDataPipeProducerHandle data_pipe_producer;
+  mojo::ScopedDataPipeConsumerHandle data_pipe_consumer;
+  if (!CreateDataPipeForStreamType(media::DemuxerStream::Type::VIDEO,
+                                   &data_pipe_producer, &data_pipe_consumer)) {
+    return absl::nullopt;
   }
 
-  // Initializes the audio consumer with |audio_capture_config|. Returns an
-  // empty Optional on failure.
-  absl::optional<AudioStreamInfo> InitializeAudioConsumer(
-      openscreen::cast::Receiver* audio_receiver,
-      const openscreen::cast::AudioCaptureConfig& audio_capture_config) {
-    DCHECK(audio_receiver);
+  // We can use unretained pointers here because StreamConsumer is owned by
+  // this object and |client_| is guaranteed to outlive this object.
+  // |data_timeout_timer_| is also owned by this object and will outlive both
+  // StreamConsumers.
+  // The frame duration is set to 10 minutes to work around cases where
+  // senders do not send data for a long period of time. We end up with
+  // overlapping video frames but this is fine since the media pipeline mostly
+  // considers the playout time when deciding which frame to present or play
+  video_consumer_ = std::make_unique<StreamConsumer>(
+      video_receiver, base::TimeDelta::FromMinutes(10),
+      std::move(data_pipe_producer),
+      base::BindRepeating(&CastStreamingSession::Client::OnVideoBufferReceived,
+                          base::Unretained(client_)),
+      base::BindRepeating(&base::OneShotTimer::Reset,
+                          base::Unretained(&data_timeout_timer_)));
 
-    // Create the audio data pipe.
-    mojo::ScopedDataPipeProducerHandle data_pipe_producer;
-    mojo::ScopedDataPipeConsumerHandle data_pipe_consumer;
-    if (!CreateDataPipeForStreamType(media::DemuxerStream::Type::AUDIO,
-                                     &data_pipe_producer,
-                                     &data_pipe_consumer)) {
-      return absl::nullopt;
-    }
+  return VideoStreamInfo{
+      VideoCaptureConfigToVideoDecoderConfig(video_capture_config),
+      std::move(data_pipe_consumer)};
+}
 
-    // We can use unretained pointers here because StreamConsumer is owned by
-    // this object and |client_| is guaranteed to outlive this object. Here,
-    // the duration is set to kNoTimestamp so the audio renderer does not block.
-    // Audio frames duration is not known ahead of time in mirroring.
-    audio_consumer_ = std::make_unique<StreamConsumer>(
-        audio_receiver, media::kNoTimestamp, std::move(data_pipe_producer),
-        base::BindRepeating(
-            &CastStreamingSession::Client::OnAudioBufferReceived,
-            base::Unretained(client_)),
-        base::BindRepeating(&base::OneShotTimer::Reset,
-                            base::Unretained(&data_timeout_timer_)));
+void CastStreamingSession::ReceiverSessionClient::OnNegotiated(
+    const openscreen::cast::ReceiverSession* session,
+    openscreen::cast::ReceiverSession::ConfiguredReceivers receivers) {
+  DVLOG(1) << __func__;
+  DCHECK_EQ(session, receiver_session_.get());
+  init_timeout_timer_.Stop();
 
-    return AudioStreamInfo{
-        AudioCaptureConfigToAudioDecoderConfig(audio_capture_config),
-        std::move(data_pipe_consumer)};
-  }
+  bool is_new_offer = is_initialized_;
+  if (is_new_offer) {
+    // This is a second offer message, reinitialize the streams.
+    bool existing_session_has_audio = audio_consumer_ != nullptr;
+    bool existing_session_has_video = video_consumer_ != nullptr;
+    audio_consumer_.reset();
+    video_consumer_.reset();
 
-  // Initializes the video consumer with |video_capture_config|. Returns an
-  // empty Optional on failure.
-  absl::optional<VideoStreamInfo> InitializeVideoConsumer(
-      openscreen::cast::Receiver* video_receiver,
-      const openscreen::cast::VideoCaptureConfig& video_capture_config) {
-    DCHECK(video_receiver);
+    bool new_offer_has_audio = receivers.audio_receiver != nullptr;
+    bool new_offer_has_video = receivers.video_receiver != nullptr;
 
-    // Creare the video data pipe.
-    mojo::ScopedDataPipeProducerHandle data_pipe_producer;
-    mojo::ScopedDataPipeConsumerHandle data_pipe_consumer;
-    if (!CreateDataPipeForStreamType(media::DemuxerStream::Type::VIDEO,
-                                     &data_pipe_producer,
-                                     &data_pipe_consumer)) {
-      return absl::nullopt;
-    }
-
-    // We can use unretained pointers here because StreamConsumer is owned by
-    // this object and |client_| is guaranteed to outlive this object.
-    // |data_timeout_timer_| is also owned by this object and will outlive both
-    // StreamConsumers.
-    // The frame duration is set to 10 minutes to work around cases where
-    // senders do not send data for a long period of time. We end up with
-    // overlapping video frames but this is fine since the media pipeline mostly
-    // considers the playout time when deciding which frame to present or play
-    video_consumer_ = std::make_unique<StreamConsumer>(
-        video_receiver, base::TimeDelta::FromMinutes(10),
-        std::move(data_pipe_producer),
-        base::BindRepeating(
-            &CastStreamingSession::Client::OnVideoBufferReceived,
-            base::Unretained(client_)),
-        base::BindRepeating(&base::OneShotTimer::Reset,
-                            base::Unretained(&data_timeout_timer_)));
-
-    return VideoStreamInfo{
-        VideoCaptureConfigToVideoDecoderConfig(video_capture_config),
-        std::move(data_pipe_consumer)};
-  }
-
-  // openscreen::cast::ReceiverSession::Client implementation.
-  void OnNegotiated(
-      const openscreen::cast::ReceiverSession* session,
-      openscreen::cast::ReceiverSession::ConfiguredReceivers receivers) final {
-    DVLOG(1) << __func__;
-    DCHECK_EQ(session, receiver_session_.get());
-    init_timeout_timer_.Stop();
-
-    bool is_new_offer = is_initialized_;
-    if (is_new_offer) {
-      // This is a second offer message, reinitialize the streams.
-      bool existing_session_has_audio = audio_consumer_ != nullptr;
-      bool existing_session_has_video = video_consumer_ != nullptr;
-      audio_consumer_.reset();
-      video_consumer_.reset();
-
-      bool new_offer_has_audio = receivers.audio_receiver != nullptr;
-      bool new_offer_has_video = receivers.video_receiver != nullptr;
-
-      if (new_offer_has_audio != existing_session_has_audio ||
-          new_offer_has_video != existing_session_has_video) {
-        // Different audio/video configuration than in the first offer message.
-        // Return early here.
-        client_->OnSessionEnded();
-        return;
-      }
-    }
-
-    // Set |is_initialized_| now so we can return early on failure.
-    is_initialized_ = true;
-
-    absl::optional<AudioStreamInfo> audio_stream_info;
-    if (receivers.audio_receiver) {
-      audio_stream_info = InitializeAudioConsumer(receivers.audio_receiver,
-                                                  receivers.audio_config);
-      if (audio_stream_info) {
-        DVLOG(1) << "Initialized audio stream. "
-                 << audio_stream_info->decoder_config.AsHumanReadableString();
-      } else {
-        client_->OnSessionEnded();
-        return;
-      }
-    }
-
-    absl::optional<VideoStreamInfo> video_stream_info;
-    if (receivers.video_receiver) {
-      video_stream_info = InitializeVideoConsumer(receivers.video_receiver,
-                                                  receivers.video_config);
-      if (video_stream_info) {
-        DVLOG(1) << "Initialized video stream. "
-                 << video_stream_info->decoder_config.AsHumanReadableString();
-      } else {
-        audio_consumer_.reset();
-        audio_stream_info.reset();
-        client_->OnSessionEnded();
-        return;
-      }
-    }
-
-    // This is necessary in case the offer message had no audio and no video
-    // stream.
-    if (!audio_stream_info && !video_stream_info) {
+    if (new_offer_has_audio != existing_session_has_audio ||
+        new_offer_has_video != existing_session_has_video) {
+      // Different audio/video configuration than in the first offer message.
+      // Return early here.
       client_->OnSessionEnded();
       return;
     }
+  }
 
-    if (is_new_offer) {
-      client_->OnSessionReinitialization(std::move(audio_stream_info),
-                                         std::move(video_stream_info));
+  // Set |is_initialized_| now so we can return early on failure.
+  is_initialized_ = true;
+
+  absl::optional<AudioStreamInfo> audio_stream_info;
+  if (receivers.audio_receiver) {
+    audio_stream_info = InitializeAudioConsumer(receivers.audio_receiver,
+                                                receivers.audio_config);
+    if (audio_stream_info) {
+      DVLOG(1) << "Initialized audio stream. "
+               << audio_stream_info->decoder_config.AsHumanReadableString();
     } else {
-      client_->OnSessionInitialization(std::move(audio_stream_info),
-                                       std::move(video_stream_info));
-      data_timeout_timer_.Start(
-          FROM_HERE, kNoDataTimeout,
-          base::BindOnce(&CastStreamingSession::Internal::OnDataTimeout,
-                         base::Unretained(this)));
-    }
-  }
-
-  void OnReceiversDestroying(const openscreen::cast::ReceiverSession* session,
-                             ReceiversDestroyingReason reason) final {
-    // This can be called when |receiver_session_| is being destroyed, so we
-    // do not sanity-check |session| here.
-    DVLOG(1) << __func__;
-
-    switch (reason) {
-      case ReceiversDestroyingReason::kEndOfSession:
-        audio_consumer_.reset();
-        video_consumer_.reset();
-        client_->OnSessionEnded();
-        break;
-      case ReceiversDestroyingReason::kRenegotiated:
-        break;
-    }
-  }
-
-  void OnError(const openscreen::cast::ReceiverSession* session,
-               openscreen::Error error) final {
-    DCHECK_EQ(session, receiver_session_.get());
-    LOG(ERROR) << error;
-    if (!is_initialized_) {
       client_->OnSessionEnded();
-      is_initialized_ = true;
+      return;
     }
   }
 
-  void OnDataTimeout() {
-    DVLOG(1) << __func__;
-    receiver_session_.reset();
+  absl::optional<VideoStreamInfo> video_stream_info;
+  if (receivers.video_receiver) {
+    video_stream_info = InitializeVideoConsumer(receivers.video_receiver,
+                                                receivers.video_config);
+    if (video_stream_info) {
+      DVLOG(1) << "Initialized video stream. "
+               << video_stream_info->decoder_config.AsHumanReadableString();
+    } else {
+      audio_consumer_.reset();
+      audio_stream_info.reset();
+      client_->OnSessionEnded();
+      return;
+    }
   }
 
-  void OnCastChannelClosed() {
-    DVLOG(1) << __func__;
-    receiver_session_.reset();
+  // This is necessary in case the offer message had no audio and no video
+  // stream.
+  if (!audio_stream_info && !video_stream_info) {
+    client_->OnSessionEnded();
+    return;
   }
 
-  openscreen_platform::TaskRunner task_runner_;
-  openscreen::cast::Environment environment_;
-  CastMessagePortImpl cast_message_port_impl_;
-  std::unique_ptr<openscreen::cast::ReceiverSession> receiver_session_;
-  base::OneShotTimer init_timeout_timer_;
+  if (is_new_offer) {
+    client_->OnSessionReinitialization(std::move(audio_stream_info),
+                                       std::move(video_stream_info));
+  } else {
+    client_->OnSessionInitialization(std::move(audio_stream_info),
+                                     std::move(video_stream_info));
+    data_timeout_timer_.Start(
+        FROM_HERE, kNoDataTimeout,
+        base::BindOnce(
+            &CastStreamingSession::ReceiverSessionClient::OnDataTimeout,
+            base::Unretained(this)));
+  }
+}
 
-  // Timer to trigger connection closure if no data is received for 15 seconds.
-  base::OneShotTimer data_timeout_timer_;
+void CastStreamingSession::ReceiverSessionClient::OnReceiversDestroying(
+    const openscreen::cast::ReceiverSession* session,
+    ReceiversDestroyingReason reason) {
+  // This can be called when |receiver_session_| is being destroyed, so we
+  // do not sanity-check |session| here.
+  DVLOG(1) << __func__;
 
-  bool is_initialized_ = false;
-  CastStreamingSession::Client* const client_;
-  std::unique_ptr<StreamConsumer> audio_consumer_;
-  std::unique_ptr<StreamConsumer> video_consumer_;
-};
+  switch (reason) {
+    case ReceiversDestroyingReason::kEndOfSession:
+      audio_consumer_.reset();
+      video_consumer_.reset();
+      client_->OnSessionEnded();
+      break;
+    case ReceiversDestroyingReason::kRenegotiated:
+      break;
+  }
+}
+
+void CastStreamingSession::ReceiverSessionClient::OnError(
+    const openscreen::cast::ReceiverSession* session,
+    openscreen::Error error) {
+  DCHECK_EQ(session, receiver_session_.get());
+  LOG(ERROR) << error;
+  if (!is_initialized_) {
+    client_->OnSessionEnded();
+    is_initialized_ = true;
+  }
+}
+
+void CastStreamingSession::ReceiverSessionClient::OnDataTimeout() {
+  DVLOG(1) << __func__;
+  receiver_session_.reset();
+}
+
+void CastStreamingSession::ReceiverSessionClient::OnCastChannelClosed() {
+  DVLOG(1) << __func__;
+  receiver_session_.reset();
+}
 
 CastStreamingSession::Client::~Client() = default;
 CastStreamingSession::CastStreamingSession() = default;
@@ -301,15 +270,15 @@
     scoped_refptr<base::SequencedTaskRunner> task_runner) {
   DVLOG(1) << __func__;
   DCHECK(client);
-  DCHECK(!internal_);
-  internal_ =
-      std::make_unique<Internal>(client, std::move(message_port), task_runner);
+  DCHECK(!receiver_session_);
+  receiver_session_ = std::make_unique<ReceiverSessionClient>(
+      client, std::move(message_port), task_runner);
 }
 
 void CastStreamingSession::Stop() {
   DVLOG(1) << __func__;
-  DCHECK(internal_);
-  internal_.reset();
+  DCHECK(receiver_session_);
+  receiver_session_.reset();
 }
 
 }  // namespace cast_streaming
diff --git a/components/cast_streaming/browser/cast_streaming_session.h b/components/cast_streaming/browser/cast_streaming_session.h
index 64e1327..2002f67 100644
--- a/components/cast_streaming/browser/cast_streaming_session.h
+++ b/components/cast_streaming/browser/cast_streaming_session.h
@@ -9,17 +9,25 @@
 
 #include "base/callback.h"
 #include "base/sequenced_task_runner.h"
+#include "base/timer/timer.h"
 #include "components/cast/message_port/message_port.h"
+#include "components/cast_streaming/browser/cast_message_port_impl.h"
+#include "components/openscreen_platform/network_util.h"
+#include "components/openscreen_platform/task_runner.h"
 #include "media/base/audio_decoder_config.h"
 #include "media/base/video_decoder_config.h"
 #include "media/mojo/mojom/media_types.mojom.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/openscreen/src/cast/streaming/receiver.h"
+#include "third_party/openscreen/src/cast/streaming/receiver_session.h"
 
 namespace cast_streaming {
 
+class StreamConsumer;
+
 // Entry point for the Cast Streaming Receiver implementation. Used to start a
-// Cast Streaming Session for a provided FIDL MessagePort request.
+// Cast Streaming Session for a provided MessagePort server.
 class CastStreamingSession {
  public:
   template <class T>
@@ -86,8 +94,64 @@
   void Stop();
 
  private:
-  class Internal;
-  std::unique_ptr<Internal> internal_;
+  // Owns the Open Screen ReceiverSession. The Streaming Session is tied to the
+  // lifespan of this object.
+  class ReceiverSessionClient
+      : public openscreen::cast::ReceiverSession::Client {
+   public:
+    ReceiverSessionClient(
+        CastStreamingSession::Client* client,
+        std::unique_ptr<cast_api_bindings::MessagePort> message_port,
+        scoped_refptr<base::SequencedTaskRunner> task_runner);
+    ~ReceiverSessionClient() final;
+
+    ReceiverSessionClient(const ReceiverSessionClient&) = delete;
+    ReceiverSessionClient& operator=(const ReceiverSessionClient&) = delete;
+
+   private:
+    void OnInitializationTimeout();
+
+    // Initializes the audio consumer with |audio_capture_config|. Returns an
+    // empty Optional on failure.
+    absl::optional<AudioStreamInfo> InitializeAudioConsumer(
+        openscreen::cast::Receiver* audio_receiver,
+        const openscreen::cast::AudioCaptureConfig& audio_capture_config);
+
+    // Initializes the video consumer with |video_capture_config|. Returns an
+    // empty Optional on failure.
+    absl::optional<VideoStreamInfo> InitializeVideoConsumer(
+        openscreen::cast::Receiver* video_receiver,
+        const openscreen::cast::VideoCaptureConfig& video_capture_config);
+
+    // openscreen::cast::ReceiverSession::Client implementation.
+    void OnNegotiated(
+        const openscreen::cast::ReceiverSession* session,
+        openscreen::cast::ReceiverSession::ConfiguredReceivers receivers) final;
+    void OnReceiversDestroying(const openscreen::cast::ReceiverSession* session,
+                               ReceiversDestroyingReason reason) final;
+    void OnError(const openscreen::cast::ReceiverSession* session,
+                 openscreen::Error error) final;
+
+    void OnDataTimeout();
+    void OnCastChannelClosed();
+
+    openscreen_platform::TaskRunner task_runner_;
+    openscreen::cast::Environment environment_;
+    CastMessagePortImpl cast_message_port_impl_;
+    std::unique_ptr<openscreen::cast::ReceiverSession> receiver_session_;
+    base::OneShotTimer init_timeout_timer_;
+
+    // Timer to trigger connection closure if no data is received for 15
+    // seconds.
+    base::OneShotTimer data_timeout_timer_;
+
+    bool is_initialized_ = false;
+    CastStreamingSession::Client* const client_;
+    std::unique_ptr<StreamConsumer> audio_consumer_;
+    std::unique_ptr<StreamConsumer> video_consumer_;
+  };
+
+  std::unique_ptr<ReceiverSessionClient> receiver_session_;
 };
 
 }  // namespace cast_streaming
diff --git a/components/content_creation/notes/DEPS b/components/content_creation/notes/DEPS
index 354c1ba..1134fc4 100644
--- a/components/content_creation/notes/DEPS
+++ b/components/content_creation/notes/DEPS
@@ -1,5 +1,8 @@
 include_rules = [
   "+components/keyed_service/core",
+  "+components/signin/public/identity_manager",
   "+components/strings/grit",
+  "+google_apis",
+  "+services/network/public/cpp",
   "+ui/base",
 ]
diff --git a/components/content_creation/notes/core/BUILD.gn b/components/content_creation/notes/core/BUILD.gn
index 4eca277..892d58d 100644
--- a/components/content_creation/notes/core/BUILD.gn
+++ b/components/content_creation/notes/core/BUILD.gn
@@ -10,6 +10,7 @@
 
   deps = [
     "//base",
+    "//components/content_creation/notes/core/server",
     "//components/content_creation/notes/core/templates",
     "//components/keyed_service/core",
   ]
diff --git a/components/content_creation/notes/core/server/BUILD.gn b/components/content_creation/notes/core/server/BUILD.gn
new file mode 100644
index 0000000..cfcf23e
--- /dev/null
+++ b/components/content_creation/notes/core/server/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2021 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("//third_party/protobuf/proto_library.gni")
+
+static_library("server") {
+  sources = [
+    "notes_server_base.cc",
+    "notes_server_base.h",
+  ]
+
+  deps = [
+    ":proto",
+    "//base",
+    "//components/signin/public/identity_manager",
+    "//google_apis",
+    "//services/network/public/cpp",
+  ]
+}
+
+proto_library("proto") {
+  sources = [ "note.proto" ]
+}
diff --git a/components/content_creation/notes/core/server/note.proto b/components/content_creation/notes/core/server/note.proto
new file mode 100644
index 0000000..2ebad64
--- /dev/null
+++ b/components/content_creation/notes/core/server/note.proto
@@ -0,0 +1,34 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto3";
+package web_notes;
+
+option optimize_for = LITE_RUNTIME;
+
+message Note {
+  // The user's comment about the highlighted quote.
+  string comment = 1;
+  // The highlighted text on the web page.
+  string quote = 2;
+  // The URL of the page of the note and quote.
+  string web_page_url = 3;
+  // The text-fragment directive for Chrome to highlight the quote.
+  string highlight_directive = 4;
+}
+
+message NoteContentId {
+  // The contributor account ID.
+  string account_id = 1;
+  // The note ID.
+  string webnote_id = 2;
+}
+
+message SaveNoteRequest {
+  Note note = 1;
+}
+
+message SaveNoteResponse {
+  NoteContentId note_content_id = 1;
+}
diff --git a/components/content_creation/notes/core/server/notes_server_base.cc b/components/content_creation/notes/core/server/notes_server_base.cc
new file mode 100644
index 0000000..59ed7eb
--- /dev/null
+++ b/components/content_creation/notes/core/server/notes_server_base.cc
@@ -0,0 +1,50 @@
+// Copyright 2021 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 "components/content_creation/notes/core/server/notes_server_base.h"
+
+#import "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
+#import "google_apis/google_api_keys.h"
+#import "services/network/public/cpp/shared_url_loader_factory.h"
+#import "services/network/public/cpp/simple_url_loader.h"
+
+NotesServerBase::NotesServerBase(
+    scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
+    signin::IdentityManager* identity_manager)
+    : identity_manager_(identity_manager) {
+  url_loader_factory_ = std::move(loader_factory);
+}
+
+NotesServerBase::~NotesServerBase() {}
+
+signin::ScopeSet NotesServerBase::GetAuthScopes() {
+  NOTIMPLEMENTED();
+
+  return {"put_real_one_here"};
+}
+
+GURL NotesServerBase::GetNotesServerURL() {
+  NOTIMPLEMENTED();
+
+  return GURL();
+}
+
+std::unique_ptr<network::ResourceRequest>
+NotesServerBase::CreateNoteResourceRequest(GURL request_url,
+                                           const std::string request_method) {
+  NOTIMPLEMENTED();
+
+  return std::make_unique<network::ResourceRequest>();
+}
+
+bool NotesServerBase::HasValidNonEmptyResponse(
+    const std::string& response_body) {
+  NOTIMPLEMENTED();
+
+  return true;
+}
+
+void NotesServerBase::StartAccessTokenFetch() {
+  NOTIMPLEMENTED();
+}
diff --git a/components/content_creation/notes/core/server/notes_server_base.h b/components/content_creation/notes/core/server/notes_server_base.h
new file mode 100644
index 0000000..ad504af
--- /dev/null
+++ b/components/content_creation/notes/core/server/notes_server_base.h
@@ -0,0 +1,75 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_CONTENT_CREATION_NOTES_CORE_SERVER_NOTES_SERVER_BASE_H_
+#define COMPONENTS_CONTENT_CREATION_NOTES_CORE_SERVER_NOTES_SERVER_BASE_H_
+
+#include <string>
+
+#import "components/signin/public/identity_manager/access_token_info.h"
+#import "components/signin/public/identity_manager/scope_set.h"
+#import "google_apis/gaia/google_service_auth_error.h"
+#import "services/network/public/cpp/resource_request.h"
+
+namespace network {
+class SimpleURLLoader;
+class SharedURLLoaderFactory;
+}  // namespace network
+
+namespace signin {
+class IdentityManager;
+class PrimaryAccountAccessTokenFetcher;
+}  // namespace signin
+
+// Base class for interactions with the Notes Server.
+class NotesServerBase {
+ public:
+  explicit NotesServerBase(
+      scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
+      signin::IdentityManager* identity_manager);
+  virtual ~NotesServerBase();
+
+  virtual void Start() = 0;
+
+  // Not copyable or movable.
+  NotesServerBase(const NotesServerBase&) = delete;
+  NotesServerBase& operator=(const NotesServerBase&) = delete;
+
+ protected:
+  // Returns the ScopeSet for which the user needs to authenticate to access the
+  // Notes Server.
+  signin::ScopeSet GetAuthScopes();
+
+  // Returns the URL of the Notes Server
+  GURL GetNotesServerURL();
+
+  // Creates the basic ResourceRequest for Note queries.
+  std::unique_ptr<network::ResourceRequest> CreateNoteResourceRequest(
+      GURL request_url,
+      const std::string request_method);
+
+  // Returns whether the response returned from the server is valid and
+  // non-empty.
+  bool HasValidNonEmptyResponse(const std::string& response_body);
+
+  // Starts the request to get the user's access token.
+  void StartAccessTokenFetch();
+
+  // Called when the user's access token has been fetched.
+  virtual void AccessTokenFetchFinished(
+      base::TimeTicks token_start_ticks,
+      GoogleServiceAuthError error,
+      signin::AccessTokenInfo access_token_info) = 0;
+
+  // The access token for the user. Used to authenticate to the server.
+  std::string access_token_;
+
+  signin::IdentityManager* const identity_manager_;
+  std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher> token_fetcher_;
+
+  scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+  std::unique_ptr<network::SimpleURLLoader> url_loader_;
+};
+
+#endif  // COMPONENTS_CONTENT_CREATION_NOTES_CORE_SERVER_NOTES_SERVER_BASE_H_
diff --git a/components/content_settings/browser/page_specific_content_settings_unittest.cc b/components/content_settings/browser/page_specific_content_settings_unittest.cc
index 7cff682..6cad7a60 100644
--- a/components/content_settings/browser/page_specific_content_settings_unittest.cc
+++ b/components/content_settings/browser/page_specific_content_settings_unittest.cc
@@ -344,7 +344,7 @@
                                           blocked_by_policy);
   content_settings->OnSharedWorkerAccessed(
       GURL("http://youtube.com/worker.js"), "worker",
-      blink::StorageKey(url::Origin::Create(GURL("https://youtube.com"))),
+      blink::StorageKey::CreateFromStringForTesting("https://youtube.com"),
       blocked_by_policy);
 
   const auto& objects = content_settings->allowed_local_shared_objects();
diff --git a/components/guest_view/browser/BUILD.gn b/components/guest_view/browser/BUILD.gn
index f60ffe5..896e019 100644
--- a/components/guest_view/browser/BUILD.gn
+++ b/components/guest_view/browser/BUILD.gn
@@ -33,6 +33,7 @@
   ]
   deps = [
     "//build:chromeos_buildflags",
+    "//components/crash/core/common:crash_key",
     "//components/keyed_service/content",
     "//components/keyed_service/core",
     "//components/zoom",
diff --git a/components/gwp_asan/client/sampling_malloc_shims_unittest.cc b/components/gwp_asan/client/sampling_malloc_shims_unittest.cc
index 8eeb3ec..d4a9fdc 100644
--- a/components/gwp_asan/client/sampling_malloc_shims_unittest.cc
+++ b/components/gwp_asan/client/sampling_malloc_shims_unittest.cc
@@ -10,6 +10,7 @@
 #include <string>
 
 #include "base/allocator/allocator_shim.h"
+#include "base/allocator/buildflags.h"
 #include "base/callback_helpers.h"
 #include "base/memory/page_size.h"
 #include "base/strings/string_number_conversions.h"
@@ -295,7 +296,8 @@
 }
 #endif  // defined(OS_WIN)
 
-#if defined(OS_APPLE)
+// PartitionAlloc-Everywhere does not support batch_malloc / batch_free.
+#if defined(OS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
     BatchFree,
     SamplingMallocShimsTest::multiprocessTestSetup) {
@@ -323,7 +325,7 @@
 TEST_F(SamplingMallocShimsTest, BatchFree) {
   runTest("BatchFree");
 }
-#endif  // defined(OS_APPLE)
+#endif  // defined(OS_APPLE) && !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
 
 }  // namespace
 
diff --git a/components/optimization_guide/core/hints_component_util.h b/components/optimization_guide/core/hints_component_util.h
index c9faf7b2..2170c3c 100644
--- a/components/optimization_guide/core/hints_component_util.h
+++ b/components/optimization_guide/core/hints_component_util.h
@@ -24,16 +24,17 @@
 // Keep in sync with OptimizationGuideProcessHintsResult in
 // tools/metrics/histograms/enums.xml.
 enum class ProcessHintsComponentResult {
-  kSuccess,
-  kFailedInvalidParameters,
-  kFailedReadingFile,
-  kFailedInvalidConfiguration,
-  kFailedFinishProcessing,
-  kSkippedProcessingHints,
-  kProcessedNoHints,
+  kSuccess = 0,
+  kFailedInvalidParameters = 1,
+  kFailedReadingFile = 2,
+  kFailedInvalidConfiguration = 3,
+  kFailedFinishProcessing = 4,
+  kSkippedProcessingHints = 5,
+  kProcessedNoHints = 6,
+  kFailedPreviouslyAttemptedVersionInvalid = 7,
 
   // Insert new values before this line.
-  kMaxValue = kProcessedNoHints,
+  kMaxValue = kFailedPreviouslyAttemptedVersionInvalid,
 };
 
 // Records the ProcessHintsComponentResult to UMA.
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer.cc b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
index 3ed7842..54e64ff 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer.cc
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer.cc
@@ -69,13 +69,13 @@
 bool ShouldProcessNavigation(content::NavigationHandle* navigation_handle) {
   if (!navigation_handle->IsInMainFrame())
     return false;
-  // Ignore navigations not happening in the primary FrameTree. Using IsCurrent
+  // Ignore navigations not happening in the primary FrameTree. Using IsActive
   // as a proxy for "is in primary FrameTree".
   // TODO(https://crbug.com/1190112): Add proper support for prerendering when
   // there are better content APIs.
   content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
       navigation_handle->GetPreviousRenderFrameHostId());
-  return !rfh || rfh->IsCurrent();
+  return !rfh || rfh->IsActive();
 }
 
 }  // namespace
@@ -194,7 +194,7 @@
   auto* render_frame_host =
       content::RenderFrameHost::FromID(id.frame_routing_id);
 
-  if (!render_frame_host || !render_frame_host->GetMainFrame()->IsCurrent()) {
+  if (!render_frame_host || !render_frame_host->GetMainFrame()->IsActive()) {
     // Ignore media that starts playing in a page that was navigated away
     // from.
     return;
@@ -349,7 +349,7 @@
     //
     // TODO(crbug.com/738577): use a DocumentId here instead, to eliminate this
     // race.
-    if (render_frame_host_or_null->GetMainFrame()->IsCurrent()) {
+    if (render_frame_host_or_null->GetMainFrame()->IsActive()) {
       return committed_load_.get();
     }
   }
@@ -491,7 +491,7 @@
   }
   if (!navigation_handle->IsInMainFrame()) {
     if (committed_load_ && navigation_handle->GetParentFrame() &&
-        navigation_handle->GetParentFrame()->GetMainFrame()->IsCurrent()) {
+        navigation_handle->GetParentFrame()->GetMainFrame()->IsActive()) {
       committed_load_->DidFinishSubFrameNavigation(navigation_handle);
       committed_load_->metrics_update_dispatcher()->DidFinishSubFrameNavigation(
           navigation_handle);
@@ -883,7 +883,7 @@
   // TODO(crbug.com/1061060): We should not ignore page timings if the page is
   // in bfcache.
   // TODO(https://crbug.com/1190112): Add support for Prerender
-  if (!render_frame_host->GetMainFrame()->IsCurrent()) {
+  if (!render_frame_host->GetMainFrame()->IsActive()) {
     RecordInternalError(ERR_IPC_FROM_WRONG_FRAME);
     return;
   }
@@ -1004,10 +1004,10 @@
   // data from frames that have already been navigated away from. However, this
   // could be false if this is called for the page that is prerendering with
   // MPArch. Therefore, ignore navigations not happening in the primary
-  // FrameTree. Using IsCurrent as a proxy for "is in primary FrameTree".
+  // FrameTree. Using IsActive as a proxy for "is in primary FrameTree".
   // TODO(https://crbug.com/1190112): Add proper support for prerendering when
   // there are better content APIs.
-  if (!render_frame_host->GetMainFrame()->IsCurrent())
+  if (!render_frame_host->GetMainFrame()->IsActive())
     return;
 
   if (!committed_load_) {
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc b/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
index 57d6923b0..47991d1f 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
@@ -1425,7 +1425,7 @@
   content::NavigationSimulator::NavigateAndCommitFromBrowser(
       web_contents(), GURL(kDefaultTestUrl2));
 
-  DCHECK(!old_rfh->IsCurrent());
+  DCHECK(!old_rfh->IsActive());
   observer()->ResourceLoadComplete(
       old_rfh, content::GlobalRequestID(),
       *CreateResourceLoadInfo(GURL("http://www.other.com/"),
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index 6121030..30248ca 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -194,8 +194,8 @@
 }
 
 bool ContentPasswordManagerDriver::CanShowAutofillUi() const {
-  // Don't show AutofillUi for non-current RenderFrameHost.
-  return render_frame_host_->IsCurrent();
+  // Don't show AutofillUi for inactive RenderFrameHost.
+  return render_frame_host_->IsActive();
 }
 
 const GURL& ContentPasswordManagerDriver::GetLastCommittedURL() const {
diff --git a/components/payments/content/android_payment_app.cc b/components/payments/content/android_payment_app.cc
index 60456849..262c2452 100644
--- a/components/payments/content/android_payment_app.cc
+++ b/components/payments/content/android_payment_app.cc
@@ -60,7 +60,7 @@
 
   content::RenderFrameHost* rfh =
       content::RenderFrameHost::FromID(frame_routing_id_);
-  if (!rfh || !rfh->IsCurrent())
+  if (!rfh || !rfh->IsActive())
     return;
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(rfh);
diff --git a/components/payments/content/installable_payment_app_crawler.cc b/components/payments/content/installable_payment_app_crawler.cc
index 9b391fc2..7333336 100644
--- a/components/payments/content/installable_payment_app_crawler.cc
+++ b/components/payments/content/installable_payment_app_crawler.cc
@@ -427,7 +427,7 @@
   // TODO(crbug.com/1058840): Move this sanity check to ManifestIconDownloader
   // after DownloadImage refactor is done.
   auto* rfh = content::RenderFrameHost::FromID(initiator_frame_routing_id_);
-  auto* web_contents = rfh && rfh->IsCurrent()
+  auto* web_contents = rfh && rfh->IsActive()
                            ? content::WebContents::FromRenderFrameHost(rfh)
                            : nullptr;
   if (!web_contents) {
diff --git a/components/payments/content/payment_credential.cc b/components/payments/content/payment_credential.cc
index 721824b..7a61cb1 100644
--- a/components/payments/content/payment_credential.cc
+++ b/components/payments/content/payment_credential.cc
@@ -26,7 +26,7 @@
 // static
 bool PaymentCredential::IsFrameAllowedToUseSecurePaymentConfirmation(
     content::RenderFrameHost* rfh) {
-  return rfh && rfh->IsCurrent() &&
+  return rfh && rfh->IsActive() &&
          rfh->IsFeatureEnabled(
              blink::mojom::PermissionsPolicyFeature::kPayment) &&
          base::FeatureList::IsEnabled(features::kSecurePaymentConfirmation);
diff --git a/components/payments/content/payment_request.cc b/components/payments/content/payment_request.cc
index bbd570c..5509338 100644
--- a/components/payments/content/payment_request.cc
+++ b/components/payments/content/payment_request.cc
@@ -906,9 +906,8 @@
 
 content::WebContents* PaymentRequest::web_contents() {
   auto* rfh = content::RenderFrameHost::FromID(initiator_frame_routing_id_);
-  return rfh && rfh->IsCurrent()
-             ? content::WebContents::FromRenderFrameHost(rfh)
-             : nullptr;
+  return rfh && rfh->IsActive() ? content::WebContents::FromRenderFrameHost(rfh)
+                                : nullptr;
 }
 
 void PaymentRequest::RecordFirstAbortReason(
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 4d09609c..dfcbf279 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -97,9 +97,8 @@
 
 content::WebContents* PaymentRequestState::GetWebContents() {
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  return rfh && rfh->IsCurrent()
-             ? content::WebContents::FromRenderFrameHost(rfh)
-             : nullptr;
+  return rfh && rfh->IsActive() ? content::WebContents::FromRenderFrameHost(rfh)
+                                : nullptr;
 }
 
 ContentPaymentRequestDelegate* PaymentRequestState::GetPaymentRequestDelegate()
diff --git a/components/payments/content/service_worker_payment_app_factory.cc b/components/payments/content/service_worker_payment_app_factory.cc
index 54da818..882a955 100644
--- a/components/payments/content/service_worker_payment_app_factory.cc
+++ b/components/payments/content/service_worker_payment_app_factory.cc
@@ -182,7 +182,7 @@
 
 void ServiceWorkerPaymentAppFactory::Create(base::WeakPtr<Delegate> delegate) {
   auto* rfh = delegate->GetInitiatorRenderFrameHost();
-  if (!rfh || !rfh->IsCurrent() || !delegate->GetWebContents())
+  if (!rfh || !rfh->IsActive() || !delegate->GetWebContents())
     return;  // The frame or page is being unloaded.
 
   auto creator = std::make_unique<ServiceWorkerPaymentAppCreator>(
diff --git a/components/payments/content/service_worker_payment_app_finder.cc b/components/payments/content/service_worker_payment_app_finder.cc
index ff439183..676dea39 100644
--- a/components/payments/content/service_worker_payment_app_finder.cc
+++ b/components/payments/content/service_worker_payment_app_finder.cc
@@ -132,7 +132,7 @@
       base::OnceClosure finished_using_resources_callback) {
     DCHECK(!verifier_);
     DCHECK(initiator_render_frame_host);
-    DCHECK(initiator_render_frame_host->IsCurrent());
+    DCHECK(initiator_render_frame_host->IsActive());
 
     downloader_ = std::move(downloader);
 
@@ -408,7 +408,7 @@
   DCHECK(!requested_method_data.empty());
 
   auto* rfh = content::RenderFrameHost::FromID(frame_routing_id_);
-  if (!rfh || !rfh->IsCurrent())
+  if (!rfh || !rfh->IsActive())
     return;
 
   // Do not look up payment handlers for ignored payment methods.
diff --git a/components/performance_manager/performance_manager_tab_helper.cc b/components/performance_manager/performance_manager_tab_helper.cc
index 46c494c2..598c612 100644
--- a/components/performance_manager/performance_manager_tab_helper.cc
+++ b/components/performance_manager/performance_manager_tab_helper.cc
@@ -179,7 +179,7 @@
                 frame_node->SetIsCurrent(is_current);
               },
               render_frame_host->GetLastCommittedURL(),
-              render_frame_host->IsCurrent()));
+              render_frame_host->IsActive()));
 
   frames_[render_frame_host] = std::move(frame);
 }
@@ -435,7 +435,7 @@
     const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
   // This favicon change might have been initiated by a different frame some
   // time ago and the main frame might have changed.
-  if (!render_frame_host->IsCurrent())
+  if (!render_frame_host->IsActive())
     return;
 
   // TODO(siggi): This logic belongs in the policy layer rather than here.
diff --git a/components/policy/core/common/cloud/enterprise_metrics.cc b/components/policy/core/common/cloud/enterprise_metrics.cc
index f54e991..79d8595 100644
--- a/components/policy/core/common/cloud/enterprise_metrics.cc
+++ b/components/policy/core/common/cloud/enterprise_metrics.cc
@@ -189,8 +189,11 @@
     "Enterprise.AutoEnrollmentPsmHashDanceDifferentResultsComparison";
 const char kUMAPsmSuccessTime[] =
     "Enterprise.AutoEnrollmentPrivateSetMembershipSuccessTime";
-const char kUMAPsmRequestStatus[] =
-    "Enterprise.AutoEnrollmentPrivateSetMembershipRequestStatus";
+const char kUMAPsmResult[] = "Enterprise.AutoEnrollmentPsmResult";
+const char kUMAPsmNetworkErrorCode[] =
+    "Enterprise.AutoEnrollmentPsmRequestNetworkErrorCode";
+const char kUMAPsmDmServerRequestStatus[] =
+    "Enterprise.AutoEnrollmentPsmDmServerRequestStatus";
 
 const char kUMAHashDanceSuccessTime[] =
     "Enterprise.AutoEnrollmentHashDanceSuccessTime";
diff --git a/components/policy/core/common/cloud/enterprise_metrics.h b/components/policy/core/common/cloud/enterprise_metrics.h
index ff93aa2..764c858 100644
--- a/components/policy/core/common/cloud/enterprise_metrics.h
+++ b/components/policy/core/common/cloud/enterprise_metrics.h
@@ -261,7 +261,9 @@
 POLICY_EXPORT extern const char kUMAPsmHashDanceComparison[];
 POLICY_EXPORT extern const char kUMAPsmHashDanceDifferentResultsComparison[];
 POLICY_EXPORT extern const char kUMAPsmSuccessTime[];
-POLICY_EXPORT extern const char kUMAPsmRequestStatus[];
+POLICY_EXPORT extern const char kUMAPsmResult[];
+POLICY_EXPORT extern const char kUMAPsmNetworkErrorCode[];
+POLICY_EXPORT extern const char kUMAPsmDmServerRequestStatus[];
 
 // DeviceAutoEnrollmentRequest i.e. hash dance request UMA histogram names.
 POLICY_EXPORT extern const char kUMAHashDanceSuccessTime[];
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index ac737cf9..8d68cc4 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -7085,9 +7085,9 @@
         },
       ],
       'supported_on': [
-        'chrome.*:79-',
-        'chrome_os:79-',
-        'android:79-',
+        'chrome.*:79-92',
+        'chrome_os:79-92',
+        'android:79-92',
       ],
       'features': {
         'dynamic_refresh': True,
@@ -7097,7 +7097,8 @@
       'id': 623,
       'caption': '''Default legacy <ph name="ATTRIBUTE_SAMESITE_NAME">SameSite</ph> cookie behavior setting''',
       'tags': [],
-      'desc': '''Allows you to revert all cookies to legacy <ph name="ATTRIBUTE_SAMESITE_NAME">SameSite</ph> behavior. Reverting to legacy behavior causes cookies that don't specify a <ph name="ATTRIBUTE_SAMESITE_NAME">SameSite</ph> attribute to be treated as if they were "<ph name="ATTRIBUTE_VALUE_SAMESITE_NONE">SameSite=None</ph>", removes the requirement for "<ph name="ATTRIBUTE_VALUE_SAMESITE_NONE">SameSite=None</ph>" cookies to carry the "<ph name="ATTRIBUTE_SECURE_NAME">Secure</ph>" attribute, and skips the scheme comparison when evaluating if two sites are same-site. See https://www.chromium.org/administrators/policy-list-3/cookie-legacy-samesite-policies for full description.
+      'deprecated': True,
+      'desc': '''This policy is deprecated, if you still require legacy cookie behavior please use <ph name="LEGACY_SAMESITE_COOKIE_BEHAVIOR_ENABLED_FOR_DOMAIN_LIST_POLICY_NAME">LegacySameSiteCookieBehaviorEnabledForDomainList</ph>. Allows you to revert all cookies to legacy <ph name="ATTRIBUTE_SAMESITE_NAME">SameSite</ph> behavior. Reverting to legacy behavior causes cookies that don't specify a <ph name="ATTRIBUTE_SAMESITE_NAME">SameSite</ph> attribute to be treated as if they were "<ph name="ATTRIBUTE_VALUE_SAMESITE_NONE">SameSite=None</ph>", removes the requirement for "<ph name="ATTRIBUTE_VALUE_SAMESITE_NONE">SameSite=None</ph>" cookies to carry the "<ph name="ATTRIBUTE_SECURE_NAME">Secure</ph>" attribute, and skips the scheme comparison when evaluating if two sites are same-site. See https://www.chromium.org/administrators/policy-list-3/cookie-legacy-samesite-policies for full description.
 
           When this policy is not set, the default <ph name="ATTRIBUTE_SAMESITE_NAME">SameSite</ph> behavior for cookies will depend on the user's personal configuration for the <ph name="FEATURE_NAME_SAMESITE_BY_DEFAULT_COOKIES">SameSite-by-default</ph> feature, the <ph name="FEATURE_NAME_SAMESITE_NONE_MUST_BE_SECURE">Cookies-without-SameSite-must-be-secure</ph> feature, and the <ph name="FEATURE_NAME_SCHEMEFUL_SAME_SITE">Schemeful Same-Site</ph> feature which may be set by a field trial or by enabling or disabling the <ph name="FLAG_NAME_SAMESITE_BY_DEFAULT_COOKIES">same-site-by-default-cookies</ph> flag, the <ph name="FLAG_NAME_SAMESITE_NONE_MUST_BE_SECURE">cookies-without-same-site-must-be-secure</ph> flag, or the <ph name="FLAG_NAME_SCHEMEFUL_SAME_SITE">schemeful-same-site</ph> flag, respectively.''',
     },
diff --git a/components/services/storage/service_worker/service_worker_database_unittest.cc b/components/services/storage/service_worker/service_worker_database_unittest.cc
index a7454b2..e0f7625 100644
--- a/components/services/storage/service_worker/service_worker_database_unittest.cc
+++ b/components/services/storage/service_worker/service_worker_database_unittest.cc
@@ -780,7 +780,7 @@
       ServiceWorkerDatabase::Status::kOk,
       database->DeleteRegistration(
           kNonExistentRegistrationId,
-          blink::StorageKey(url::Origin::Create(GURL("https://example.net"))),
+          blink::StorageKey::CreateFromStringForTesting("https://example.net"),
           &deleted_version));
   EXPECT_EQ(blink::mojom::kInvalidServiceWorkerVersionId,
             deleted_version.version_id);
@@ -2426,8 +2426,8 @@
 
   // https://googlechrome.github.io/samples/service-worker/basic/ provided the
   // service worker for this test.
-  blink::StorageKey key(
-      url::Origin::Create(GURL("https://googlechrome.github.io/")));
+  blink::StorageKey key = blink::StorageKey::CreateFromStringForTesting(
+      "https://googlechrome.github.io/");
 
   std::vector<mojom::ServiceWorkerRegistrationDataPtr> registrations;
   std::vector<std::vector<ResourceRecordPtr>> resources_list;
diff --git a/components/url_formatter/tools/BUILD.gn b/components/url_formatter/tools/BUILD.gn
index cb471141..f038c1e 100644
--- a/components/url_formatter/tools/BUILD.gn
+++ b/components/url_formatter/tools/BUILD.gn
@@ -8,5 +8,6 @@
     "//base",
     "//base:i18n",
     "//components/url_formatter:url_formatter",
+    "//url:url",
   ]
 }
diff --git a/components/url_formatter/tools/format_url.cc b/components/url_formatter/tools/format_url.cc
index 42ed52c..20063ddc 100644
--- a/components/url_formatter/tools/format_url.cc
+++ b/components/url_formatter/tools/format_url.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// This binary takes a list of domain names, tries to convert them to unicode
-// and prints out the result. The list can be passed as a text file or via
-// stdin. In both cases, the output is printed as (input_domain, output_domain,
-// spoof_check_result) tuples on separate lines. spoof_check_result is the
-// string representation of IDNSpoofChecker::Result enum with an additional
-// kTopDomainLookalike value.
+// This binary takes a list of domain names in ASCII or unicode, passes them
+// through the IDN decoding algorithm and prints out the result. The list can be
+// passed as a text file or via stdin. In both cases, the output is printed as
+// (input_domain, output_domain, spoof_check_result) tuples on separate lines.
+// spoof_check_result is the string representation of IDNSpoofChecker::Result
+// enum with an additional kTopDomainLookalike value.
 
 #include <cstdlib>
 #include <fstream>
@@ -22,6 +22,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "components/url_formatter/spoof_checks/idn_spoof_checker.h"
 #include "components/url_formatter/url_formatter.h"
+#include "url/gurl.h"
 
 using url_formatter::IDNConversionResult;
 using url_formatter::IDNSpoofChecker;
@@ -31,11 +32,12 @@
   std::cout << process_name << " <file>" << std::endl;
   std::cout << std::endl;
   std::cout << "<file> is a text file with one hostname per line." << std::endl;
-  std::cout << "Hostnames must be in ASCII. Internationalized domain names "
-               "(IDN) must be encoded in punycode."
+  std::cout << "Hostnames can be ASCII or unicode. Internationalized domain "
+               "can (IDN) be encoded in unicode or punycode."
             << std::endl;
   std::cout << "Each hostname is converted to unicode, if safe. Otherwise, "
-            << "it's printed unchanged." << std::endl;
+            << "ASCII hostnames are printed unchanged and unicode hostnames "
+            << "are printed in punycode." << std::endl;
 }
 
 std::string SpoofCheckResultToString(IDNSpoofChecker::Result result) {
@@ -93,23 +95,24 @@
 void Convert(std::istream& input) {
   base::i18n::InitializeICU();
   for (std::string line; std::getline(input, line);) {
-    CHECK(!base::StartsWith(line,
-                            "http:", base::CompareCase::INSENSITIVE_ASCII) &&
-          !base::StartsWith(line,
-                            "https:", base::CompareCase::INSENSITIVE_ASCII) &&
-          base::IsStringASCII(line))
-        << "This binary only accepts hostnames in ASCII form (punycode for "
-           "IDN): "
-        << line;
+    CHECK(
+        !base::StartsWith(line,
+                          "http:", base::CompareCase::INSENSITIVE_ASCII) &&
+        !base::StartsWith(line, "https:", base::CompareCase::INSENSITIVE_ASCII))
+        << "This binary only accepts hostnames" << line;
+
+    const std::string ascii_hostname =
+        base::IsStringASCII(line) ? line : GURL("https://" + line).host();
 
     // Convert twice, first with spoof checks on, then with spoof checks
     // ignored inside GetSpoofCheckResult(). This is because only the call to
     // UnsafeIDNToUnicodeWithDetails returns information about spoof check
     // results (a quirk of the url_formatter interface).
-    const std::u16string converted_hostname = url_formatter::IDNToUnicode(line);
+    const std::u16string converted_hostname =
+        url_formatter::IDNToUnicode(ascii_hostname);
     const std::string spoof_check_result =
-        GetSpoofCheckResult(line, converted_hostname);
-    std::cout << line << ", " << converted_hostname << ", "
+        GetSpoofCheckResult(ascii_hostname, converted_hostname);
+    std::cout << ascii_hostname << ", " << converted_hostname << ", "
               << spoof_check_result << std::endl;
   }
 }
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn
index 23163a2..714422a 100644
--- a/components/variations/BUILD.gn
+++ b/components/variations/BUILD.gn
@@ -35,7 +35,7 @@
   deps = [ "//base" ]
 }
 
-static_library("variations") {
+component("variations") {
   sources = [
     "active_field_trials.cc",
     "active_field_trials.h",
@@ -89,6 +89,9 @@
     "variations_url_constants.h",
   ]
 
+  # Needed for exporting functions (because the target type is a component).
+  defines = [ "IS_VARIATIONS_IMPL" ]
+
   if (is_android || is_ios) {
     sources += [
       "variations_request_scheduler_mobile.cc",
@@ -190,7 +193,6 @@
     "study_filtering_unittest.cc",
     "synthetic_trial_registry_unittest.cc",
     "variations_associated_data_unittest.cc",
-    "variations_crash_keys_unittest.cc",
     "variations_ids_provider_unittest.cc",
     "variations_murmur_hash_unittest.cc",
     "variations_request_scheduler_unittest.cc",
@@ -207,6 +209,14 @@
     sources += [ "variations_crash_keys_chromeos_unittest.cc" ]
   }
 
+  # This test uses crash_reporter::GetCrashKeyValue(), which in a component
+  # build returns the crash keys in the unit-test, not the "variations"
+  # target. The test verifies the keys in the "variations" target, not the
+  # test. As such, it fails in component builds.
+  if (!is_component_build) {
+    sources += [ "variations_crash_keys_unittest.cc" ]
+  }
+
   deps = [
     ":test_support",
     ":variations",
diff --git a/components/variations/active_field_trials.h b/components/variations/active_field_trials.h
index a1cba30..0307a98 100644
--- a/components/variations/active_field_trials.h
+++ b/components/variations/active_field_trials.h
@@ -9,6 +9,7 @@
 
 #include <string>
 
+#include "base/component_export.h"
 #include "base/metrics/field_trial.h"
 #include "base/strings/string_piece.h"
 
@@ -16,18 +17,19 @@
 
 // The Unique ID of a trial and its active group, where the name and group
 // identifiers are hashes of the trial and group name strings.
-struct ActiveGroupId {
+struct COMPONENT_EXPORT(VARIATIONS) ActiveGroupId {
   uint32_t name;
   uint32_t group;
 };
 
 // Returns an ActiveGroupId struct for the given trial and group names.
+COMPONENT_EXPORT(VARIATIONS)
 ActiveGroupId MakeActiveGroupId(base::StringPiece trial_name,
                                 base::StringPiece group_name);
 
 // We need to supply a Compare class for templates since ActiveGroupId is a
 // user-defined type.
-struct ActiveGroupIdCompare {
+struct COMPONENT_EXPORT(VARIATIONS) ActiveGroupIdCompare {
   bool operator() (const ActiveGroupId& lhs, const ActiveGroupId& rhs) const {
     // The group and name fields are just SHA-1 Hashes, so we just need to treat
     // them as IDs and do a less-than comparison. We test group first, since
@@ -43,6 +45,7 @@
 // Field Trials for which a group has not been chosen yet are NOT returned in
 // this list. Field trial names are suffixed with |suffix| before hashing is
 // executed.
+COMPONENT_EXPORT(VARIATIONS)
 void GetFieldTrialActiveGroupIds(base::StringPiece suffix,
                                  std::vector<ActiveGroupId>* name_group_ids);
 
@@ -52,6 +55,7 @@
 // with the names as hex strings. Field Trials for which a group has not been
 // chosen yet are NOT returned in this list. Field trial names are suffixed with
 // |suffix| before hashing is executed.
+COMPONENT_EXPORT(VARIATIONS)
 void GetFieldTrialActiveGroupIdsAsStrings(base::StringPiece suffix,
                                           std::vector<std::string>* output);
 
@@ -61,12 +65,14 @@
 // group. The strings are formatted as "<TrialName>-<GroupName>",
 // with the names as hex strings. Synthetic Field Trials for which a group
 // which hasn't been chosen yet are NOT returned in this list.
+COMPONENT_EXPORT(VARIATIONS)
 void GetSyntheticTrialGroupIdsAsString(std::vector<std::string>* output);
 
 // Sets the version of the seed that the current set of FieldTrials was
 // generated from.
 // TODO(crbug/507665): Move this to field_trials_provider once it moves
 // into components/variations
+COMPONENT_EXPORT(VARIATIONS)
 void SetSeedVersion(const std::string& seed_version);
 
 // Gets the version of the seed that the current set of FieldTrials was
@@ -75,12 +81,14 @@
 // processes.
 // TODO(crbug/507665): Move this to field_trials_provider once it moves
 // into components/variations
+COMPONENT_EXPORT(VARIATIONS)
 const std::string& GetSeedVersion();
 
 // Expose some functions for testing. These functions just wrap functionality
 // that is implemented above.
 namespace testing {
 
+COMPONENT_EXPORT(VARIATIONS)
 void TestGetFieldTrialActiveGroupIds(
     base::StringPiece suffix,
     const base::FieldTrial::ActiveGroups& active_groups,
diff --git a/components/variations/android/variations_seed_bridge.h b/components/variations/android/variations_seed_bridge.h
index e4ac1854..5b1c2ed 100644
--- a/components/variations/android/variations_seed_bridge.h
+++ b/components/variations/android/variations_seed_bridge.h
@@ -8,32 +8,35 @@
 #include <jni.h>
 #include <string>
 
+#include "base/component_export.h"
 #include "components/variations/seed_response.h"
 
 namespace variations {
 namespace android {
 
 // Return the first run seed data pulled from the Java side of application.
+COMPONENT_EXPORT(VARIATIONS)
 std::unique_ptr<variations::SeedResponse> GetVariationsFirstRunSeed();
 
 // Clears first run seed preferences stored on the Java side of Chrome for
 // Android.
-void ClearJavaFirstRunPrefs();
+COMPONENT_EXPORT(VARIATIONS) void ClearJavaFirstRunPrefs();
 
 // Marks variations seed as stored to avoid repeated fetches of the seed at
 // the Java side.
-void MarkVariationsSeedAsStored();
+COMPONENT_EXPORT(VARIATIONS) void MarkVariationsSeedAsStored();
 
 // Sets test data on the Java side. The data is pulled during the unit tests to
 // C++ side and is being checked for consistency.
 // This method is used for unit testing purposes only.
+COMPONENT_EXPORT(VARIATIONS)
 void SetJavaFirstRunPrefsForTesting(const std::string& seed_data,
                                     const std::string& seed_signature,
                                     const std::string& seed_country,
                                     long response_date,
                                     bool is_gzip_compressed);
 
-bool HasMarkedPrefsForTesting();
+COMPONENT_EXPORT(VARIATIONS) bool HasMarkedPrefsForTesting();
 
 }  // namespace android
 }  // namespace variations
diff --git a/components/variations/child_process_field_trial_syncer.h b/components/variations/child_process_field_trial_syncer.h
index ed6b570..6931731b 100644
--- a/components/variations/child_process_field_trial_syncer.h
+++ b/components/variations/child_process_field_trial_syncer.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
 #include "base/threading/thread_local.h"
@@ -19,7 +20,8 @@
 // processes. Specifically, when a field trial is activated in the browser, it
 // also activates it in the child process and when a field trial is activated in
 // the child process, it notifies the browser process to activate it.
-class ChildProcessFieldTrialSyncer : public base::FieldTrialList::Observer {
+class COMPONENT_EXPORT(VARIATIONS) ChildProcessFieldTrialSyncer
+    : public base::FieldTrialList::Observer {
  public:
   using FieldTrialActivatedCallback =
       base::RepeatingCallback<void(const std::string& trial_name)>;
diff --git a/components/variations/client_filterable_state.h b/components/variations/client_filterable_state.h
index 06c3c406..2e1ab5dd 100644
--- a/components/variations/client_filterable_state.h
+++ b/components/variations/client_filterable_state.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/callback.h"
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/time/time.h"
 #include "base/version.h"
@@ -31,7 +32,7 @@
 using IsEnterpriseFunction = base::OnceCallback<bool()>;
 
 // A container for all of the client state which is used for filtering studies.
-struct ClientFilterableState {
+struct COMPONENT_EXPORT(VARIATIONS) ClientFilterableState {
   static Study::Platform GetCurrentPlatform();
 
   // base::Version used in {min,max}_os_version filtering.
diff --git a/components/variations/entropy_provider.h b/components/variations/entropy_provider.h
index 0f765b16..8cd0109 100644
--- a/components/variations/entropy_provider.h
+++ b/components/variations/entropy_provider.h
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
 
@@ -23,7 +24,8 @@
 // It works by taking the first 64 bits of the SHA1 hash of the entropy source
 // concatenated with the trial name, or randomization seed and using that for
 // the final entropy value.
-class SHA1EntropyProvider : public base::FieldTrial::EntropyProvider {
+class COMPONENT_EXPORT(VARIATIONS) SHA1EntropyProvider
+    : public base::FieldTrial::EntropyProvider {
  public:
   // Creates a SHA1EntropyProvider with the given |entropy_source|, which
   // should contain a large amount of entropy - for example, a textual
@@ -47,7 +49,7 @@
 // the actual low entropy source's hash would fall in the sorted list of all
 // those hashes, and uses that as the final value. For more info, see:
 // https://docs.google.com/document/d/1cPF5PruriWNP2Z5gSkq4MBTm0wSZqLyIJkUO9ekibeo
-class NormalizedMurmurHashEntropyProvider
+class COMPONENT_EXPORT(VARIATIONS) NormalizedMurmurHashEntropyProvider
     : public base::FieldTrial::EntropyProvider {
  public:
   NormalizedMurmurHashEntropyProvider(uint16_t low_entropy_source,
diff --git a/components/variations/hashing.h b/components/variations/hashing.h
index 149813f..2cb0760 100644
--- a/components/variations/hashing.h
+++ b/components/variations/hashing.h
@@ -7,13 +7,14 @@
 
 #include <stdint.h>
 
+#include "base/component_export.h"
 #include "base/strings/string_piece.h"
 
 namespace variations {
 
 // Computes a uint32_t hash of a given string based on its SHA1 hash. Suitable
 // for uniquely identifying field trial names and group names.
-uint32_t HashName(base::StringPiece name);
+COMPONENT_EXPORT(VARIATIONS) uint32_t HashName(base::StringPiece name);
 
 }  // namespace variations
 
diff --git a/components/variations/metrics.h b/components/variations/metrics.h
index 660a4751..4febb0dd 100644
--- a/components/variations/metrics.h
+++ b/components/variations/metrics.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_VARIATIONS_METRICS_H_
 #define COMPONENTS_VARIATIONS_METRICS_H_
 
+#include "base/component_export.h"
 #include "build/build_config.h"
 
 namespace variations {
@@ -89,19 +90,21 @@
 
 #if defined(OS_ANDROID)
 // Records the result of importing a seed during Android first run.
+COMPONENT_EXPORT(VARIATIONS)
 void RecordFirstRunSeedImportResult(FirstRunSeedImportResult result);
 #endif  // OS_ANDROID
 
 // Records the result of attempting to load the latest variations seed on
 // startup.
-void RecordLoadSeedResult(LoadSeedResult state);
+COMPONENT_EXPORT(VARIATIONS) void RecordLoadSeedResult(LoadSeedResult state);
 
 // Records the result of attempting to load the safe variations seed on startup.
+COMPONENT_EXPORT(VARIATIONS)
 void RecordLoadSafeSeedResult(LoadSeedResult state);
 
 // Records the result of attempting to store a variations seed received from the
 // server.
-void RecordStoreSeedResult(StoreSeedResult result);
+COMPONENT_EXPORT(VARIATIONS) void RecordStoreSeedResult(StoreSeedResult result);
 
 }  // namespace variations
 
diff --git a/components/variations/platform_field_trials.h b/components/variations/platform_field_trials.h
index 335cd0e..df468a6 100644
--- a/components/variations/platform_field_trials.h
+++ b/components/variations/platform_field_trials.h
@@ -5,13 +5,14 @@
 #ifndef COMPONENTS_VARIATIONS_PLATFORM_FIELD_TRIALS_H_
 #define COMPONENTS_VARIATIONS_PLATFORM_FIELD_TRIALS_H_
 
+#include "base/component_export.h"
 #include "base/metrics/field_trial.h"
 
 namespace variations {
 
 // Infrastructure for setting up platform specific field trials. Chrome and
 // WebView make use through their corresponding subclasses.
-class PlatformFieldTrials {
+class COMPONENT_EXPORT(VARIATIONS) PlatformFieldTrials {
  public:
   PlatformFieldTrials() = default;
   virtual ~PlatformFieldTrials() = default;
diff --git a/components/variations/pref_names.h b/components/variations/pref_names.h
index ca7e4e7..a7ef1d3 100644
--- a/components/variations/pref_names.h
+++ b/components/variations/pref_names.h
@@ -5,31 +5,40 @@
 #ifndef COMPONENTS_VARIATIONS_PREF_NAMES_H_
 #define COMPONENTS_VARIATIONS_PREF_NAMES_H_
 
+#include "base/component_export.h"
+
 namespace variations {
 namespace prefs {
 
 // Alphabetical list of preference names specific to the variations component.
 // Keep alphabetized and document each in the .cc file.
 
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kDeviceVariationsRestrictionsByPolicy[];
-extern const char kVariationsCompressedSeed[];
-extern const char kVariationsCountry[];
-extern const char kVariationsCrashStreak[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsCompressedSeed[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsCountry[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsCrashStreak[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsFailedToFetchSeedStreak[];
-extern const char kVariationsLastFetchTime[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsLastFetchTime[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsPermanentConsistencyCountry[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsPermanentOverriddenCountry[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsRestrictionsByPolicy[];
-extern const char kVariationsRestrictParameter[];
-extern const char kVariationsSafeCompressedSeed[];
-extern const char kVariationsSafeSeedDate[];
-extern const char kVariationsSafeSeedFetchTime[];
-extern const char kVariationsSafeSeedLocale[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsRestrictParameter[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsSafeCompressedSeed[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsSafeSeedDate[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsSafeSeedFetchTime[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsSafeSeedLocale[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsSafeSeedPermanentConsistencyCountry[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsSafeSeedSessionConsistencyCountry[];
-extern const char kVariationsSafeSeedSignature[];
-extern const char kVariationsSeedDate[];
-extern const char kVariationsSeedSignature[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsSafeSeedSignature[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsSeedDate[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kVariationsSeedSignature[];
 
 }  // namespace prefs
 }  // namespace variations
diff --git a/components/variations/processed_study.h b/components/variations/processed_study.h
index 504b5f4..584e7410 100644
--- a/components/variations/processed_study.h
+++ b/components/variations/processed_study.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/metrics/field_trial.h"
 
 namespace variations {
@@ -16,7 +17,7 @@
 
 // Wrapper over Study with extra information computed during pre-processing,
 // such as whether the study is expired and its total probability.
-class ProcessedStudy {
+class COMPONENT_EXPORT(VARIATIONS) ProcessedStudy {
  public:
   // The default group used when a study doesn't specify one. This is needed
   // because the field trial api requires a default group name.
diff --git a/components/variations/seed_response.h b/components/variations/seed_response.h
index 058bb11..bee917c07 100644
--- a/components/variations/seed_response.h
+++ b/components/variations/seed_response.h
@@ -7,12 +7,14 @@
 
 #include <string>
 
+#include "base/component_export.h"
+
 namespace variations {
 
 // Represents data received when downloading the seed: "data" is the response
 // body while the other fields come from headers.
 // This is only used on Android.
-struct SeedResponse {
+struct COMPONENT_EXPORT(VARIATIONS) SeedResponse {
   SeedResponse();
   ~SeedResponse();
 
diff --git a/components/variations/study_filtering.h b/components/variations/study_filtering.h
index aba51115..e4ec9de 100644
--- a/components/variations/study_filtering.h
+++ b/components/variations/study_filtering.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/component_export.h"
 #include "base/time/time.h"
 #include "base/version.h"
 #include "components/variations/client_filterable_state.h"
@@ -25,66 +26,82 @@
 namespace internal {
 
 // Checks whether a study is applicable for the given |channel| per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyChannel(const Study::Filter& filter, Study::Channel channel);
 
 // Checks whether a study is applicable for the given |form_factor| per
 // |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyFormFactor(const Study::Filter& filter,
                           Study::FormFactor form_factor);
 
 // Checks whether a study is applicable for the given |hardware_class| per
 // |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyHardwareClass(const Study::Filter& filter,
                              const std::string& hardware_class);
 
 // Checks whether a study is applicable for the given |locale| per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyLocale(const Study::Filter& filter, const std::string& locale);
 
 // Checks whether a study is applicable for the given |platform| per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyPlatform(const Study::Filter& filter, Study::Platform platform);
 
 // Checks whether a study is applicable given |is_low_end_device| per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyLowEndDevice(const Study::Filter& filter,
                             bool is_low_end_device);
 
 // Checks whether a study is applicable given the ChromeVariations policy value.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyPolicyRestriction(const Study::Filter& filter,
                                  RestrictionPolicy policy_restriction);
 
 // Checks whether a study is applicable for the given date/time per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyStartDate(const Study::Filter& filter,
                          const base::Time& date_time);
 
 // Checks whether a study is applicable for the given date/time per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyEndDate(const Study::Filter& filter,
                        const base::Time& date_time);
 
 // Checks whether a study is applicable for the given version per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyVersion(const Study::Filter& filter,
                        const base::Version& version);
 
 // Checks whether a study is applicable for the given OS version per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyOSVersion(const Study::Filter& filter,
                          const base::Version& os_version);
 
 // Checks whether a study is applicable for the given |country| per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyCountry(const Study::Filter& filter, const std::string& country);
 
 // Checks whether a study is applicable given |is_enterprise| per |filter|.
+COMPONENT_EXPORT(VARIATIONS)
 bool CheckStudyEnterprise(const Study::Filter& filter,
                           const ClientFilterableState& client_state);
 
 // Returns the country that should be used for filtering this study, depending
 // on whether the study has session or permanent consistency.
+COMPONENT_EXPORT(VARIATIONS)
 const std::string& GetClientCountryForStudy(
     const Study& study,
     const ClientFilterableState& client_state);
 
 // Checks whether |study| is expired using the given date/time.
+COMPONENT_EXPORT(VARIATIONS)
 bool IsStudyExpired(const Study& study, const base::Time& date_time);
 
 // Returns whether |study| should be disabled according to the restriction
 // parameters in the |config|.
+COMPONENT_EXPORT(VARIATIONS)
 bool ShouldAddStudy(const Study& study,
                     const ClientFilterableState& client_state,
                     const VariationsLayers& layers);
@@ -95,6 +112,7 @@
 // validates and pre-processes them, adding any kept studies to the
 // |filtered_studies| list. Ensures that the resulting list will not have more
 // than one study with the same name.
+COMPONENT_EXPORT(VARIATIONS)
 void FilterAndValidateStudies(const VariationsSeed& seed,
                               const ClientFilterableState& client_state,
                               const VariationsLayers& layers,
diff --git a/components/variations/synthetic_trial_registry.h b/components/variations/synthetic_trial_registry.h
index 3cfe4de..cb9e584 100644
--- a/components/variations/synthetic_trial_registry.h
+++ b/components/variations/synthetic_trial_registry.h
@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/feature_list.h"
 #include "base/metrics/field_trial_params.h"
 #include "base/observer_list.h"
@@ -24,10 +25,11 @@
 class SyntheticTrialRegistryTest;
 
 namespace internal {
+COMPONENT_EXPORT(VARIATIONS)
 extern const base::Feature kExternalExperimentAllowlist;
 }  // namespace internal
 
-class SyntheticTrialRegistry {
+class COMPONENT_EXPORT(VARIATIONS) SyntheticTrialRegistry {
  public:
   // Constructor that specifies whether the SyntheticTrialRegistry should use
   // an allowlist for external experiments. Some embedders such as WebLayer
diff --git a/components/variations/synthetic_trials.h b/components/variations/synthetic_trials.h
index 89cbd1bf..9586882 100644
--- a/components/variations/synthetic_trials.h
+++ b/components/variations/synthetic_trials.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/component_export.h"
 #include "base/time/time.h"
 #include "components/variations/active_field_trials.h"
 
@@ -18,7 +19,7 @@
 // A Field Trial and its selected group, which represent a particular
 // Chrome configuration state. For example, the trial name could map to
 // a preference name, and the group name could map to a preference value.
-struct SyntheticTrialGroup {
+struct COMPONENT_EXPORT(VARIATIONS) SyntheticTrialGroup {
  public:
   SyntheticTrialGroup(uint32_t trial, uint32_t group);
   ~SyntheticTrialGroup();
@@ -31,7 +32,7 @@
 };
 
 // Interface class to observe changes to synthetic trials in MetricsService.
-class SyntheticTrialObserver {
+class COMPONENT_EXPORT(VARIATIONS) SyntheticTrialObserver {
  public:
   // Called when the list of synthetic field trial groups has changed.
   virtual void OnSyntheticTrialsChanged(
diff --git a/components/variations/synthetic_trials_active_group_id_provider.h b/components/variations/synthetic_trials_active_group_id_provider.h
index 365cacc..17ea2a4 100644
--- a/components/variations/synthetic_trials_active_group_id_provider.h
+++ b/components/variations/synthetic_trials_active_group_id_provider.h
@@ -7,6 +7,7 @@
 
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/synchronization/lock.h"
 #include "components/variations/active_field_trials.h"
@@ -22,7 +23,8 @@
 // This is a helper class which can observe the creation of SyntheticTrialGroups
 // and later provide a list of active group IDs to be included in the crash
 // reports. This class is a thread-safe singleton.
-class SyntheticTrialsActiveGroupIdProvider : public SyntheticTrialObserver {
+class COMPONENT_EXPORT(VARIATIONS) SyntheticTrialsActiveGroupIdProvider
+    : public SyntheticTrialObserver {
  public:
   static SyntheticTrialsActiveGroupIdProvider* GetInstance();
 
diff --git a/components/variations/variations_associated_data.h b/components/variations/variations_associated_data.h
index 214e3189..21da1ca 100644
--- a/components/variations/variations_associated_data.h
+++ b/components/variations/variations_associated_data.h
@@ -88,18 +88,21 @@
 // the trial and append groups) and needs to have a variations::VariationID
 // associated with it so Google servers can recognize the FieldTrial.
 // Thread safe.
+COMPONENT_EXPORT(VARIATIONS)
 void AssociateGoogleVariationID(IDCollectionKey key,
                                 const std::string& trial_name,
                                 const std::string& group_name,
                                 VariationID id);
 
 // As above, but overwrites any previously set id. Thread safe.
+COMPONENT_EXPORT(VARIATIONS)
 void AssociateGoogleVariationIDForce(IDCollectionKey key,
                                      const std::string& trial_name,
                                      const std::string& group_name,
                                      VariationID id);
 
 // As above, but takes an ActiveGroupId hash pair, rather than the string names.
+COMPONENT_EXPORT(VARIATIONS)
 void AssociateGoogleVariationIDForceHashes(IDCollectionKey key,
                                            const ActiveGroupId& active_group,
                                            VariationID id);
@@ -110,47 +113,57 @@
 // for the named group. This API can be nicely combined with
 // FieldTrial::GetActiveFieldTrialGroups() to enumerate the variation IDs for
 // all active FieldTrial groups. Thread safe.
+COMPONENT_EXPORT(VARIATIONS)
 VariationID GetGoogleVariationID(IDCollectionKey key,
                                  const std::string& trial_name,
                                  const std::string& group_name);
 
 // Same as GetGoogleVariationID(), but takes in a hashed |active_group| rather
 // than the string trial and group name.
+COMPONENT_EXPORT(VARIATIONS)
 VariationID GetGoogleVariationIDFromHashes(IDCollectionKey key,
                                            const ActiveGroupId& active_group);
 
 // Deprecated. Use base::AssociateFieldTrialParams() instead.
+COMPONENT_EXPORT(VARIATIONS)
 bool AssociateVariationParams(const std::string& trial_name,
                               const std::string& group_name,
                               const std::map<std::string, std::string>& params);
 
 // Deprecated. Use base::GetFieldTrialParams() instead.
+COMPONENT_EXPORT(VARIATIONS)
 bool GetVariationParams(const std::string& trial_name,
                         std::map<std::string, std::string>* params);
 
 // Deprecated. Use base::GetFieldTrialParamsByFeature() instead.
+COMPONENT_EXPORT(VARIATIONS)
 bool GetVariationParamsByFeature(const base::Feature& feature,
                                  std::map<std::string, std::string>* params);
 
 // Deprecated. Use base::GetFieldTrialParamValue() instead.
+COMPONENT_EXPORT(VARIATIONS)
 std::string GetVariationParamValue(const std::string& trial_name,
                                    const std::string& param_name);
 
 // Deprecated. Use base::GetFieldTrialParamValueByFeature() instead.
+COMPONENT_EXPORT(VARIATIONS)
 std::string GetVariationParamValueByFeature(const base::Feature& feature,
                                             const std::string& param_name);
 
 // Deprecated. Use base::GetFieldTrialParamByFeatureAsInt() instead.
+COMPONENT_EXPORT(VARIATIONS)
 int GetVariationParamByFeatureAsInt(const base::Feature& feature,
                                     const std::string& param_name,
                                     int default_value);
 
 // Deprecated. Use base::GetFieldTrialParamByFeatureAsDouble() instead.
+COMPONENT_EXPORT(VARIATIONS)
 double GetVariationParamByFeatureAsDouble(const base::Feature& feature,
                                           const std::string& param_name,
                                           double default_value);
 
 // Deprecated. Use base::GetFieldTrialParamByFeatureAsBool() instead.
+COMPONENT_EXPORT(VARIATIONS)
 bool GetVariationParamByFeatureAsBool(const base::Feature& feature,
                                       const std::string& param_name,
                                       bool default_value);
@@ -160,11 +173,11 @@
 
 // Clears all of the mapped associations. Deprecated, use ScopedFeatureList
 // instead as it does a lot of work for you automatically.
-void ClearAllVariationIDs();
+COMPONENT_EXPORT(VARIATIONS) void ClearAllVariationIDs();
 
 // Clears all of the associated params. Deprecated, use ScopedFeatureList
 // instead as it does a lot of work for you automatically.
-void ClearAllVariationParams();
+COMPONENT_EXPORT(VARIATIONS) void ClearAllVariationParams();
 
 }  // namespace testing
 
diff --git a/components/variations/variations_client.h b/components/variations/variations_client.h
index bdfa3bd1..df74d1ba 100644
--- a/components/variations/variations_client.h
+++ b/components/variations/variations_client.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_VARIATIONS_VARIATIONS_CLIENT_H_
 #define COMPONENTS_VARIATIONS_VARIATIONS_CLIENT_H_
 
+#include "base/component_export.h"
 #include "components/variations/variations.mojom.h"
 
 namespace variations {
@@ -12,7 +13,7 @@
 // Used by VariationsURLLoaderThrottle to insulate the content layer from
 // concepts like user sign in which don't belong there. There is an instance per
 // profile, so there can be multiple clients at a time when in multi user mode.
-class VariationsClient {
+class COMPONENT_EXPORT(VARIATIONS) VariationsClient {
  public:
   virtual ~VariationsClient() = default;
 
diff --git a/components/variations/variations_crash_keys.h b/components/variations/variations_crash_keys.h
index 2bee11c6..908c1d3 100644
--- a/components/variations/variations_crash_keys.h
+++ b/components/variations/variations_crash_keys.h
@@ -8,38 +8,41 @@
 #include <string>
 #include <vector>
 
+#include "base/component_export.h"
+
 namespace variations {
 
 struct SyntheticTrialGroup;
 
 // The key used in crash reports to indicate the number of active experiments.
 // Should match the number of entries in kExperimentListKey.
-extern const char kNumExperimentsKey[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kNumExperimentsKey[];
 
 // The key used in crash reports to list all the active experiments. Each
 // experiment is listed as two hex numbers: trial ID and group ID, separated by
 // a dash. The experiments are separated by a comma.
-extern const char kExperimentListKey[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kExperimentListKey[];
 
 // Initializes crash keys that report the current set of active FieldTrial
 // groups (aka variations) for crash reports. After initialization, an observer
 // will be registered on FieldTrialList that will keep the crash keys up-to-date
 // with newly-activated trials. Synthetic trials must be manually updated using
 // the API below.
-void InitCrashKeys();
+COMPONENT_EXPORT(VARIATIONS) void InitCrashKeys();
 
 // Updates variations crash keys by replacing the list of synthetic trials with
 // the specified list. Does not affect non-synthetic trials.
+COMPONENT_EXPORT(VARIATIONS)
 void UpdateCrashKeysWithSyntheticTrials(
     const std::vector<SyntheticTrialGroup>& synthetic_trials);
 
 // Clears the internal instance, for testing.
-void ClearCrashKeysInstanceForTesting();
+COMPONENT_EXPORT(VARIATIONS) void ClearCrashKeysInstanceForTesting();
 
 // The list of experiments, in the format needed by the crash keys. The
 // |num_experiments| goes into the |kNumExperimentsKey| crash key, and the
 // |experiment_list| goes into the |kExperimentListKey| crash key.
-struct ExperimentListInfo {
+struct COMPONENT_EXPORT(VARIATIONS) ExperimentListInfo {
   int num_experiments = 0;
   std::string experiment_list;
 };
@@ -48,7 +51,7 @@
 // Specifically, returns the string used for representing the active experiment
 // groups + the synthetic trials in |experiment_list| and the number of elements
 // in that list in |num_experiments|. Must be called on the UI thread.
-ExperimentListInfo GetExperimentListInfo();
+COMPONENT_EXPORT(VARIATIONS) ExperimentListInfo GetExperimentListInfo();
 
 }  // namespace variations
 
diff --git a/components/variations/variations_crash_keys_chromeos.h b/components/variations/variations_crash_keys_chromeos.h
index 3453776..eebd2ab 100644
--- a/components/variations/variations_crash_keys_chromeos.h
+++ b/components/variations/variations_crash_keys_chromeos.h
@@ -5,6 +5,7 @@
 #ifndef COMPONENTS_VARIATIONS_VARIATIONS_CRASH_KEYS_CHROMEOS_H_
 #define COMPONENTS_VARIATIONS_VARIATIONS_CRASH_KEYS_CHROMEOS_H_
 
+#include "base/component_export.h"
 #include "base/sequenced_task_runner.h"
 #include "components/variations/variations_crash_keys.h"
 
@@ -13,6 +14,7 @@
 // On a separate thread, report the provided crash keys to Chrome OS using a
 // .variant-list.txt in the user's home directory, or /home/chronos if no user
 // is logged in.
+COMPONENT_EXPORT(VARIATIONS)
 void ReportVariationsToChromeOs(scoped_refptr<base::SequencedTaskRunner> runner,
                                 ExperimentListInfo info);
 
diff --git a/components/variations/variations_ids_provider.h b/components/variations/variations_ids_provider.h
index 00ed598..3242b90 100644
--- a/components/variations/variations_ids_provider.h
+++ b/components/variations/variations_ids_provider.h
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/component_export.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
@@ -32,7 +33,7 @@
 // (i) VariationsIDs associated with external experiments, which can be sent
 // only for signed-in users and (ii) VariationsIDs that can be sent in first-
 // and third-party contexts.
-struct VariationsHeaderKey {
+struct COMPONENT_EXPORT(VARIATIONS) VariationsHeaderKey {
   bool is_signed_in;
   Study_GoogleWebVisibility web_visibility;
 
@@ -43,10 +44,11 @@
 // A helper class for maintaining client experiments and metrics state
 // transmitted in custom HTTP request headers.
 // This class is a thread-safe singleton.
-class VariationsIdsProvider : public base::FieldTrialList::Observer,
-                              public SyntheticTrialObserver {
+class COMPONENT_EXPORT(VARIATIONS) VariationsIdsProvider
+    : public base::FieldTrialList::Observer,
+      public SyntheticTrialObserver {
  public:
-  class Observer {
+  class COMPONENT_EXPORT(VARIATIONS) Observer {
    public:
     // Called when variation ids headers are updated.
     virtual void VariationIdsHeaderUpdated() = 0;
diff --git a/components/variations/variations_layers.h b/components/variations/variations_layers.h
index d24484f..c5c17f6 100644
--- a/components/variations/variations_layers.h
+++ b/components/variations/variations_layers.h
@@ -7,6 +7,7 @@
 
 #include <map>
 
+#include "base/component_export.h"
 #include "base/metrics/field_trial.h"
 #include "components/variations/proto/variations_seed.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -15,7 +16,7 @@
 
 // Parses out the Variations Layers data from the provided seed and
 // chooses which member within each layer should be the active one.
-class VariationsLayers {
+class COMPONENT_EXPORT(VARIATIONS) VariationsLayers {
  public:
   VariationsLayers(
       const VariationsSeed& seed,
diff --git a/components/variations/variations_murmur_hash.h b/components/variations/variations_murmur_hash.h
index d4a0291..62df4c3 100644
--- a/components/variations/variations_murmur_hash.h
+++ b/components/variations/variations_murmur_hash.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/component_export.h"
 #include "base/strings/string_piece.h"
 
 namespace variations {
@@ -16,7 +17,7 @@
 
 // Hash utilities for NormalizedMurmurHashEntropyProvider. For more info, see:
 // https://docs.google.com/document/d/1cPF5PruriWNP2Z5gSkq4MBTm0wSZqLyIJkUO9ekibeo
-class VariationsMurmurHash {
+class COMPONENT_EXPORT(VARIATIONS) VariationsMurmurHash {
  public:
   // Prepares data to be hashed by VariationsMurmurHash: align and zero-pad to a
   // multiple of 4 bytes, and produce the same uint32_t values regardless of
diff --git a/components/variations/variations_request_scheduler.h b/components/variations/variations_request_scheduler.h
index 72ca48da..d2b114f 100644
--- a/components/variations/variations_request_scheduler.h
+++ b/components/variations/variations_request_scheduler.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_VARIATIONS_VARIATIONS_REQUEST_SCHEDULER_H_
 
 #include "base/bind.h"
+#include "base/component_export.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/time/time.h"
@@ -16,7 +17,7 @@
 namespace variations {
 
 // A helper class that makes VariationsService requests at the correct times.
-class VariationsRequestScheduler {
+class COMPONENT_EXPORT(VARIATIONS) VariationsRequestScheduler {
  public:
   virtual ~VariationsRequestScheduler();
 
diff --git a/components/variations/variations_request_scheduler_mobile.h b/components/variations/variations_request_scheduler_mobile.h
index ee1e117d..fe8193e5 100644
--- a/components/variations/variations_request_scheduler_mobile.h
+++ b/components/variations/variations_request_scheduler_mobile.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_VARIATIONS_VARIATIONS_REQUEST_SCHEDULER_MOBILE_H_
 
 #include "base/bind.h"
+#include "base/component_export.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/timer/timer.h"
@@ -17,12 +18,13 @@
 
 // A specialized VariationsRequestScheduler that manages request cycles for
 // VariationsService on mobile platforms.
-class VariationsRequestSchedulerMobile : public VariationsRequestScheduler {
+class COMPONENT_EXPORT(VARIATIONS) VariationsRequestSchedulerMobile
+    : public VariationsRequestScheduler {
  public:
   // |task} is the closure to call when the scheduler deems ready. |local_state|
   // is the PrefService that contains the time of the last fetch.
-  explicit VariationsRequestSchedulerMobile(const base::RepeatingClosure& task,
-                                            PrefService* local_state);
+  VariationsRequestSchedulerMobile(const base::RepeatingClosure& task,
+                                   PrefService* local_state);
   ~VariationsRequestSchedulerMobile() override;
 
   // Base class overrides.
diff --git a/components/variations/variations_seed_processor.h b/components/variations/variations_seed_processor.h
index 553556b..763ac56 100644
--- a/components/variations/variations_seed_processor.h
+++ b/components/variations/variations_seed_processor.h
@@ -12,6 +12,7 @@
 
 #include "base/callback_forward.h"
 #include "base/compiler_specific.h"
+#include "base/component_export.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
@@ -29,7 +30,7 @@
 struct ClientFilterableState;
 
 // Helper class to instantiate field trials from a variations seed.
-class VariationsSeedProcessor {
+class COMPONENT_EXPORT(VARIATIONS) VariationsSeedProcessor {
  public:
   using UIStringOverrideCallback =
       base::RepeatingCallback<void(uint32_t, const std::u16string&)>;
diff --git a/components/variations/variations_seed_simulator.h b/components/variations/variations_seed_simulator.h
index 72986af..3cb9e1f 100644
--- a/components/variations/variations_seed_simulator.h
+++ b/components/variations/variations_seed_simulator.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/compiler_specific.h"
+#include "base/component_export.h"
 #include "base/macros.h"
 #include "base/metrics/field_trial.h"
 #include "base/version.h"
@@ -23,12 +24,15 @@
 
 // VariationsSeedSimulator simulates the result of creating a set of studies
 // and detecting which studies would result in group changes.
-class VariationsSeedSimulator {
+class COMPONENT_EXPORT(VARIATIONS) VariationsSeedSimulator {
  public:
   // The result of variations seed simulation, counting the number of experiment
   // group changes of each type that are expected to occur on a restart with the
   // seed.
-  struct Result {
+  struct COMPONENT_EXPORT(VARIATIONS) Result {
+    Result();
+    ~Result();
+
     // The number of expected group changes that do not fall into any special
     // category. This is a lower bound due to session randomized studies.
     int normal_group_change_count;
@@ -40,9 +44,6 @@
     // The number of expected group changes that fall in the category of killed
     // experiments that should trigger the "critical" restart mechanism.
     int kill_critical_group_change_count;
-
-    Result();
-    ~Result();
   };
 
   // Creates the simulator with the given default and low entropy providers. The
diff --git a/components/variations/variations_seed_store.h b/components/variations/variations_seed_store.h
index 8f905e1..62b55da06 100644
--- a/components/variations/variations_seed_store.h
+++ b/components/variations/variations_seed_store.h
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "base/compiler_specific.h"
+#include "base/component_export.h"
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/time/time.h"
@@ -27,7 +28,7 @@
 
 // VariationsSeedStore is a helper class for reading and writing the variations
 // seed from Local State.
-class VariationsSeedStore {
+class COMPONENT_EXPORT(VARIATIONS) VariationsSeedStore {
  public:
   // Standard constructor. Enables signature verification.
   explicit VariationsSeedStore(PrefService* local_state);
diff --git a/components/variations/variations_switches.h b/components/variations/variations_switches.h
index c4559c2d..32bb2e8 100644
--- a/components/variations/variations_switches.h
+++ b/components/variations/variations_switches.h
@@ -5,20 +5,31 @@
 #ifndef COMPONENTS_VARIATIONS_VARIATIONS_SWITCHES_H_
 #define COMPONENTS_VARIATIONS_VARIATIONS_SWITCHES_H_
 
+#include "base/component_export.h"
+
 namespace variations {
 namespace switches {
 
 // Alphabetical list of switches specific to the variations component. Document
 // each in the .cc file.
 
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kDisableFieldTrialTestingConfig[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kEnableBenchmarking[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kFakeVariationsChannel[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kForceFieldTrialParams[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kForceVariationIds[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kForceDisableVariationIds[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsOverrideCountry[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsServerURL[];
+COMPONENT_EXPORT(VARIATIONS)
 extern const char kVariationsInsecureServerURL[];
 
 }  // namespace switches
diff --git a/components/variations/variations_url_constants.h b/components/variations/variations_url_constants.h
index 336effdc..f6c2852 100644
--- a/components/variations/variations_url_constants.h
+++ b/components/variations/variations_url_constants.h
@@ -5,11 +5,13 @@
 #ifndef COMPONENTS_VARIATIONS_VARIATIONS_URL_CONSTANTS_H_
 #define COMPONENTS_VARIATIONS_VARIATIONS_URL_CONSTANTS_H_
 
+#include "base/component_export.h"
+
 namespace variations {
 
-extern const char kDefaultServerUrl[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kDefaultServerUrl[];
 
-extern const char kDefaultInsecureServerUrl[];
+COMPONENT_EXPORT(VARIATIONS) extern const char kDefaultInsecureServerUrl[];
 
 }  // namespace variations
 
diff --git a/components/viz/common/BUILD.gn b/components/viz/common/BUILD.gn
index 6869f9d..55ddc42 100644
--- a/components/viz/common/BUILD.gn
+++ b/components/viz/common/BUILD.gn
@@ -296,6 +296,7 @@
 
   deps = [
     "//base",
+    "//base/util/values:values_util",
     "//build:chromeos_buildflags",
 
     # TODO(staraz): cc/base was added because SharedQuadState includes
diff --git a/components/viz/common/quads/render_pass_io.cc b/components/viz/common/quads/render_pass_io.cc
index 33fcfb23..148e0bc 100644
--- a/components/viz/common/quads/render_pass_io.cc
+++ b/components/viz/common/quads/render_pass_io.cc
@@ -13,8 +13,11 @@
 #include "base/containers/span.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_tokenizer.h"
+#include "base/util/values/values_util.h"
 #include "cc/paint/paint_op_reader.h"
 #include "cc/paint/paint_op_writer.h"
+#include "components/viz/common/quads/compositor_frame.h"
+#include "components/viz/common/quads/compositor_render_pass.h"
 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
 #include "components/viz/common/quads/picture_draw_quad.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
@@ -882,23 +885,31 @@
   dict.SetIntKey("sink_id", id.frame_sink_id().sink_id());
   dict.SetIntKey("parent_seq", id.local_surface_id().parent_sequence_number());
   dict.SetIntKey("child_seq", id.local_surface_id().child_sequence_number());
+  dict.SetKey("embed_token", util::UnguessableTokenToValue(
+                                 id.local_surface_id().embed_token()));
 
-  // |embed_token_| doesn't need to be saved as long as a consistent token is
-  // used when deserializing.
   return dict;
 }
 
 absl::optional<SurfaceId> SurfaceIdFromDict(const base::Value& dict) {
+  if (!dict.is_dict()) {
+    return absl::nullopt;
+  }
   absl::optional<int> client_id = dict.FindIntKey("client_id");
   absl::optional<int> sink_id = dict.FindIntKey("sink_id");
   absl::optional<int> parent_seq = dict.FindIntKey("parent_seq");
   absl::optional<int> child_seq = dict.FindIntKey("child_seq");
-  if (!client_id || !sink_id || !parent_seq || !child_seq)
+  const base::Value* embed_token_value = dict.FindKey("embed_token");
+  if (!client_id || !sink_id || !parent_seq || !child_seq || !embed_token_value)
     return absl::nullopt;
 
-  base::UnguessableToken token = base::UnguessableToken::Deserialize(1, 1);
+  auto token = util::ValueToUnguessableToken(*embed_token_value);
+  if (!token) {
+    return absl::nullopt;
+  }
+
   return SurfaceId(FrameSinkId(*client_id, *sink_id),
-                   LocalSurfaceId(*parent_seq, *child_seq, token));
+                   LocalSurfaceId(*parent_seq, *child_seq, *token));
 }
 
 base::Value SurfaceRangeToDict(const SurfaceRange& range) {
@@ -910,6 +921,9 @@
 }
 
 absl::optional<SurfaceRange> SurfaceRangeFromDict(const base::Value& dict) {
+  if (!dict.is_dict()) {
+    return absl::nullopt;
+  }
   const base::Value* start_dict = dict.FindDictKey("start");
   const base::Value* end_dict = dict.FindDictKey("end");
   if (!end_dict)
@@ -2076,6 +2090,7 @@
     CompositorRenderPassList* render_pass_list) {
   DCHECK(render_pass_list);
   DCHECK(render_pass_list->empty());
+
   if (!dict.is_dict())
     return false;
   const base::Value* list = dict.FindListKey("render_pass_list");
@@ -2092,4 +2107,136 @@
   return true;
 }
 
+base::Value CompositorFrameToDict(const CompositorFrame& compositor_frame) {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  auto render_pass_list_dict =
+      CompositorRenderPassListToDict(compositor_frame.render_pass_list);
+  dict.SetKey("render_pass_list", std::move(render_pass_list_dict));
+
+  base::Value referenced_surfaces(base::Value::Type::LIST);
+  for (auto& surface_range : compositor_frame.metadata.referenced_surfaces) {
+    referenced_surfaces.Append(SurfaceRangeToDict(surface_range));
+  }
+  base::Value metadataDict(base::Value::Type::DICTIONARY);
+  metadataDict.SetKey("referenced_surfaces", std::move(referenced_surfaces));
+  dict.SetKey("metadata", std::move(metadataDict));
+
+  return dict;
+}
+
+bool CompositorFrameFromDict(const base::Value& dict,
+                             CompositorFrame* compositor_frame) {
+  DCHECK(compositor_frame);
+  if (!dict.is_dict()) {
+    return false;
+  }
+
+  const base::Value* render_pass_list_dict =
+      dict.FindDictKey("render_pass_list");
+  if (!render_pass_list_dict) {
+    return false;
+  }
+  if (!CompositorRenderPassListFromDict(*render_pass_list_dict,
+                                        &compositor_frame->render_pass_list)) {
+    return false;
+  }
+
+  const base::Value* metadata = dict.FindDictKey("metadata");
+  if (!metadata || !metadata->is_dict()) {
+    return false;
+  }
+  const base::Value* referenced_surfaces =
+      metadata->FindListKey("referenced_surfaces");
+  if (!referenced_surfaces || !referenced_surfaces->is_list()) {
+    return false;
+  }
+  for (auto& referenced_surface_dict : referenced_surfaces->GetList()) {
+    auto referenced_surface = SurfaceRangeFromDict(referenced_surface_dict);
+    if (!referenced_surface) {
+      return false;
+    }
+    compositor_frame->metadata.referenced_surfaces.push_back(
+        *referenced_surface);
+  }
+
+  return true;
+}
+
+base::Value FrameDataToList(const std::vector<FrameData>& frame_data_list) {
+  base::Value list(base::Value::Type::LIST);
+
+  for (auto& frame_data : frame_data_list) {
+    base::Value frame_dict(base::Value::Type::DICTIONARY);
+
+    frame_dict.SetKey("surface_id", SurfaceIdToDict(frame_data.surface_id));
+    // This cast will be safe because we
+    // should never have more than |INT_MAX|
+    // frames in recorded data.
+    frame_dict.SetIntKey("frame_index",
+                         static_cast<int>(frame_data.frame_index));
+    frame_dict.SetKey("compositor_frame",
+                      CompositorFrameToDict(frame_data.compositor_frame));
+
+    list.Append(std::move(frame_dict));
+  }
+  return list;
+}
+
+bool FrameDataFromList(const base::Value& list,
+                       std::vector<FrameData>* frame_data_list) {
+  DCHECK(frame_data_list);
+  DCHECK(frame_data_list->empty());
+
+  if (!list.is_list()) {
+    return false;
+  }
+  for (const auto& frame_data_dict : list.GetList()) {
+    FrameData frame_data;
+    auto* surface_id_dict = frame_data_dict.FindDictKey("surface_id");
+    if (!surface_id_dict) {
+      return false;
+    }
+    absl::optional<SurfaceId> surface_id = SurfaceIdFromDict(*surface_id_dict);
+    if (!surface_id) {
+      return false;
+    }
+    frame_data.surface_id = *surface_id;
+
+    absl::optional<int> frame_index = frame_data_dict.FindIntKey("frame_index");
+    if (!frame_index) {
+      return false;
+    }
+    frame_data.frame_index = *frame_index;
+
+    const base::Value* compositor_frame_dict =
+        frame_data_dict.FindDictKey("compositor_frame");
+    if (!compositor_frame_dict) {
+      return false;
+    }
+    if (!CompositorFrameFromDict(*compositor_frame_dict,
+                                 &frame_data.compositor_frame)) {
+      return false;
+    }
+
+    frame_data_list->push_back(std::move(frame_data));
+  }
+
+  return true;
+}
+
+FrameData::FrameData() = default;
+FrameData::FrameData(FrameData&& other) = default;
+FrameData& FrameData::operator=(FrameData&& other) = default;
+
+FrameData::FrameData(const SurfaceId& surface_id,
+                     const uint64_t frame_index,
+                     const CompositorFrame& compositor_frame)
+    : surface_id(surface_id), frame_index(frame_index) {
+  this->compositor_frame.metadata = compositor_frame.metadata.Clone();
+  this->compositor_frame.resource_list = compositor_frame.resource_list;
+  for (const auto& render_pass : compositor_frame.render_pass_list) {
+    this->compositor_frame.render_pass_list.push_back(render_pass->DeepCopy());
+  }
+}
+
 }  // namespace viz
diff --git a/components/viz/common/quads/render_pass_io.h b/components/viz/common/quads/render_pass_io.h
index c8567dc..e78f24c 100644
--- a/components/viz/common/quads/render_pass_io.h
+++ b/components/viz/common/quads/render_pass_io.h
@@ -6,8 +6,10 @@
 #define COMPONENTS_VIZ_COMMON_QUADS_RENDER_PASS_IO_H_
 
 #include <memory>
+#include <vector>
 
 #include "base/values.h"
+#include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/compositor_render_pass.h"
 #include "components/viz/common/viz_common_export.h"
 
@@ -22,6 +24,29 @@
 VIZ_COMMON_EXPORT bool CompositorRenderPassListFromDict(
     const base::Value& dict,
     CompositorRenderPassList* render_pass_list);
+
+// Represents the important information used for (de)serialization of
+// `CompositorFrame`s on a given surface.
+struct VIZ_COMMON_EXPORT FrameData {
+  FrameData();
+  FrameData(const SurfaceId& surface_id,
+            const uint64_t frame_index,
+            const CompositorFrame& compositor_frame);
+  FrameData(FrameData&& other);
+  FrameData& operator=(FrameData&& other);
+
+  SurfaceId surface_id;
+  uint64_t frame_index;
+  CompositorFrame compositor_frame;
+};
+
+// These functions (de)serialize data about CompositorFrames for multiple
+// surfaces, represented as arrays of `FrameData`s.
+VIZ_COMMON_EXPORT base::Value FrameDataToList(
+    const std::vector<FrameData>& frame_data_list);
+VIZ_COMMON_EXPORT bool FrameDataFromList(
+    const base::Value& list,
+    std::vector<FrameData>* frame_data_list);
 }  // namespace viz
 
 #endif  // COMPONENTS_VIZ_COMMON_QUADS_RENDER_PASS_IO_H_
diff --git a/components/viz/common/quads/render_pass_io_unittest.cc b/components/viz/common/quads/render_pass_io_unittest.cc
index 1c819ea537..390434a 100644
--- a/components/viz/common/quads/render_pass_io_unittest.cc
+++ b/components/viz/common/quads/render_pass_io_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/json/json_reader.h"
 #include "base/path_service.h"
 #include "base/values.h"
+#include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
 #include "components/viz/common/quads/solid_color_draw_quad.h"
 #include "components/viz/common/quads/stream_video_draw_quad.h"
@@ -19,6 +20,7 @@
 #include "components/viz/common/quads/video_hole_draw_quad.h"
 #include "components/viz/common/quads/yuv_video_draw_quad.h"
 #include "components/viz/test/paths.h"
+#include "components/viz/test/test_surface_id_allocator.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace gl {
@@ -28,11 +30,6 @@
 namespace viz {
 namespace {
 
-constexpr SurfaceId kSurfaceId1(FrameSinkId(1, 1),
-                                LocalSurfaceId(1, 1, base::UnguessableToken()));
-constexpr SurfaceId kSurfaceId2(FrameSinkId(2, 2),
-                                LocalSurfaceId(2, 2, base::UnguessableToken()));
-
 TEST(RenderPassIOTest, Default) {
   auto render_pass0 = CompositorRenderPass::Create();
   base::Value dict0 = CompositorRenderPassToDict(*render_pass0);
@@ -192,6 +189,8 @@
       DrawQuad::Material::kSurfaceContent,
       DrawQuad::Material::kSurfaceContent,
   };
+  TestSurfaceIdAllocator kSurfaceId1(FrameSinkId(1, 1));
+  TestSurfaceIdAllocator kSurfaceId2(FrameSinkId(2, 2));
   auto render_pass0 = CompositorRenderPass::Create();
   {
     // Add to shared_quad_state_list.
@@ -363,5 +362,28 @@
   EXPECT_EQ(dict0, dict1);
 }
 
+TEST(RenderPassIOTest, CompositorFrameData) {
+  // Validate recorded multi-surface compositor frame data from
+  // https://www.youtube.com/
+  base::FilePath test_data_dir;
+  ASSERT_TRUE(base::PathService::Get(Paths::DIR_TEST_DATA, &test_data_dir));
+  base::FilePath json_path =
+      test_data_dir.Append(FILE_PATH_LITERAL("render_pass_data"))
+          .Append(FILE_PATH_LITERAL("multi_surface_test"))
+          .Append(FILE_PATH_LITERAL("youtube"))
+          .Append(FILE_PATH_LITERAL("0358.json"));
+  ASSERT_TRUE(base::PathExists(json_path));
+  std::string json_text;
+  ASSERT_TRUE(base::ReadFileToString(json_path, &json_text));
+
+  absl::optional<base::Value> list0 = base::JSONReader::Read(json_text);
+  EXPECT_TRUE(list0.has_value());
+  std::vector<FrameData> frame_data_list;
+  EXPECT_TRUE(FrameDataFromList(list0.value(), &frame_data_list));
+  base::Value list1 = FrameDataToList(frame_data_list);
+
+  EXPECT_EQ(list0, list1);
+}
+
 }  // namespace
 }  // namespace viz
diff --git a/components/viz/test/data/render_pass_data/multi_surface_test/youtube/0358.json b/components/viz/test/data/render_pass_data/multi_surface_test/youtube/0358.json
new file mode 100644
index 0000000..e4928488
--- /dev/null
+++ b/components/viz/test/data/render_pass_data/multi_surface_test/youtube/0358.json
@@ -0,0 +1,2547 @@
+[ {
+   "compositor_frame": {
+      "metadata": {
+         "referenced_surfaces": [ {
+            "end": {
+               "child_seq": 1,
+               "client_id": 20,
+               "embed_token": "40EF4BADBEA6D438ECDA5BCAAD99E819",
+               "parent_seq": 3,
+               "sink_id": 2
+            },
+            "start": {
+               "child_seq": 1,
+               "client_id": 20,
+               "embed_token": "40EF4BADBEA6D438ECDA5BCAAD99E819",
+               "parent_seq": 3,
+               "sink_id": 2
+            }
+         } ]
+      },
+      "render_pass_list": {
+         "metadata": [ {
+            "quad_count": 1,
+            "render_pass_id": "152",
+            "shared_quad_state_count": 1
+         }, {
+            "quad_count": 2,
+            "render_pass_id": "143",
+            "shared_quad_state_count": 2
+         }, {
+            "quad_count": 23,
+            "render_pass_id": "3",
+            "shared_quad_state_count": 7
+         } ],
+         "render_pass_count": 3,
+         "render_pass_list": [ {
+            "backdrop_filters": [  ],
+            "cache_render_pass": false,
+            "color_space": {
+               "matrix": "RGB",
+               "primaries": "BT709",
+               "range": "FULL",
+               "transfer": "IEC61966_2_1"
+            },
+            "damage_rect": {
+               "height": 24,
+               "width": 32,
+               "x": 0,
+               "y": 0
+            },
+            "filters": [  ],
+            "generate_mipmap": false,
+            "has_damage_from_contributing_content": false,
+            "has_transparent_background": true,
+            "id": "152",
+            "output_rect": {
+               "height": 24,
+               "width": 32,
+               "x": 0,
+               "y": 0
+            },
+            "quad_list": [ {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 472 ],
+               "shared_quad_state_index": 0,
+               "tex_coord_rect": {
+                  "height": 24.0,
+                  "width": 32.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 64
+               },
+               "visible_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               }
+            } ],
+            "shared_quad_state_list": [ {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               }
+            } ],
+            "transform_to_root_target": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 970.0, 49.0, 0.0, 1.0 ]
+         }, {
+            "backdrop_filters": [  ],
+            "cache_render_pass": false,
+            "color_space": {
+               "matrix": "RGB",
+               "primaries": "BT709",
+               "range": "FULL",
+               "transfer": "IEC61966_2_1"
+            },
+            "damage_rect": {
+               "height": 24,
+               "width": 32,
+               "x": 0,
+               "y": 0
+            },
+            "filters": [  ],
+            "generate_mipmap": false,
+            "has_damage_from_contributing_content": false,
+            "has_transparent_background": true,
+            "id": "143",
+            "output_rect": {
+               "height": 24,
+               "width": 32,
+               "x": 0,
+               "y": 0
+            },
+            "quad_list": [ {
+               "backdrop_filter_quality": 1.0,
+               "filters_origin": {
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "filters_scale": {
+                  "x": 1.0,
+                  "y": 1.0
+               },
+               "force_anti_aliasing_off": true,
+               "intersects_damage_under": false,
+               "mask_texture_size": {
+                  "height": 0,
+                  "width": 0
+               },
+               "mask_uv_rect": {
+                  "height": 0.0,
+                  "width": 0.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "material": "kCompositorRenderPass",
+               "needs_blending": true,
+               "rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "render_pass_id": "152",
+               "resources": [  ],
+               "shared_quad_state_index": 0,
+               "tex_coord_rect": {
+                  "height": 24.0,
+                  "width": 32.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "visible_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "color": -16777216,
+               "force_anti_aliasing_off": true,
+               "material": "kSolidColor",
+               "needs_blending": false,
+               "rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [  ],
+               "shared_quad_state_index": 1,
+               "visible_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               }
+            } ],
+            "shared_quad_state_list": [ {
+               "are_contents_opaque": false,
+               "blend_mode": "kDstIn",
+               "clip_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 0.10000000149011612,
+               "quad_layer_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               }
+            } ],
+            "transform_to_root_target": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 970.0, 49.0, 0.0, 1.0 ]
+         }, {
+            "backdrop_filters": [  ],
+            "cache_render_pass": false,
+            "color_space": {
+               "matrix": "RGB",
+               "primaries": "BT709",
+               "range": "FULL",
+               "transfer": "IEC61966_2_1"
+            },
+            "damage_rect": {
+               "height": 22,
+               "width": 312,
+               "x": 3,
+               "y": 1167
+            },
+            "filters": [  ],
+            "generate_mipmap": false,
+            "has_damage_from_contributing_content": true,
+            "has_transparent_background": false,
+            "id": "3",
+            "output_rect": {
+               "height": 1192,
+               "width": 1148,
+               "x": 0,
+               "y": 0
+            },
+            "quad_list": [ {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 22,
+                  "width": 312,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 483 ],
+               "shared_quad_state_index": 0,
+               "tex_coord_rect": {
+                  "height": 22.0,
+                  "width": 312.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 512
+               },
+               "visible_rect": {
+                  "height": 22,
+                  "width": 312,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 78,
+                  "width": 353,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 476 ],
+               "shared_quad_state_index": 1,
+               "tex_coord_rect": {
+                  "height": 78.0,
+                  "width": 353.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 128,
+                  "width": 384
+               },
+               "visible_rect": {
+                  "height": 78,
+                  "width": 353,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "allow_merge": true,
+               "default_background_color": -394759,
+               "is_reflection": false,
+               "material": "kSurfaceContent",
+               "needs_blending": true,
+               "rect": {
+                  "height": 1107,
+                  "width": 1140,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [  ],
+               "shared_quad_state_index": 2,
+               "stretch_content": false,
+               "surface_range": {
+                  "end": {
+                     "child_seq": 1,
+                     "client_id": 20,
+                     "embed_token": "40EF4BADBEA6D438ECDA5BCAAD99E819",
+                     "parent_seq": 3,
+                     "sink_id": 2
+                  },
+                  "start": {
+                     "child_seq": 1,
+                     "client_id": 20,
+                     "embed_token": "40EF4BADBEA6D438ECDA5BCAAD99E819",
+                     "parent_seq": 3,
+                     "sink_id": 2
+                  }
+               },
+               "visible_rect": {
+                  "height": 1107,
+                  "width": 1140,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 24,
+                  "width": 16,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 469 ],
+               "shared_quad_state_index": 3,
+               "tex_coord_rect": {
+                  "height": 24.0,
+                  "width": 16.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 64
+               },
+               "visible_rect": {
+                  "height": 24,
+                  "width": 16,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "backdrop_filter_quality": 1.0,
+               "filters_origin": {
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "filters_scale": {
+                  "x": 1.0,
+                  "y": 1.0
+               },
+               "force_anti_aliasing_off": true,
+               "intersects_damage_under": false,
+               "mask_texture_size": {
+                  "height": 0,
+                  "width": 0
+               },
+               "mask_uv_rect": {
+                  "height": 0.0,
+                  "width": 0.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "material": "kCompositorRenderPass",
+               "needs_blending": true,
+               "rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "render_pass_id": "143",
+               "resources": [  ],
+               "shared_quad_state_index": 4,
+               "tex_coord_rect": {
+                  "height": 24.0,
+                  "width": 32.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "visible_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 28,
+                  "width": 511,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 482 ],
+               "shared_quad_state_index": 5,
+               "tex_coord_rect": {
+                  "height": 28.0,
+                  "width": 511.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 512
+               },
+               "visible_rect": {
+                  "height": 28,
+                  "width": 511,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 28,
+                  "width": 413,
+                  "x": 511,
+                  "y": 0
+               },
+               "resources": [ 473 ],
+               "shared_quad_state_index": 5,
+               "tex_coord_rect": {
+                  "height": 28.0,
+                  "width": 413.0,
+                  "x": 1.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 512
+               },
+               "visible_rect": {
+                  "height": 28,
+                  "width": 413,
+                  "x": 511,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 255,
+                  "width": 255,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 471 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 255.0,
+                  "width": 255.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 255,
+                  "width": 255,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 255,
+                  "width": 254,
+                  "x": 255,
+                  "y": 0
+               },
+               "resources": [ 474 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 255.0,
+                  "width": 254.0,
+                  "x": 1.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 81,
+                  "width": 254,
+                  "x": 255,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 255,
+                  "width": 254,
+                  "x": 509,
+                  "y": 0
+               },
+               "resources": [ 159 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 255.0,
+                  "width": 254.0,
+                  "x": 1.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 81,
+                  "width": 254,
+                  "x": 509,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 255,
+                  "width": 254,
+                  "x": 763,
+                  "y": 0
+               },
+               "resources": [ 160 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 255.0,
+                  "width": 254.0,
+                  "x": 1.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 81,
+                  "width": 254,
+                  "x": 763,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 255,
+                  "width": 131,
+                  "x": 1017,
+                  "y": 0
+               },
+               "resources": [ 475 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 255.0,
+                  "width": 131.0,
+                  "x": 1.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 255,
+                  "width": 131,
+                  "x": 1017,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 254,
+                  "width": 255,
+                  "x": 0,
+                  "y": 255
+               },
+               "resources": [ 162 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 254.0,
+                  "width": 255.0,
+                  "x": 0.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 254,
+                  "width": 4,
+                  "x": 0,
+                  "y": 255
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 254,
+                  "width": 131,
+                  "x": 1017,
+                  "y": 255
+               },
+               "resources": [ 163 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 254.0,
+                  "width": 131.0,
+                  "x": 1.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 254,
+                  "width": 4,
+                  "x": 1144,
+                  "y": 255
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 254,
+                  "width": 255,
+                  "x": 0,
+                  "y": 509
+               },
+               "resources": [ 164 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 254.0,
+                  "width": 255.0,
+                  "x": 0.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 254,
+                  "width": 4,
+                  "x": 0,
+                  "y": 509
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 254,
+                  "width": 131,
+                  "x": 1017,
+                  "y": 509
+               },
+               "resources": [ 165 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 254.0,
+                  "width": 131.0,
+                  "x": 1.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 254,
+                  "width": 4,
+                  "x": 1144,
+                  "y": 509
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 254,
+                  "width": 255,
+                  "x": 0,
+                  "y": 763
+               },
+               "resources": [ 166 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 254.0,
+                  "width": 255.0,
+                  "x": 0.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 254,
+                  "width": 4,
+                  "x": 0,
+                  "y": 763
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 254,
+                  "width": 131,
+                  "x": 1017,
+                  "y": 763
+               },
+               "resources": [ 167 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 254.0,
+                  "width": 131.0,
+                  "x": 1.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 254,
+                  "width": 4,
+                  "x": 1144,
+                  "y": 763
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 175,
+                  "width": 255,
+                  "x": 0,
+                  "y": 1017
+               },
+               "resources": [ 168 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 175.0,
+                  "width": 255.0,
+                  "x": 0.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 175,
+                  "width": 255,
+                  "x": 0,
+                  "y": 1017
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 175,
+                  "width": 254,
+                  "x": 255,
+                  "y": 1017
+               },
+               "resources": [ 169 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 175.0,
+                  "width": 254.0,
+                  "x": 1.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 4,
+                  "width": 254,
+                  "x": 255,
+                  "y": 1188
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 175,
+                  "width": 254,
+                  "x": 509,
+                  "y": 1017
+               },
+               "resources": [ 170 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 175.0,
+                  "width": 254.0,
+                  "x": 1.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 4,
+                  "width": 254,
+                  "x": 509,
+                  "y": 1188
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 175,
+                  "width": 254,
+                  "x": 763,
+                  "y": 1017
+               },
+               "resources": [ 171 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 175.0,
+                  "width": 254.0,
+                  "x": 1.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 4,
+                  "width": 254,
+                  "x": 763,
+                  "y": 1188
+               }
+            }, {
+               "force_anti_aliasing_off": true,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 175,
+                  "width": 131,
+                  "x": 1017,
+                  "y": 1017
+               },
+               "resources": [ 172 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 175.0,
+                  "width": 131.0,
+                  "x": 1.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 256,
+                  "width": 256
+               },
+               "visible_rect": {
+                  "height": 175,
+                  "width": 131,
+                  "x": 1017,
+                  "y": 1017
+               }
+            } ],
+            "shared_quad_state_list": [ {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 22,
+                  "width": 312,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 3.0, 1167.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 22,
+                  "width": 312,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 78,
+                  "width": 353,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 655.0, 69.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 78,
+                  "width": 353,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": true,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 1107,
+                  "width": 1140,
+                  "x": 4,
+                  "y": 81
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 1107,
+                  "width": 1140,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 4.0, 81.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 1107,
+                  "width": 1140,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 24,
+                  "width": 16,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 978.0, 49.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 24,
+                  "width": 16,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 970.0, 49.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 24,
+                  "width": 32,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 28,
+                  "width": 924,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 112.0, 47.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 28,
+                  "width": 924,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": true,
+               "blend_mode": "kSrcOver",
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 1192,
+                  "width": 1148,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 1192,
+                  "width": 1148,
+                  "x": 0,
+                  "y": 0
+               }
+            } ],
+            "transform_to_root_target": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]
+         } ]
+      }
+   },
+   "frame_index": 341,
+   "surface_id": {
+      "child_seq": 1,
+      "client_id": 0,
+      "embed_token": "998A37E7C6C2083CAEEDB7EE0F1D0112",
+      "parent_seq": 1,
+      "sink_id": 1
+   }
+}, {
+   "compositor_frame": {
+      "metadata": {
+         "referenced_surfaces": [  ]
+      },
+      "render_pass_list": {
+         "metadata": [ {
+            "quad_count": 25,
+            "render_pass_id": "113",
+            "shared_quad_state_count": 20
+         } ],
+         "render_pass_count": 1,
+         "render_pass_list": [ {
+            "backdrop_filters": [  ],
+            "cache_render_pass": false,
+            "color_space": {
+               "matrix": "RGB",
+               "primaries": "BT709",
+               "range": "FULL",
+               "transfer": "IEC61966_2_1"
+            },
+            "damage_rect": {
+               "height": 1107,
+               "width": 1124,
+               "x": 0,
+               "y": 0
+            },
+            "filters": [  ],
+            "generate_mipmap": false,
+            "has_damage_from_contributing_content": true,
+            "has_transparent_background": false,
+            "id": "113",
+            "output_rect": {
+               "height": 1107,
+               "width": 1140,
+               "x": 0,
+               "y": 0
+            },
+            "quad_list": [ {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 1107,
+                  "width": 16,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 113 ],
+               "shared_quad_state_index": 0,
+               "tex_coord_rect": {
+                  "height": 1107.0,
+                  "width": 16.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 1120,
+                  "width": 64
+               },
+               "visible_rect": {
+                  "height": 1107,
+                  "width": 16,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 1051,
+                  "width": 72,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 81 ],
+               "shared_quad_state_index": 1,
+               "tex_coord_rect": {
+                  "height": 1051.0,
+                  "width": 72.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 1088,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 1051,
+                  "width": 72,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 56,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 91 ],
+               "shared_quad_state_index": 2,
+               "tex_coord_rect": {
+                  "height": 56.0,
+                  "width": 1124.0,
+                  "x": 0.0,
+                  "y": 142.0
+               },
+               "texture_size": {
+                  "height": 288,
+                  "width": 1152
+               },
+               "visible_rect": {
+                  "height": 56,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 56,
+                  "width": 106,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 89 ],
+               "shared_quad_state_index": 3,
+               "tex_coord_rect": {
+                  "height": 56.0,
+                  "width": 106.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 56,
+                  "width": 106,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 56,
+                  "width": 575,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 83 ],
+               "shared_quad_state_index": 4,
+               "tex_coord_rect": {
+                  "height": 56.0,
+                  "width": 575.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 576
+               },
+               "visible_rect": {
+                  "height": 56,
+                  "width": 575,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 56,
+                  "width": 477,
+                  "x": 575,
+                  "y": 0
+               },
+               "resources": [ 85 ],
+               "shared_quad_state_index": 4,
+               "tex_coord_rect": {
+                  "height": 56.0,
+                  "width": 477.0,
+                  "x": 1.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 576
+               },
+               "visible_rect": {
+                  "height": 56,
+                  "width": 477,
+                  "x": 575,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 58,
+                  "width": 1052,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 87 ],
+               "shared_quad_state_index": 5,
+               "tex_coord_rect": {
+                  "height": 58.0,
+                  "width": 1052.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 1088
+               },
+               "visible_rect": {
+                  "height": 58,
+                  "width": 1052,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 18,
+                  "width": 80,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 110 ],
+               "shared_quad_state_index": 6,
+               "tex_coord_rect": {
+                  "height": 18.0,
+                  "width": 80.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 18,
+                  "width": 80,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 18,
+                  "width": 76,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 111 ],
+               "shared_quad_state_index": 7,
+               "tex_coord_rect": {
+                  "height": 18.0,
+                  "width": 76.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 18,
+                  "width": 76,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 18,
+                  "width": 66,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 109 ],
+               "shared_quad_state_index": 8,
+               "tex_coord_rect": {
+                  "height": 18.0,
+                  "width": 66.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 18,
+                  "width": 66,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 18,
+                  "width": 60,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 104 ],
+               "shared_quad_state_index": 9,
+               "tex_coord_rect": {
+                  "height": 18.0,
+                  "width": 60.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 64
+               },
+               "visible_rect": {
+                  "height": 18,
+                  "width": 60,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 18,
+                  "width": 93,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 103 ],
+               "shared_quad_state_index": 10,
+               "tex_coord_rect": {
+                  "height": 18.0,
+                  "width": 93.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 18,
+                  "width": 93,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 18,
+                  "width": 100,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 105 ],
+               "shared_quad_state_index": 11,
+               "tex_coord_rect": {
+                  "height": 18.0,
+                  "width": 100.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 18,
+                  "width": 100,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 18,
+                  "width": 93,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 77 ],
+               "shared_quad_state_index": 12,
+               "tex_coord_rect": {
+                  "height": 18.0,
+                  "width": 93.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 18,
+                  "width": 93,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 18,
+                  "width": 113,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 92 ],
+               "shared_quad_state_index": 13,
+               "tex_coord_rect": {
+                  "height": 18.0,
+                  "width": 113.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 18,
+                  "width": 113,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 18,
+                  "width": 128,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 90 ],
+               "shared_quad_state_index": 14,
+               "tex_coord_rect": {
+                  "height": 18.0,
+                  "width": 128.0,
+                  "x": 0.0,
+                  "y": 0.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 192
+               },
+               "visible_rect": {
+                  "height": 18,
+                  "width": 128,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 15,
+                  "width": 141,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 78 ],
+               "shared_quad_state_index": 15,
+               "tex_coord_rect": {
+                  "height": 15.0,
+                  "width": 141.0,
+                  "x": 0.0,
+                  "y": 3.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 192
+               },
+               "visible_rect": {
+                  "height": 15,
+                  "width": 141,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 15,
+                  "width": 75,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 82 ],
+               "shared_quad_state_index": 16,
+               "tex_coord_rect": {
+                  "height": 15.0,
+                  "width": 75.0,
+                  "x": 0.0,
+                  "y": 3.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 15,
+                  "width": 75,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": true,
+               "rect": {
+                  "height": 15,
+                  "width": 84,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 79 ],
+               "shared_quad_state_index": 17,
+               "tex_coord_rect": {
+                  "height": 15.0,
+                  "width": 84.0,
+                  "x": 0.0,
+                  "y": 3.0
+               },
+               "texture_size": {
+                  "height": 64,
+                  "width": 128
+               },
+               "visible_rect": {
+                  "height": 15,
+                  "width": 84,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 190,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               },
+               "resources": [ 99 ],
+               "shared_quad_state_index": 18,
+               "tex_coord_rect": {
+                  "height": 190.0,
+                  "width": 1124.0,
+                  "x": 0.0,
+                  "y": 97.0
+               },
+               "texture_size": {
+                  "height": 288,
+                  "width": 1152
+               },
+               "visible_rect": {
+                  "height": 190,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 286,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 190
+               },
+               "resources": [ 100 ],
+               "shared_quad_state_index": 18,
+               "tex_coord_rect": {
+                  "height": 286.0,
+                  "width": 1124.0,
+                  "x": 0.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 288,
+                  "width": 1152
+               },
+               "visible_rect": {
+                  "height": 286,
+                  "width": 1052,
+                  "x": 72,
+                  "y": 190
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 286,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 476
+               },
+               "resources": [ 101 ],
+               "shared_quad_state_index": 18,
+               "tex_coord_rect": {
+                  "height": 286.0,
+                  "width": 1124.0,
+                  "x": 0.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 288,
+                  "width": 1152
+               },
+               "visible_rect": {
+                  "height": 286,
+                  "width": 1052,
+                  "x": 72,
+                  "y": 476
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 286,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 762
+               },
+               "resources": [ 106 ],
+               "shared_quad_state_index": 18,
+               "tex_coord_rect": {
+                  "height": 286.0,
+                  "width": 1124.0,
+                  "x": 0.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 288,
+                  "width": 1152
+               },
+               "visible_rect": {
+                  "height": 286,
+                  "width": 1052,
+                  "x": 72,
+                  "y": 762
+               }
+            }, {
+               "force_anti_aliasing_off": false,
+               "is_premultiplied": true,
+               "material": "kTiledContent",
+               "nearest_neighbor": false,
+               "needs_blending": false,
+               "rect": {
+                  "height": 59,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 1048
+               },
+               "resources": [ 108 ],
+               "shared_quad_state_index": 18,
+               "tex_coord_rect": {
+                  "height": 59.0,
+                  "width": 1124.0,
+                  "x": 0.0,
+                  "y": 1.0
+               },
+               "texture_size": {
+                  "height": 288,
+                  "width": 1152
+               },
+               "visible_rect": {
+                  "height": 59,
+                  "width": 1052,
+                  "x": 72,
+                  "y": 1048
+               }
+            }, {
+               "color": -394759,
+               "force_anti_aliasing_off": false,
+               "material": "kSolidColor",
+               "needs_blending": false,
+               "rect": {
+                  "height": 1107,
+                  "width": 16,
+                  "x": 1124,
+                  "y": 0
+               },
+               "resources": [  ],
+               "shared_quad_state_index": 19,
+               "visible_rect": {
+                  "height": 1107,
+                  "width": 16,
+                  "x": 1124,
+                  "y": 0
+               }
+            } ],
+            "shared_quad_state_list": [ {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 1107,
+                  "width": 16,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1124.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 1107,
+                  "width": 16,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": true,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 1107,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 1051,
+                  "width": 72,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 56.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 1051,
+                  "width": 72,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 1107,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 1056,
+                  "width": 1124,
+                  "x": 0,
+                  "y": -1000
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 56,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 1107,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 56,
+                  "width": 106,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1018.0, 57.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 56,
+                  "width": 106,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 56,
+                  "width": 1052,
+                  "x": 72,
+                  "y": 57
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 56,
+                  "width": 2033,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 72.0, 57.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 56,
+                  "width": 1052,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 1107,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 58,
+                  "width": 1052,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 72.0, 56.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 58,
+                  "width": 1052,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 18,
+                  "width": 172,
+                  "x": 822,
+                  "y": 1083
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 80,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 822.0, 1083.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 18,
+                  "width": 80,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 18,
+                  "width": 88,
+                  "x": 486,
+                  "y": 1083
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 76,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 486.0, 1083.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 18,
+                  "width": 76,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 18,
+                  "width": 168,
+                  "x": 150,
+                  "y": 1063
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 66,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 150.0, 1063.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 18,
+                  "width": 66,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 18,
+                  "width": 150,
+                  "x": 822,
+                  "y": 681
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 60,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 822.0, 681.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 18,
+                  "width": 60,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 18,
+                  "width": 172,
+                  "x": 486,
+                  "y": 681
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 93,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 486.0, 681.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 18,
+                  "width": 93,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 18,
+                  "width": 149,
+                  "x": 150,
+                  "y": 701
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 100,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 150.0, 701.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 18,
+                  "width": 100,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 18,
+                  "width": 176,
+                  "x": 822,
+                  "y": 311
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 93,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 822.0, 311.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 18,
+                  "width": 93,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 18,
+                  "width": 157,
+                  "x": 486,
+                  "y": 291
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 113,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 486.0, 291.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 18,
+                  "width": 113,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 18,
+                  "width": 172,
+                  "x": 150,
+                  "y": 291
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 128,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 150.0, 291.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 18,
+                  "width": 128,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 15,
+                  "width": 235,
+                  "x": 822,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 141,
+                  "x": 0,
+                  "y": -3
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 822.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 15,
+                  "width": 141,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 15,
+                  "width": 234,
+                  "x": 486,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 75,
+                  "x": 0,
+                  "y": -3
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 486.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 15,
+                  "width": 75,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": false,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 15,
+                  "width": 172,
+                  "x": 150,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 18,
+                  "width": 84,
+                  "x": 0,
+                  "y": -3
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 150.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 15,
+                  "width": 84,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": true,
+               "blend_mode": "kSrcOver",
+               "clip_rect": {
+                  "height": 1107,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               },
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 4396,
+                  "width": 1124,
+                  "x": 0,
+                  "y": -669
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 1107,
+                  "width": 1124,
+                  "x": 0,
+                  "y": 0
+               }
+            }, {
+               "are_contents_opaque": true,
+               "blend_mode": "kSrcOver",
+               "de_jelly_delta_y": 0.0,
+               "is_fast_rounded_corner": false,
+               "opacity": 1.0,
+               "quad_layer_rect": {
+                  "height": 1107,
+                  "width": 1140,
+                  "x": 0,
+                  "y": 0
+               },
+               "quad_to_target_transform": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+               "rounded_corner_bounds": {
+                  "type": "kEmpty"
+               },
+               "sorting_context_id": 0,
+               "visible_quad_layer_rect": {
+                  "height": 1107,
+                  "width": 1140,
+                  "x": 0,
+                  "y": 0
+               }
+            } ],
+            "transform_to_root_target": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ]
+         } ]
+      }
+   },
+   "frame_index": 25,
+   "surface_id": {
+      "child_seq": 1,
+      "client_id": 20,
+      "embed_token": "40EF4BADBEA6D438ECDA5BCAAD99E819",
+      "parent_seq": 3,
+      "sink_id": 2
+   }
+} ]
diff --git a/content/app/content_main.cc b/content/app/content_main.cc
index 48f236f..df88067d 100644
--- a/content/app/content_main.cc
+++ b/content/app/content_main.cc
@@ -25,6 +25,7 @@
 #include "base/trace_event/trace_config.h"
 #include "base/trace_event/trace_log.h"
 #include "build/build_config.h"
+#include "build/chromeos_buildflags.h"
 #include "components/tracing/common/trace_to_console.h"
 #include "components/tracing/common/tracing_switches.h"
 #include "content/app/content_main_runner_impl.h"
@@ -167,12 +168,18 @@
   const auto& command_line = *base::CommandLine::ForCurrentProcess();
   const bool is_browser = !command_line.HasSwitch(switches::kProcessType);
   if (is_browser) {
-    if (mojo::PlatformChannel::CommandLineHasPassedEndpoint(command_line)) {
-      config->is_broker_process = false;
+    // On Lacros, Chrome is not always the broker, because ash-chrome is.
+    // Otherwise, look at the command line flag to decide whether it is
+    // a broker.
+    config->is_broker_process =
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+        false
+#else
+        !mojo::PlatformChannel::CommandLineHasPassedEndpoint(command_line)
+#endif
+        ;
+    if (!config->is_broker_process)
       config->force_direct_shared_memory_allocation = true;
-    } else {
-      config->is_broker_process = true;
-    }
   } else {
 #if defined(OS_WIN)
     if (base::win::GetVersion() >= base::win::Version::WIN8_1) {
diff --git a/content/browser/android/dialog_overlay_impl.cc b/content/browser/android/dialog_overlay_impl.cc
index d221038..ebcf00c 100644
--- a/content/browser/android/dialog_overlay_impl.cc
+++ b/content/browser/android/dialog_overlay_impl.cc
@@ -50,7 +50,7 @@
       content::WebContents::FromRenderFrameHost(rfhi));
 
   // If the overlay would not be immediately used, fail the request.
-  if (!rfhi->IsCurrent() || !web_contents_impl || web_contents_impl->IsHidden())
+  if (!rfhi->IsActive() || !web_contents_impl || web_contents_impl->IsHidden())
     return 0;
 
   // Dialog-based overlays are not supported for persistent video.
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 3a71829..2e4b5d0f 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -9225,9 +9225,9 @@
               ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1)));
 }
 
-// Navigate from A(B) to C and check IsCurrent status for RenderFrameHost A
+// Navigate from A(B) to C and check IsActive status for RenderFrameHost A
 // and B before and after entering back-forward cache.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CheckIsCurrent) {
+IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, CheckIsActive) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_a(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b)"));
@@ -9238,16 +9238,16 @@
   RenderFrameHostImpl* rfh_a = current_frame_host();
   RenderFrameHostImpl* rfh_b = rfh_a->child_at(0)->current_frame_host();
 
-  EXPECT_TRUE(rfh_a->IsCurrent());
-  EXPECT_TRUE(rfh_b->IsCurrent());
+  EXPECT_TRUE(rfh_a->IsActive());
+  EXPECT_TRUE(rfh_b->IsActive());
 
   // 2) Navigate to C.
   EXPECT_TRUE(NavigateToURL(shell(), url_c));
   EXPECT_TRUE(rfh_a->IsInBackForwardCache());
   EXPECT_TRUE(rfh_b->IsInBackForwardCache());
 
-  EXPECT_FALSE(rfh_a->IsCurrent());
-  EXPECT_FALSE(rfh_b->IsCurrent());
+  EXPECT_FALSE(rfh_a->IsActive());
+  EXPECT_FALSE(rfh_b->IsActive());
 }
 
 // Test that LifecycleStateImpl is updated correctly when page enters and
diff --git a/content/browser/background_sync/background_sync_service_impl_test_harness.cc b/content/browser/background_sync/background_sync_service_impl_test_harness.cc
index 0f371ee..da71875 100644
--- a/content/browser/background_sync/background_sync_service_impl_test_harness.cc
+++ b/content/browser/background_sync/background_sync_service_impl_test_harness.cc
@@ -186,7 +186,7 @@
 
   embedded_worker_helper_->context_wrapper()->FindReadyRegistrationForId(
       sw_registration_id_,
-      blink::StorageKey(url::Origin::Create(GURL(kServiceWorkerScope))),
+      blink::StorageKey::CreateFromStringForTesting(kServiceWorkerScope),
       base::BindOnce(FindServiceWorkerRegistrationCallback, &sw_registration_));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(sw_registration_);
diff --git a/content/browser/cache_storage/cache_storage_context_impl.cc b/content/browser/cache_storage/cache_storage_context_impl.cc
index 327d150..cbce26b0 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.cc
+++ b/content/browser/cache_storage/cache_storage_context_impl.cc
@@ -57,6 +57,10 @@
     mojo::PendingReceiver<storage::mojom::CacheStorageControl> control,
     const base::FilePath& user_data_directory,
     scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+    mojo::PendingReceiver<storage::mojom::QuotaClient>
+        cache_storage_client_remote,
+    mojo::PendingReceiver<storage::mojom::QuotaClient>
+        background_fetch_client_remote,
     mojo::PendingRemote<storage::mojom::BlobStorageContext>
         blob_storage_context) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -73,29 +77,18 @@
   DCHECK(!cache_manager_);
   cache_manager_ = LegacyCacheStorageManager::Create(
       user_data_directory, std::move(cache_task_runner),
-      base::SequencedTaskRunnerHandle::Get(), quota_manager_proxy,
+      base::SequencedTaskRunnerHandle::Get(), std::move(quota_manager_proxy),
       base::MakeRefCounted<BlobStorageContextWrapper>(
           std::move(blob_storage_context)));
 
-  mojo::PendingRemote<storage::mojom::QuotaClient> cache_storage_client;
   mojo::MakeSelfOwnedReceiver(
       std::make_unique<CacheStorageQuotaClient>(
           cache_manager_, storage::mojom::CacheStorageOwner::kCacheAPI),
-      cache_storage_client.InitWithNewPipeAndPassReceiver());
-  quota_manager_proxy->RegisterClient(
-      std::move(cache_storage_client),
-      storage::QuotaClientType::kServiceWorkerCache,
-      {blink::mojom::StorageType::kTemporary});
-
-  mojo::PendingRemote<storage::mojom::QuotaClient> background_fetch_client;
+      std::move(cache_storage_client_remote));
   mojo::MakeSelfOwnedReceiver(
       std::make_unique<CacheStorageQuotaClient>(
           cache_manager_, storage::mojom::CacheStorageOwner::kBackgroundFetch),
-      background_fetch_client.InitWithNewPipeAndPassReceiver());
-  quota_manager_proxy->RegisterClient(
-      std::move(background_fetch_client),
-      storage::QuotaClientType::kBackgroundFetch,
-      {blink::mojom::StorageType::kTemporary});
+      std::move(background_fetch_client_remote));
 }
 
 void CacheStorageContextImpl::AddReceiver(
diff --git a/content/browser/cache_storage/cache_storage_context_impl.h b/content/browser/cache_storage/cache_storage_context_impl.h
index d6aa151d..1bb315c0 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.h
+++ b/content/browser/cache_storage/cache_storage_context_impl.h
@@ -14,6 +14,7 @@
 #include "base/threading/sequence_bound.h"
 #include "components/services/storage/public/mojom/blob_storage_context.mojom.h"
 #include "components/services/storage/public/mojom/cache_storage_control.mojom.h"
+#include "components/services/storage/public/mojom/quota_client.mojom-forward.h"
 #include "content/browser/cache_storage/cache_storage_manager.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -58,6 +59,10 @@
   void Init(mojo::PendingReceiver<storage::mojom::CacheStorageControl> control,
             const base::FilePath& user_data_directory,
             scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy,
+            mojo::PendingReceiver<storage::mojom::QuotaClient>
+                cache_storage_client_remote,
+            mojo::PendingReceiver<storage::mojom::QuotaClient>
+                background_fetch_client_remote,
             mojo::PendingRemote<storage::mojom::BlobStorageContext>
                 blob_storage_context);
 
diff --git a/content/browser/cache_storage/cache_storage_control_wrapper.cc b/content/browser/cache_storage/cache_storage_control_wrapper.cc
index 5daa9e3..15ae5e5 100644
--- a/content/browser/cache_storage/cache_storage_control_wrapper.cc
+++ b/content/browser/cache_storage/cache_storage_control_wrapper.cc
@@ -16,11 +16,33 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(quota_manager_proxy);
 
+  // QuotaManagerProxy::RegisterClient() must be called during construction
+  // until crbug.com/1182630 is fixed.
+  mojo::PendingRemote<storage::mojom::QuotaClient> cache_storage_client_remote;
+  mojo::PendingReceiver<storage::mojom::QuotaClient>
+      cache_storage_client_receiver =
+          cache_storage_client_remote.InitWithNewPipeAndPassReceiver();
+  quota_manager_proxy->RegisterClient(
+      std::move(cache_storage_client_remote),
+      storage::QuotaClientType::kServiceWorkerCache,
+      {blink::mojom::StorageType::kTemporary});
+  mojo::PendingRemote<storage::mojom::QuotaClient>
+      background_fetch_client_remote;
+  mojo::PendingReceiver<storage::mojom::QuotaClient>
+      background_fetch_client_receiver =
+          background_fetch_client_remote.InitWithNewPipeAndPassReceiver();
+  quota_manager_proxy->RegisterClient(
+      std::move(background_fetch_client_remote),
+      storage::QuotaClientType::kBackgroundFetch,
+      {blink::mojom::StorageType::kTemporary});
+
   cache_storage_context_ = base::SequenceBound<CacheStorageContextImpl>(
       CacheStorageContextImpl::CreateSchedulerTaskRunner());
   cache_storage_context_.AsyncCall(&CacheStorageContextImpl::Init)
       .WithArgs(cache_storage_control_.BindNewPipeAndPassReceiver(),
                 user_data_directory, std::move(quota_manager_proxy),
+                std::move(cache_storage_client_receiver),
+                std::move(background_fetch_client_receiver),
                 std::move(blob_storage_context));
 
   if (special_storage_policy) {
diff --git a/content/browser/compute_pressure/compute_pressure_host.cc b/content/browser/compute_pressure/compute_pressure_host.cc
index dcaedab..71cc854 100644
--- a/content/browser/compute_pressure/compute_pressure_host.cc
+++ b/content/browser/compute_pressure/compute_pressure_host.cc
@@ -76,7 +76,7 @@
 
   GlobalFrameRoutingId frame_id = receivers_.current_context();
   RenderFrameHost* rfh = content::RenderFrameHost::FromID(frame_id);
-  if (!rfh || !rfh->IsCurrent()) {
+  if (!rfh || !rfh->IsActive()) {
     std::move(callback).Run(
         blink::mojom::ComputePressureStatus::kSecurityError);
     return;
@@ -143,7 +143,7 @@
     GlobalFrameRoutingId frame_id = observer_contexts_[observer_id];
 
     RenderFrameHost* rfh = content::RenderFrameHost::FromID(frame_id);
-    if (!rfh || !rfh->IsCurrent()) {
+    if (!rfh || !rfh->IsActive()) {
       // TODO(oyiptong): Is it safe to disconnect observers in this state?
       continue;
     }
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index 91a67e44..079a5f3 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -261,7 +261,7 @@
   if (!context_)
     return CreateContextErrorResponse();
   context_->StartActiveServiceWorker(
-      GURL(scope_url), blink::StorageKey(url::Origin::Create(GURL(scope_url))),
+      GURL(scope_url), blink::StorageKey::CreateFromStringForTesting(scope_url),
       base::DoNothing());
   return Response::Success();
 }
@@ -272,7 +272,8 @@
   if (!context_)
     return CreateContextErrorResponse();
   context_->SkipWaitingWorker(
-      GURL(scope_url), blink::StorageKey(url::Origin::Create(GURL(scope_url))));
+      GURL(scope_url),
+      blink::StorageKey::CreateFromStringForTesting(scope_url));
   return Response::Success();
 }
 
@@ -311,7 +312,8 @@
   if (!context_)
     return CreateContextErrorResponse();
   context_->UpdateRegistration(
-      GURL(scope_url), blink::StorageKey(url::Origin::Create(GURL(scope_url))));
+      GURL(scope_url),
+      blink::StorageKey::CreateFromStringForTesting(scope_url));
   return Response::Success();
 }
 
diff --git a/content/browser/file_system_access/file_system_access_handle_base.cc b/content/browser/file_system_access/file_system_access_handle_base.cc
index 61aa5da..799bf5f 100644
--- a/content/browser/file_system_access/file_system_access_handle_base.cc
+++ b/content/browser/file_system_access/file_system_access_handle_base.cc
@@ -45,7 +45,7 @@
     Observe(WebContentsImpl::FromRenderFrameHostID(context_.frame_id));
 
     // Disable back-forward cache as File System Access's usage of
-    // RenderFrameHost::IsCurrent at the moment is not compatible with bfcache.
+    // RenderFrameHost::IsActive at the moment is not compatible with bfcache.
     BackForwardCache::DisableForRenderFrameHost(
         context_.frame_id,
         BackForwardCacheDisable::DisabledReason(
diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
index 21e27bd..4b4066a 100644
--- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
+++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc
@@ -1635,7 +1635,7 @@
 
   const base::FilePath path_base = temp_dir_.GetPath();
   const StorageKey storage_key =
-      StorageKey(Origin::Create(GURL("http://www.google.com/")));
+      StorageKey::CreateFromStringForTesting("http://www.google.com/");
   ASSERT_FALSE(path_base.empty());
   ASSERT_TRUE(PathIsWritable(path_base));
 
diff --git a/content/browser/keyboard_lock/keyboard_lock_service_impl.cc b/content/browser/keyboard_lock/keyboard_lock_service_impl.cc
index 27c9a4b7..dc1bf9f 100644
--- a/content/browser/keyboard_lock/keyboard_lock_service_impl.cc
+++ b/content/browser/keyboard_lock/keyboard_lock_service_impl.cc
@@ -64,7 +64,7 @@
   else
     LogKeyboardLockMethodCalled(KeyboardLockMethods::kRequestSomeKeys);
 
-  if (!render_frame_host_->IsCurrent()) {
+  if (!render_frame_host_->IsActive()) {
     std::move(callback).Run(KeyboardLockRequestResult::kFrameDetachedError);
     return;
   }
diff --git a/content/browser/manifest/manifest_manager_host.cc b/content/browser/manifest/manifest_manager_host.cc
index 2c02ec5..aa2d6e9b 100644
--- a/content/browser/manifest/manifest_manager_host.cc
+++ b/content/browser/manifest/manifest_manager_host.cc
@@ -87,7 +87,7 @@
 
 void ManifestManagerHost::ManifestUrlChanged(
     const absl::optional<GURL>& manifest_url) {
-  if (!manifest_manager_frame_->IsCurrent())
+  if (!manifest_manager_frame_->IsActive())
     return;
 
   manifest_manager_frame_->UpdateManifestURL(manifest_url);
diff --git a/content/browser/media/capture_handle_manager.cc b/content/browser/media/capture_handle_manager.cc
index c95fc9b..461bc38 100644
--- a/content/browser/media/capture_handle_manager.cc
+++ b/content/browser/media/capture_handle_manager.cc
@@ -118,12 +118,12 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   auto* const capturer_rfhi = RenderFrameHostImpl::FromID(capturer);
-  if (!capturer_rfhi || !capturer_rfhi->IsCurrent()) {
+  if (!capturer_rfhi || !capturer_rfhi->IsActive()) {
     return nullptr;
   }
 
   auto* const captured_rfhi = RenderFrameHostImpl::FromID(captured);
-  if (!captured_rfhi || !captured_rfhi->IsCurrent()) {
+  if (!captured_rfhi || !captured_rfhi->IsActive()) {
     return nullptr;
   }
 
@@ -162,7 +162,7 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   auto* const capturer_rfhi = RenderFrameHostImpl::FromID(capturer_);
-  if (!capturer_rfhi || !capturer_rfhi->IsCurrent()) {
+  if (!capturer_rfhi || !capturer_rfhi->IsActive()) {
     DVLOG(1) << "Invalid capturer: " << capturer_ << ".";
     return;
   }
diff --git a/content/browser/net/trust_token_browsertest.cc b/content/browser/net/trust_token_browsertest.cc
index a92d965..151899a 100644
--- a/content/browser/net/trust_token_browsertest.cc
+++ b/content/browser/net/trust_token_browsertest.cc
@@ -155,6 +155,7 @@
       AllOf(
           HasHeader(network::kTrustTokensRequestHeaderSecRedemptionRecord,
                     StrEq("")),
+          Not(HasHeader(network::kTrustTokensSecTrustTokenVersionHeader)),
           Not(HasHeader(network::kTrustTokensRequestHeaderSecTime)),
           Not(HasHeader(
               network::
@@ -250,6 +251,7 @@
       Optional(AllOf(
           Not(HasHeader(network::kTrustTokensRequestHeaderSecTime)),
           HasHeader(network::kTrustTokensRequestHeaderSecRedemptionRecord),
+          HasHeader(network::kTrustTokensSecTrustTokenVersionHeader),
           SignaturesAreWellFormedAndVerify(),
           SecSignatureHeaderKeyHashes(IsSubsetOf(
               request_handler_.hashes_of_redemption_bound_public_keys())))));
@@ -312,6 +314,7 @@
       Optional(AllOf(
           Not(HasHeader(network::kTrustTokensRequestHeaderSecTime)),
           HasHeader(network::kTrustTokensRequestHeaderSecRedemptionRecord),
+          HasHeader(network::kTrustTokensSecTrustTokenVersionHeader),
           SignaturesAreWellFormedAndVerify(),
           SecSignatureHeaderKeyHashes(IsSubsetOf(
               request_handler_.hashes_of_redemption_bound_public_keys())))));
@@ -349,6 +352,7 @@
       Optional(AllOf(
           Not(HasHeader(network::kTrustTokensRequestHeaderSecTime)),
           HasHeader(network::kTrustTokensRequestHeaderSecRedemptionRecord),
+          HasHeader(network::kTrustTokensSecTrustTokenVersionHeader),
           SignaturesAreWellFormedAndVerify(),
           SecSignatureHeaderKeyHashes(IsSubsetOf(
               request_handler_.hashes_of_redemption_bound_public_keys())))));
@@ -436,6 +440,7 @@
       Optional(AllOf(
           Not(HasHeader(network::kTrustTokensRequestHeaderSecTime)),
           HasHeader(network::kTrustTokensRequestHeaderSecRedemptionRecord),
+          HasHeader(network::kTrustTokensSecTrustTokenVersionHeader),
           SignaturesAreWellFormedAndVerify(),
           SecSignatureHeaderKeyHashes(IsSubsetOf(
               request_handler_.hashes_of_redemption_bound_public_keys())))));
diff --git a/content/browser/portal/portal.cc b/content/browser/portal/portal.cc
index bdd5073e..6b161a0 100644
--- a/content/browser/portal/portal.cc
+++ b/content/browser/portal/portal.cc
@@ -469,7 +469,7 @@
 std::pair<bool, blink::mojom::PortalActivateResult> Portal::CanActivate() {
   WebContentsImpl* outer_contents = GetPortalHostContents();
 
-  DCHECK(owner_render_frame_host_->IsCurrent())
+  DCHECK(owner_render_frame_host_->IsActive())
       << "The binding should have been closed when the portal's outer "
          "FrameTreeNode was deleted due to swap out.";
 
diff --git a/content/browser/prerender/prerender_host_registry.cc b/content/browser/prerender/prerender_host_registry.cc
index c4091028..17a3e7f 100644
--- a/content/browser/prerender/prerender_host_registry.cc
+++ b/content/browser/prerender/prerender_host_registry.cc
@@ -97,20 +97,23 @@
     PrerenderHost::FinalStatus final_status) {
   TRACE_EVENT1("navigation", "PrerenderHostRegistry::AbandonHost",
                "frame_tree_node_id", frame_tree_node_id);
+
+  auto found = prerender_host_by_frame_tree_node_id_.find(frame_tree_node_id);
+  if (found == prerender_host_by_frame_tree_node_id_.end())
+    return;
+
   // Remove the prerender host from the host maps so that it's not used for
   // activation during asynchronous deletion.
-  std::unique_ptr<PrerenderHost> prerender_host =
-      AbandonHostInternal(frame_tree_node_id);
-  if (prerender_host) {
-    // Report only if this is the first valid call for `frame_tree_node_id`.
-    prerender_host->RecordFinalStatus(PassKey(), final_status);
+  std::unique_ptr<PrerenderHost> prerender_host = std::move(found->second);
+  prerender_host_by_frame_tree_node_id_.erase(found);
 
-    // Asynchronously delete the prerender host.
-    to_be_deleted_hosts_.push_back(std::move(prerender_host));
-    GetUIThreadTaskRunner({})->PostTask(
-        FROM_HERE, base::BindOnce(&PrerenderHostRegistry::DeleteAbandonedHosts,
-                                  weak_factory_.GetWeakPtr()));
-  }
+  prerender_host->RecordFinalStatus(PassKey(), final_status);
+
+  // Asynchronously delete the prerender host.
+  to_be_deleted_hosts_.push_back(std::move(prerender_host));
+  GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&PrerenderHostRegistry::DeleteAbandonedHosts,
+                                weak_factory_.GetWeakPtr()));
 }
 
 int PrerenderHostRegistry::ReserveHostToActivate(
@@ -238,16 +241,6 @@
   return nullptr;
 }
 
-std::unique_ptr<PrerenderHost> PrerenderHostRegistry::AbandonHostInternal(
-    int frame_tree_node_id) {
-  auto found = prerender_host_by_frame_tree_node_id_.find(frame_tree_node_id);
-  if (found == prerender_host_by_frame_tree_node_id_.end())
-    return nullptr;
-  std::unique_ptr<PrerenderHost> prerender_host = std::move(found->second);
-  prerender_host_by_frame_tree_node_id_.erase(found);
-  return prerender_host;
-}
-
 void PrerenderHostRegistry::DeleteAbandonedHosts() {
   to_be_deleted_hosts_.clear();
 }
diff --git a/content/browser/prerender/prerender_host_registry.h b/content/browser/prerender/prerender_host_registry.h
index 1b55ad8f..7f4d5be 100644
--- a/content/browser/prerender/prerender_host_registry.h
+++ b/content/browser/prerender/prerender_host_registry.h
@@ -128,7 +128,6 @@
   PrerenderHost* FindHostByUrlForTesting(const GURL& prerendering_url);
 
  private:
-  std::unique_ptr<PrerenderHost> AbandonHostInternal(int frame_tree_node_id);
   void DeleteAbandonedHosts();
 
   void NotifyTrigger(const GURL& url);
diff --git a/content/browser/renderer_host/back_forward_cache_disable.cc b/content/browser/renderer_host/back_forward_cache_disable.cc
index f9b9cc6..1a49916 100644
--- a/content/browser/renderer_host/back_forward_cache_disable.cc
+++ b/content/browser/renderer_host/back_forward_cache_disable.cc
@@ -33,8 +33,6 @@
       return "WebUSB";
     case BackForwardCacheDisable::DisabledReasonId::kMediaSession:
       return "MediaSession";
-    default:
-      return "Unknown (default)";
   }
 }
 
diff --git a/content/browser/renderer_host/back_forward_cache_impl.cc b/content/browser/renderer_host/back_forward_cache_impl.cc
index cd9adc7..3a3f148 100644
--- a/content/browser/renderer_host/back_forward_cache_impl.cc
+++ b/content/browser/renderer_host/back_forward_cache_impl.cc
@@ -576,14 +576,14 @@
 
   // If this function is called after we navigated to a new RenderFrameHost,
   // then |rfh| must already be replaced by the new RenderFrameHost. If this
-  // function is called before we navigated, then |rfh| must be a current
+  // function is called before we navigated, then |rfh| must be an active
   // RenderFrameHost.
-  bool is_current_rfh = rfh->IsCurrent();
+  bool is_active_rfh = rfh->IsActive();
 
   // Two pages in the same BrowsingInstance can script each other. When a page
   // can be scripted from outside, it can't enter the BackForwardCache.
   //
-  // If the |rfh| is not a "current" RenderFrameHost anymore, the
+  // If the |rfh| is not an "active" RenderFrameHost anymore, the
   // "RelatedActiveContentsCount" below is compared against 0, not 1. This is
   // because |rfh| is not "active" itself.
   //
@@ -593,7 +593,7 @@
   // CanPotentiallyStorePageLater instead of CanStorePageNow because it's needed
   // to determine whether to do a proactive BrowsingInstance swap or not, which
   // should not be done if the page has related active contents.
-  unsigned expected_related_active_contents_count = is_current_rfh ? 1 : 0;
+  unsigned expected_related_active_contents_count = is_active_rfh ? 1 : 0;
   // We should never have fewer than expected.
   DCHECK_GE(rfh->GetSiteInstance()->GetRelatedActiveContentsCount(),
             expected_related_active_contents_count);
diff --git a/content/browser/renderer_host/cookie_utils.cc b/content/browser/renderer_host/cookie_utils.cc
index 57df488..b686284 100644
--- a/content/browser/renderer_host/cookie_utils.cc
+++ b/content/browser/renderer_host/cookie_utils.cc
@@ -79,7 +79,7 @@
     const network::mojom::CookieAccessDetailsPtr& cookie_details) {
   RenderFrameHostImpl* root_frame_host = rfh->GetMainFrame();
 
-  if (!root_frame_host->IsCurrent())
+  if (!root_frame_host->IsActive())
     return;
 
   bool samesite_treated_as_lax_cookies = false;
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
index 28ca2358..b5ba432c 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
@@ -263,7 +263,7 @@
             DCHECK_CURRENTLY_ON(BrowserThread::UI);
             RenderFrameHostImpl* const rfhi =
                 RenderFrameHostImpl::FromID(render_process_id, render_frame_id);
-            if (!rfhi || !rfhi->IsCurrent()) {
+            if (!rfhi || !rfhi->IsActive()) {
               return;
             }
             if (rfhi != rfhi->GetMainFrame()) {
diff --git a/content/browser/renderer_host/navigator_unittest.cc b/content/browser/renderer_host/navigator_unittest.cc
index dd33548..b655e987 100644
--- a/content/browser/renderer_host/navigator_unittest.cc
+++ b/content/browser/renderer_host/navigator_unittest.cc
@@ -121,7 +121,7 @@
 
   // Commit the navigation.
   navigation->Commit();
-  EXPECT_TRUE(main_test_rfh()->IsCurrent());
+  EXPECT_TRUE(main_test_rfh()->IsActive());
   EXPECT_EQ(main_test_rfh()->lifecycle_state(),
             RenderFrameHostImpl::LifecycleStateImpl::kActive);
   if (AreDefaultSiteInstancesEnabled()) {
@@ -191,7 +191,7 @@
 
   // Commit the navigation.
   navigation->Commit();
-  EXPECT_TRUE(main_test_rfh()->IsCurrent());
+  EXPECT_TRUE(main_test_rfh()->IsActive());
   if (AreDefaultSiteInstancesEnabled()) {
     EXPECT_TRUE(main_test_rfh()->GetSiteInstance()->IsDefaultSiteInstance());
   } else {
@@ -246,7 +246,7 @@
 
   // Commit the navigation.
   navigation->Commit();
-  EXPECT_TRUE(main_test_rfh()->IsCurrent());
+  EXPECT_TRUE(main_test_rfh()->IsActive());
   EXPECT_EQ(kUrl2, contents()->GetLastCommittedURL());
   EXPECT_FALSE(GetSpeculativeRenderFrameHost(node));
 
diff --git a/content/browser/renderer_host/render_document_host_user_data_browsertest.cc b/content/browser/renderer_host/render_document_host_user_data_browsertest.cc
index 8aba392..f501247 100644
--- a/content/browser/renderer_host/render_document_host_user_data_browsertest.cc
+++ b/content/browser/renderer_host/render_document_host_user_data_browsertest.cc
@@ -319,7 +319,7 @@
   // 5) Check that the RDHUD object is not cleared after renderer process
   // crashes.
   EXPECT_EQ(top_frame_host(), rfh_a);
-  EXPECT_FALSE(pending_rfh->IsCurrent());
+  EXPECT_FALSE(pending_rfh->IsActive());
   EXPECT_FALSE(rfh_a->IsRenderFrameLive());
   EXPECT_TRUE(pending_rfh->IsRenderFrameLive());
   EXPECT_TRUE(data);
@@ -379,7 +379,7 @@
   EXPECT_EQ(navigation_request->associated_site_instance_type(),
             NavigationRequest::AssociatedSiteInstanceType::CURRENT);
   EXPECT_TRUE(current_rfh);
-  EXPECT_TRUE(current_rfh->IsCurrent());
+  EXPECT_TRUE(current_rfh->IsActive());
 
   // 4) Get the RenderDocumentHostUserData associated with speculative
   // RenderFrameHost.
@@ -572,8 +572,8 @@
   EXPECT_TRUE(data_a);
   EXPECT_TRUE(data_b);
 
-  EXPECT_FALSE(rfh_a->IsCurrent());
-  EXPECT_FALSE(rfh_b->IsCurrent());
+  EXPECT_FALSE(rfh_a->IsActive());
+  EXPECT_FALSE(rfh_b->IsActive());
 }
 
 // Tests that RenderDocumentHostUserData associated with RenderFrameHost is not
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index bf3147e..96764279c 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1550,13 +1550,11 @@
   //
   // Note that this logic is fairly subtle. It needs to include all subframes
   // and all speculative frames, but it should exclude case #1 (a main
-  // RenderFrame owned by the RenderView). It can't simply:
-  // - check |frame_tree_node_->render_manager()->speculative_frame_host()| for
-  //   equality against |this|. The speculative frame host is unset before the
-  //   speculative frame host is destroyed, so this condition would never be
-  //   matched for a speculative RFH that needs to be destroyed.
-  // - check |IsCurrent()|, because the RenderFrames in an InterstitialPageImpl
-  //   are never considered current.
+  // RenderFrame owned by the RenderView). It can't simply check
+  // |frame_tree_node_->render_manager()->speculative_frame_host()| for
+  // equality against |this|. The speculative frame host is unset before the
+  // speculative frame host is destroyed, so this condition would never be
+  // matched for a speculative RFH that needs to be destroyed.
   //
   // Directly comparing against |RenderViewHostImpl::GetMainFrame()| still has
   // one additional subtlety though: |GetMainFrame()| can sometimes return a
@@ -2503,10 +2501,9 @@
 
 gfx::AcceleratedWidget
 RenderFrameHostImpl::AccessibilityGetAcceleratedWidget() {
-  // Only the main frame's current frame host is connected to the native
-  // widget tree for accessibility, so return null if this is queried on
-  // any other frame.
-  if (!is_main_frame() || !IsCurrent())
+  // Only the active RenderFrameHost is connected to the native widget tree for
+  // accessibility, so return null if this is queried on any other frame.
+  if (!is_main_frame() || !IsActive())
     return gfx::kNullAcceleratedWidget;
 
   RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>(
@@ -2943,7 +2940,7 @@
   if (IsRenderFrameCreated()) {
     GetMojomFrameInRenderer()->Delete(intent);
 
-    if (!frame_tree_node_->IsMainFrame() && IsCurrent()) {
+    if (!frame_tree_node_->IsMainFrame() && IsActive()) {
       DCHECK_NE(lifecycle_state(), LifecycleStateImpl::kSpeculative);
       // Documents from the page in the BackForwardCache don't run their unload
       // handlers, even if they have one. As a result, this should never delay
@@ -5925,10 +5922,10 @@
   bool effective_transient_activation_state =
       params->allow_popup || frame_tree_node_->HasTransientUserActivation();
 
-  // Ignore window creation when sent from a frame that's not current or
+  // Ignore window creation when sent from a frame that's not active or
   // created.
   bool can_create_window =
-      IsCurrent() && is_render_frame_created() &&
+      IsActive() && is_render_frame_created() &&
       GetContentClient()->browser()->CanCreateWindow(
           this, GetLastCommittedURL(), GetMainFrame()->GetLastCommittedURL(),
           last_committed_origin_, params->window_container_type,
@@ -7073,7 +7070,7 @@
 
   // The renderer can exit view source mode when any error or cancellation
   // happen. When reusing the same renderer, overwrite to recover the mode.
-  if (is_view_source && IsCurrent()) {
+  if (is_view_source && IsActive()) {
     DCHECK(!GetParent());
     GetAssociatedLocalFrame()->EnableViewSourceMode();
   }
@@ -7994,20 +7991,19 @@
   }
 }
 
-bool RenderFrameHostImpl::IsCurrent() {
+bool RenderFrameHostImpl::IsActive() {
   // When the document is transitioning away from kActive/kPrerendering to a
   // yet-to-be-determined state, the RenderFrameHostManager has already
-  // updated its current RenderFrameHost, and the old document is no longer
-  // the current one. In that case, return false.
+  // updated its active RenderFrameHost, and the old document is no longer
+  // the active one. In that case, return false.
   if (has_pending_lifecycle_state_update_)
     return false;
 
-  // Only documents in the LifecycleStateImpl::kActive are considered current.
   return lifecycle_state() == LifecycleStateImpl::kActive;
 }
 
 size_t RenderFrameHostImpl::GetProxyCount() {
-  if (!IsCurrent())
+  if (!IsActive())
     return 0;
   return frame_tree_node_->render_manager()->GetProxyCount();
 }
@@ -10971,10 +10967,9 @@
   base::debug::SetCrashKeyString(lifecycle_state_key,
                                  LifecycleStateImplToString(lifecycle_state()));
 
-  static auto* const is_current_key = base::debug::AllocateCrashKeyString(
-      "is_current", base::debug::CrashKeySize::Size32);
-  base::debug::SetCrashKeyString(is_current_key,
-                                 bool_to_crash_key(IsCurrent()));
+  static auto* const is_active_key = base::debug::AllocateCrashKeyString(
+      "is_active", base::debug::CrashKeySize::Size32);
+  base::debug::SetCrashKeyString(is_active_key, bool_to_crash_key(IsActive()));
 
   static auto* const is_cross_process_subframe_key =
       base::debug::AllocateCrashKeyString("is_cross_process_subframe",
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 718c1de..c00698b 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -354,7 +354,7 @@
   bool IsRenderFrameCreated() override;
   bool IsRenderFrameLive() override;
   LifecycleState GetLifecycleState() override;
-  bool IsCurrent() override;
+  bool IsActive() override;
   bool IsInactiveAndDisallowActivation() override;
   size_t GetProxyCount() override;
   bool HasSelection() override;
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index 954c453..d013d64 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -4176,7 +4176,7 @@
   EXPECT_EQ(kid_url, child_rfh->GetLastCommittedURL());
   EXPECT_EQ(url, main_frame->GetLastCommittedURL());
   EXPECT_TRUE(main_frame->IsPendingDeletion());
-  EXPECT_FALSE(main_frame->IsCurrent());
+  EXPECT_FALSE(main_frame->IsActive());
   net::SiteForCookies computed_for_child = child_rfh->ComputeSiteForCookies();
   EXPECT_TRUE(
       net::SiteForCookies::FromUrl(url).IsEquivalent(computed_for_child))
@@ -4245,11 +4245,11 @@
 }
 
 // Start with A(B), navigate A to C. By emulating a slow unload handler B, check
-// the status of IsCurrent for subframes of A i.e., B before and after
+// the status of IsActive for subframes of A i.e., B before and after
 // navigating to C.
 // Test is flaky: https://crbug.com/1114149.
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       DISABLED_CheckIsCurrentBeforeAndAfterUnload) {
+                       DISABLED_CheckIsActiveBeforeAndAfterUnload) {
   IsolateAllSitesForTesting(base::CommandLine::ForCurrentProcess());
   GURL url_ab(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b)"));
@@ -4265,9 +4265,9 @@
   // 2) Leave rfh_b in pending deletion state.
   LeaveInPendingDeletionState(rfh_b);
 
-  // 3) Check the IsCurrent state of rfh_a, rfh_b before navigating to C.
-  EXPECT_TRUE(rfh_a->IsCurrent());
-  EXPECT_TRUE(rfh_b->IsCurrent());
+  // 3) Check the IsActive state of rfh_a, rfh_b before navigating to C.
+  EXPECT_TRUE(rfh_a->IsActive());
+  EXPECT_TRUE(rfh_b->IsActive());
 
   // 4) Navigate rfh_a to C.
   EXPECT_TRUE(NavigateToURL(shell(), url_c));
@@ -4282,11 +4282,11 @@
       testing::AnyOf(testing::Eq(LifecycleStateImpl::kRunningUnloadHandlers),
                      testing::Eq(LifecycleStateImpl::kInBackForwardCache)));
 
-  // 5) Check the IsCurrent state of rfh_a, rfh_b and rfh_c after navigating to
+  // 5) Check the IsActive state of rfh_a, rfh_b and rfh_c after navigating to
   // C.
-  EXPECT_FALSE(rfh_a->IsCurrent());
-  EXPECT_FALSE(rfh_b->IsCurrent());
-  EXPECT_TRUE(rfh_c->IsCurrent());
+  EXPECT_FALSE(rfh_a->IsActive());
+  EXPECT_FALSE(rfh_b->IsActive());
+  EXPECT_TRUE(rfh_c->IsActive());
 }
 
 // Test the LifecycleStateImpl is updated correctly for the main frame during
diff --git a/content/browser/renderer_host/render_frame_host_manager_unittest.cc b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
index e2bb55e..cf1effb3d 100644
--- a/content/browser/renderer_host/render_frame_host_manager_unittest.cc
+++ b/content/browser/renderer_host/render_frame_host_manager_unittest.cc
@@ -590,7 +590,7 @@
   navigation->Commit();
   TestRenderFrameHost* dest_rfh = contents()->GetMainFrame();
   EXPECT_TRUE(ntp_rfh->IsPendingDeletion());
-  EXPECT_TRUE(dest_rfh->IsCurrent());
+  EXPECT_TRUE(dest_rfh->IsActive());
 
   // The new RFH should be able to update its favicons.
   {
@@ -1143,7 +1143,7 @@
 
   // We should be able to navigate forward.
   NavigationSimulator::GoForward(contents());
-  EXPECT_TRUE(main_test_rfh()->IsCurrent());
+  EXPECT_TRUE(main_test_rfh()->IsActive());
 }
 
 // Test that we create RenderFrameProxy objects for the opener chain when
@@ -1603,21 +1603,21 @@
   contents()->NavigateAndCommit(kUrl1);
   TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
   RenderFrameDeletedObserver rfh_deleted_observer(rfh1);
-  EXPECT_TRUE(rfh1->IsCurrent());
+  EXPECT_TRUE(rfh1->IsActive());
 
   // Navigate to new site, simulating onbeforeunload approval.
   auto navigation =
       NavigationSimulatorImpl::CreateBrowserInitiated(kUrl2, contents());
   navigation->ReadyToCommit();
   EXPECT_TRUE(contents()->CrossProcessNavigationPending());
-  EXPECT_TRUE(rfh1->IsCurrent());
+  EXPECT_TRUE(rfh1->IsActive());
   TestRenderFrameHost* rfh2 = contents()->GetSpeculativePrimaryMainFrame();
 
   // Simulate the unload ack, unexpectedly early (before commit).  It should
   // have no effect.
   rfh1->SimulateUnloadACK();
   EXPECT_TRUE(contents()->CrossProcessNavigationPending());
-  EXPECT_TRUE(rfh1->IsCurrent());
+  EXPECT_TRUE(rfh1->IsActive());
 
   // The new page commits.
   navigation->set_drop_unload_ack(true);
@@ -1625,7 +1625,7 @@
   EXPECT_FALSE(contents()->CrossProcessNavigationPending());
   EXPECT_EQ(rfh2, contents()->GetMainFrame());
   EXPECT_TRUE(contents()->GetSpeculativePrimaryMainFrame() == nullptr);
-  EXPECT_TRUE(rfh2->IsCurrent());
+  EXPECT_TRUE(rfh2->IsActive());
   EXPECT_TRUE(rfh1->IsPendingDeletion());
 
   // Simulate the unload ack.
@@ -1654,7 +1654,7 @@
   contents()->NavigateAndCommit(kUrl1);
   TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
   RenderFrameDeletedObserver rfh_deleted_observer(rfh1);
-  EXPECT_TRUE(rfh1->IsCurrent());
+  EXPECT_TRUE(rfh1->IsActive());
 
   // Increment the number of active frames in SiteInstanceImpl so that rfh1 is
   // not deleted on unload.
@@ -1665,7 +1665,7 @@
       NavigationSimulatorImpl::CreateBrowserInitiated(kUrl2, contents());
   navigation->ReadyToCommit();
   EXPECT_TRUE(contents()->CrossProcessNavigationPending());
-  EXPECT_TRUE(rfh1->IsCurrent());
+  EXPECT_TRUE(rfh1->IsActive());
   TestRenderFrameHost* rfh2 = contents()->GetSpeculativePrimaryMainFrame();
 
   // The new page commits.
@@ -1675,7 +1675,7 @@
   EXPECT_EQ(rfh2, contents()->GetMainFrame());
   EXPECT_TRUE(contents()->GetSpeculativePrimaryMainFrame() == nullptr);
   EXPECT_TRUE(rfh1->IsPendingDeletion());
-  EXPECT_TRUE(rfh2->IsCurrent());
+  EXPECT_TRUE(rfh2->IsActive());
 
   // Simulate the unload ack.
   rfh1->OnUnloaded();
@@ -1702,7 +1702,7 @@
   contents()->NavigateAndCommit(kUrl1);
   TestRenderFrameHost* rfh1 = contents()->GetMainFrame();
   RenderFrameDeletedObserver rfh_deleted_observer(rfh1);
-  EXPECT_TRUE(rfh1->IsCurrent());
+  EXPECT_TRUE(rfh1->IsActive());
 
   // Increment the number of active frames in rfh1's SiteInstance so that the
   // SiteInstance is not deleted on unload.
@@ -1714,7 +1714,7 @@
       NavigationSimulatorImpl::CreateBrowserInitiated(kUrl2, contents());
   navigation->ReadyToCommit();
   EXPECT_TRUE(contents()->CrossProcessNavigationPending());
-  EXPECT_TRUE(rfh1->IsCurrent());
+  EXPECT_TRUE(rfh1->IsActive());
   TestRenderFrameHost* rfh2 = contents()->GetSpeculativePrimaryMainFrame();
 
   // The new page commits.
@@ -1724,7 +1724,7 @@
   EXPECT_EQ(rfh2, contents()->GetMainFrame());
   EXPECT_TRUE(contents()->GetSpeculativePrimaryMainFrame() == nullptr);
   EXPECT_TRUE(rfh1->IsPendingDeletion());
-  EXPECT_TRUE(rfh2->IsCurrent());
+  EXPECT_TRUE(rfh2->IsActive());
 
   // Simulate the unload ack.
   rfh1->OnUnloaded();
@@ -1748,7 +1748,7 @@
   // Navigate to the first page.
   contents()->NavigateAndCommit(kUrl1);
   TestRenderFrameHost* rfh1 = main_test_rfh();
-  EXPECT_TRUE(rfh1->IsCurrent());
+  EXPECT_TRUE(rfh1->IsActive());
 
   // Navigate to a new site, starting a cross-site navigation.
   controller().LoadURL(kUrl2, Referrer(), ui::PAGE_TRANSITION_LINK,
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 17a6fd15..a107b155 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -2962,6 +2962,9 @@
     SCOPED_CRASH_KEY_STRING32("RPH.BadMessageKill", "isolation_mode",
                               site_isolation_mode);
 
+    ChildProcessSecurityPolicyImpl::GetInstance()->LogKilledProcessOriginLock(
+        GetID());
+
     // Report a crash, since none will be generated by the killed renderer.
     base::debug::DumpWithoutCrashing();
   }
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index 5f4b4d0..8748d842 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -898,7 +898,7 @@
   if (!is_active())
     return {};
   RenderFrameHostImpl* rfh = static_cast<RenderFrameHostImpl*>(GetMainFrame());
-  if (!rfh || !rfh->IsCurrent())
+  if (!rfh || !rfh->IsActive())
     return {};
   FrameTreeNode* root = rfh->frame_tree_node();
   FrameTree* tree = root->frame_tree();
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index ecd1a27..499d2ce 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -2430,6 +2430,9 @@
 }
 
 void RenderWidgetHostImpl::OnInputEventAckTimeout() {
+  // Since input has timed out, let the BrowserUiThreadScheduler know we are
+  // done with input currently.
+  user_input_active_handle_.reset();
   RendererIsUnresponsive(base::BindRepeating(
       &RenderWidgetHostImpl::RestartInputEventAckTimeoutIfNecessary,
       weak_factory_.GetWeakPtr()));
@@ -3012,8 +3015,10 @@
 
 void RenderWidgetHostImpl::IncrementInFlightEventCount() {
   ++in_flight_event_count_;
-  if (in_flight_event_count_ == 1)
+  if (in_flight_event_count_ == 1) {
     power_mode_input_voter_->VoteFor(power_scheduler::PowerMode::kResponse);
+    user_input_active_handle_ = BrowserTaskExecutor::OnUserInputStart();
+  }
   if (!is_hidden_)
     StartInputEventAckTimeout();
 }
@@ -3026,6 +3031,7 @@
     StopInputEventAckTimeout();
     power_mode_input_voter_->ResetVoteAfterTimeout(
         power_scheduler::PowerModeVoter::kResponseTimeout);
+    user_input_active_handle_.reset();
   } else {
     // Only restart the hang monitor timer if we got a response from the
     // main thread.
@@ -3484,6 +3490,9 @@
 
 void RenderWidgetHostImpl::SetupInputRouter() {
   in_flight_event_count_ = 0;
+  // We are resetting in_flight_event_count_ so also inform the
+  // BrowserUIThreadScheduler that we are no longer processing input.
+  user_input_active_handle_.reset();
   suppress_events_until_keydown_ = false;
   monitoring_composition_info_ = false;
   StopInputEventAckTimeout();
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 5116122..fc32f19 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -45,6 +45,7 @@
 #include "content/browser/renderer_host/render_process_host_impl.h"
 #include "content/browser/renderer_host/render_widget_host_delegate.h"
 #include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/scheduler/browser_task_executor.h"
 #include "content/common/frame.mojom-forward.h"
 #include "content/public/browser/render_process_host_observer.h"
 #include "content/public/browser/render_widget_host.h"
@@ -1416,6 +1417,8 @@
 
   std::unique_ptr<power_scheduler::PowerModeVoter> power_mode_input_voter_;
   std::unique_ptr<power_scheduler::PowerModeVoter> power_mode_loading_voter_;
+  absl::optional<BrowserUIThreadScheduler::UserInputActiveHandle>
+      user_input_active_handle_;
 
   base::WeakPtrFactory<RenderWidgetHostImpl> weak_factory_{this};
 
diff --git a/content/browser/scheduler/browser_task_executor.cc b/content/browser/scheduler/browser_task_executor.cc
index cf85dd5..996d88dd 100644
--- a/content/browser/scheduler/browser_task_executor.cc
+++ b/content/browser/scheduler/browser_task_executor.cc
@@ -234,8 +234,18 @@
 void BrowserTaskExecutor::PostFeatureListSetup() {
   DCHECK(Get()->browser_ui_thread_handle_);
   DCHECK(Get()->browser_io_thread_handle_);
+  DCHECK(Get()->ui_thread_executor_);
   Get()->browser_ui_thread_handle_->PostFeatureListInitializationSetup();
   Get()->browser_io_thread_handle_->PostFeatureListInitializationSetup();
+  Get()->ui_thread_executor_->PostFeatureListSetup();
+}
+
+// static
+absl::optional<BrowserUIThreadScheduler::UserInputActiveHandle>
+BrowserTaskExecutor::OnUserInputStart() {
+  DCHECK(Get()->ui_thread_executor_);
+  return absl::optional<BrowserUIThreadScheduler::UserInputActiveHandle>(
+      Get()->ui_thread_executor_->OnUserInputStart());
 }
 
 // static
@@ -346,6 +356,17 @@
   base::SetTaskExecutorForCurrentThread(this);
 }
 
+absl::optional<BrowserUIThreadScheduler::UserInputActiveHandle>
+BrowserTaskExecutor::UIThreadExecutor::OnUserInputStart() {
+  DCHECK(browser_ui_thread_scheduler_);
+  return browser_ui_thread_scheduler_->OnUserInputStart();
+}
+
+void BrowserTaskExecutor::UIThreadExecutor::PostFeatureListSetup() {
+  DCHECK(browser_ui_thread_scheduler_);
+  browser_ui_thread_scheduler_->PostFeatureListSetup();
+}
+
 scoped_refptr<BrowserUIThreadScheduler::Handle>
 BrowserTaskExecutor::UIThreadExecutor::GetUIThreadHandle() {
   return browser_ui_thread_handle_;
diff --git a/content/browser/scheduler/browser_task_executor.h b/content/browser/scheduler/browser_task_executor.h
index a9bd928..8a8d02d19 100644
--- a/content/browser/scheduler/browser_task_executor.h
+++ b/content/browser/scheduler/browser_task_executor.h
@@ -152,6 +152,12 @@
   // for scheduling experiments to function.
   static void PostFeatureListSetup();
 
+  // Called when some part of the browser begins handling input. Must be called
+  // from the browser UI thread and the value must be reset once input is
+  // finished.
+  static absl::optional<BrowserUIThreadScheduler::UserInputActiveHandle>
+  OnUserInputStart();
+
   // Winds down the BrowserTaskExecutor, after this no tasks can be executed
   // and the base::TaskExecutor APIs are non-functional but won't crash if
   // called. In unittests however we need to clean up, so
@@ -193,6 +199,11 @@
 
     void BindToCurrentThread();
 
+    absl::optional<BrowserUIThreadScheduler::UserInputActiveHandle>
+    OnUserInputStart();
+
+    void PostFeatureListSetup();
+
    private:
     std::unique_ptr<BrowserUIThreadScheduler> browser_ui_thread_scheduler_;
     bool bound_to_thread_ = false;
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.cc b/content/browser/scheduler/browser_ui_thread_scheduler.cc
index b3617f92..06c795d 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.cc
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.cc
@@ -23,6 +23,73 @@
 
 namespace content {
 
+namespace features {
+// When the "BrowserPrioritizeNativeWork" feature is enabled, the main thread
+// will process native messages between each batch of application tasks for some
+// duration after an input event. The duration is controlled by the
+// "prioritize_for_next_ms" feature param. Special case: If
+// "prioritize_for_next_ms" is TimeDelta::Max(), native messages will be
+// processed between each batch of application tasks, independently from input
+// events.
+//
+// The goal is to reduce jank by processing subsequent input events sooner after
+// a first input event is received. Checking for native messages more frequently
+// incurs some overhead, but allows the browser to handle input more
+// consistently.
+constexpr base::Feature kBrowserPrioritizeNativeWork{
+    "BrowserPrioritizeNativeWork", base::FEATURE_DISABLED_BY_DEFAULT};
+constexpr base::FeatureParam<base::TimeDelta>
+    kBrowserPrioritizeNativeWorkAfterInputForNMsParam{
+        &kBrowserPrioritizeNativeWork, "prioritize_for_next_ms",
+        base::TimeDelta::Max()};
+}  // namespace features
+
+BrowserUIThreadScheduler::UserInputActiveHandle::UserInputActiveHandle(
+    BrowserUIThreadScheduler* scheduler)
+    : scheduler_(scheduler) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(scheduler_);
+  DCHECK_GE(scheduler_->user_input_active_handle_count, 0);
+
+  ++scheduler_->user_input_active_handle_count;
+  if (scheduler_->user_input_active_handle_count == 1) {
+    scheduler_->DidStartUserInput();
+  }
+}
+
+BrowserUIThreadScheduler::UserInputActiveHandle::UserInputActiveHandle(
+    UserInputActiveHandle&& other) {
+  MoveFrom(&other);
+}
+
+BrowserUIThreadScheduler::UserInputActiveHandle&
+BrowserUIThreadScheduler::UserInputActiveHandle::operator=(
+    UserInputActiveHandle&& other) {
+  MoveFrom(&other);
+  return *this;
+}
+
+void BrowserUIThreadScheduler::UserInputActiveHandle::MoveFrom(
+    UserInputActiveHandle* other) {
+  scheduler_ = other->scheduler_;
+  // Prevent the other's deconstructor from decrementing
+  // |user_input_active_handle_counter|.
+  other->scheduler_ = nullptr;
+}
+
+BrowserUIThreadScheduler::UserInputActiveHandle::~UserInputActiveHandle() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  if (!scheduler_) {
+    return;
+  }
+  DCHECK_GE(scheduler_->user_input_active_handle_count, 1);
+
+  --scheduler_->user_input_active_handle_count;
+  if (scheduler_->user_input_active_handle_count == 0) {
+    scheduler_->DidEndUserInput();
+  }
+}
+
 BrowserUIThreadScheduler::~BrowserUIThreadScheduler() = default;
 
 // static
@@ -65,4 +132,56 @@
   sequence_manager->EnableCrashKeys("ui_scheduler_async_stack");
 }
 
+BrowserUIThreadScheduler::UserInputActiveHandle
+BrowserUIThreadScheduler::OnUserInputStart() {
+  return BrowserUIThreadScheduler::UserInputActiveHandle(this);
+}
+
+void BrowserUIThreadScheduler::DidStartUserInput() {
+  if (!browser_prioritize_native_work_ ||
+      browser_prioritize_native_work_after_input_end_ms_.is_inf()) {
+    return;
+  }
+  owned_sequence_manager_->PrioritizeYieldingToNative(base::TimeTicks::Max());
+}
+
+void BrowserUIThreadScheduler::DidEndUserInput() {
+  if (!browser_prioritize_native_work_ ||
+      browser_prioritize_native_work_after_input_end_ms_.is_inf()) {
+    return;
+  }
+  owned_sequence_manager_->PrioritizeYieldingToNative(
+      base::TimeTicks::Now() +
+      browser_prioritize_native_work_after_input_end_ms_);
+}
+
+void BrowserUIThreadScheduler::PostFeatureListSetup() {
+  if (!base::FeatureList::IsEnabled(features::kBrowserPrioritizeNativeWork)) {
+    return;
+  }
+  browser_prioritize_native_work_after_input_end_ms_ =
+      features::kBrowserPrioritizeNativeWorkAfterInputForNMsParam.Get();
+  // Rather than just enable immediately we post a task at default priority.
+  // This ensures most start up work should be finished before we start using
+  // this policy.
+  //
+  // TODO(nuskos): Switch this to use ThreadControllerObserver after start up
+  // notification once available on android.
+  handle_->GetDefaultTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          [](BrowserUIThreadScheduler* scheduler) {
+            scheduler->browser_prioritize_native_work_ = true;
+            if (scheduler->browser_prioritize_native_work_after_input_end_ms_
+                    .is_inf()) {
+              // We will always prioritize yielding to native if the
+              // experiment is enabled but the delay after input is infinity.
+              // So enable it now.
+              scheduler->owned_sequence_manager_->PrioritizeYieldingToNative(
+                  base::TimeTicks::Max());
+            }
+          },
+          base::Unretained(this)));
+}
+
 }  // namespace content
diff --git a/content/browser/scheduler/browser_ui_thread_scheduler.h b/content/browser/scheduler/browser_ui_thread_scheduler.h
index 023d534..40fb1cb7 100644
--- a/content/browser/scheduler/browser_ui_thread_scheduler.h
+++ b/content/browser/scheduler/browser_ui_thread_scheduler.h
@@ -25,6 +25,24 @@
 // implement scheduling policy. This class is never deleted in production.
 class CONTENT_EXPORT BrowserUIThreadScheduler {
  public:
+  class UserInputActiveHandle {
+   public:
+    explicit UserInputActiveHandle(BrowserUIThreadScheduler* scheduler);
+    ~UserInputActiveHandle();
+
+    // This is a move only type.
+    UserInputActiveHandle(const UserInputActiveHandle&) = delete;
+    UserInputActiveHandle& operator=(const UserInputActiveHandle&) = delete;
+    UserInputActiveHandle& operator=(UserInputActiveHandle&&);
+    UserInputActiveHandle(UserInputActiveHandle&& other);
+
+   private:
+    void MoveFrom(UserInputActiveHandle* other);
+    // Only this constructor actually creates a UserInputActiveHandle that will
+    // inform scheduling decisions.
+    BrowserUIThreadScheduler* scheduler_ = nullptr;
+  };
+
   using Handle = BrowserTaskQueues::Handle;
 
   BrowserUIThreadScheduler();
@@ -49,6 +67,19 @@
   void CommonSequenceManagerSetup(
       base::sequence_manager::SequenceManager* sequence_manager);
 
+  // Called after the feature list is ready and we can set up any policy
+  // experiments.
+  void PostFeatureListSetup();
+  // Used in the BrowserPrioritizeNativeWork experiment, when we want to
+  // prioritize yielding to java when user input starts and for a short period
+  // after it ends.
+  BrowserUIThreadScheduler::UserInputActiveHandle OnUserInputStart();
+  void DidStartUserInput();
+  void DidEndUserInput();
+  // After user input has ended CancelNativePriority will be called to inform
+  // the SequenceManager to stop prioritizing yielding to native tasks.
+  void CancelNativePriority();
+
   // In production the BrowserUIThreadScheduler will own its SequenceManager,
   // but in tests it may not.
   std::unique_ptr<base::sequence_manager::SequenceManager>
@@ -57,6 +88,12 @@
   BrowserTaskQueues task_queues_;
   scoped_refptr<Handle> handle_;
 
+  // These four variables are used in the BrowserPrioritizeNativeWork finch
+  // experiment. False ensures this feature is disabled by default.
+  int user_input_active_handle_count = 0;
+  bool browser_prioritize_native_work_ = false;
+  base::TimeDelta browser_prioritize_native_work_after_input_end_ms_;
+
   DISALLOW_COPY_AND_ASSIGN(BrowserUIThreadScheduler);
 };
 
diff --git a/content/browser/service_worker/service_worker_registry_unittest.cc b/content/browser/service_worker/service_worker_registry_unittest.cc
index 8c61308..bb0eb9f 100644
--- a/content/browser/service_worker/service_worker_registry_unittest.cc
+++ b/content/browser/service_worker/service_worker_registry_unittest.cc
@@ -773,7 +773,7 @@
   EXPECT_EQ(
       blink::ServiceWorkerStatusCode::kOk,
       GetRegistrationsForStorageKey(
-          blink::StorageKey(url::Origin::Create(GURL("http://example.com/"))),
+          blink::StorageKey::CreateFromStringForTesting("http://example.com/"),
           registrations_for_storage_key));
   EXPECT_TRUE(registrations_for_storage_key.empty());
 
@@ -894,7 +894,7 @@
   EXPECT_EQ(
       blink::ServiceWorkerStatusCode::kOk,
       GetRegistrationsForStorageKey(
-          blink::StorageKey(url::Origin::Create(GURL("http://example.com/"))),
+          blink::StorageKey::CreateFromStringForTesting("http://example.com/"),
           registrations_for_storage_key));
   EXPECT_TRUE(registrations_for_storage_key.empty());
 
@@ -938,7 +938,7 @@
   EXPECT_EQ(
       blink::ServiceWorkerStatusCode::kOk,
       GetRegistrationsForStorageKey(
-          blink::StorageKey(url::Origin::Create(GURL("http://example.com/"))),
+          blink::StorageKey::CreateFromStringForTesting("http://example.com/"),
           registrations_for_storage_key));
   EXPECT_TRUE(registrations_for_storage_key.empty());
 
@@ -975,7 +975,7 @@
   EXPECT_EQ(
       blink::ServiceWorkerStatusCode::kOk,
       GetRegistrationsForStorageKey(
-          blink::StorageKey(url::Origin::Create(GURL("http://example.com/"))),
+          blink::StorageKey::CreateFromStringForTesting("http://example.com/"),
           registrations_for_storage_key));
   EXPECT_TRUE(registrations_for_storage_key.empty());
 
@@ -1933,7 +1933,7 @@
   {
     base::RunLoop loop;
     registry()->GetStorageUsageForStorageKey(
-        blink::StorageKey(url::Origin::Create(GURL("https://example.com/"))),
+        blink::StorageKey::CreateFromStringForTesting("https://example.com/"),
         base::BindLambdaForTesting(
             [&](blink::ServiceWorkerStatusCode status, int64_t usage) {
               EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kErrorFailed);
@@ -1956,7 +1956,7 @@
   base::RunLoop loop;
   registry()->StoreUserData(
       /*registration_id=*/1,
-      blink::StorageKey(url::Origin::Create(GURL("https://example.com/"))),
+      blink::StorageKey::CreateFromStringForTesting("https://example.com/"),
       {{"key", "value"}},
       base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
         EXPECT_EQ(status, blink::ServiceWorkerStatusCode::kErrorFailed);
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 0001863..f72f812 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -13078,7 +13078,7 @@
 
   // The old RFH should be pending deletion.
   EXPECT_TRUE(rfh->IsPendingDeletion());
-  EXPECT_FALSE(rfh->IsCurrent());
+  EXPECT_FALSE(rfh->IsActive());
   EXPECT_NE(rfh, web_contents()->GetMainFrame());
 
   // Check that it still has a valid last committed URL.
diff --git a/content/browser/site_per_process_unload_browsertest.cc b/content/browser/site_per_process_unload_browsertest.cc
index b83e0e5..a5e2c69 100644
--- a/content/browser/site_per_process_unload_browsertest.cc
+++ b/content/browser/site_per_process_unload_browsertest.cc
@@ -1218,7 +1218,7 @@
   EXPECT_TRUE(B2->GetSuddenTerminationDisablerState(
       blink::mojom::SuddenTerminationDisablerType::kUnloadHandler));
 
-  EXPECT_TRUE(B2->IsCurrent());
+  EXPECT_TRUE(B2->IsActive());
   EXPECT_TRUE(ExecJs(A1, "document.querySelector('iframe').remove()"));
   EXPECT_EQ(nullptr, frame_tree->GetFocusedFrame());
   EXPECT_EQ(2u, A1->child_count());
diff --git a/content/browser/speculation_rules/speculation_host_impl.cc b/content/browser/speculation_rules/speculation_host_impl.cc
index f6c0944..5a531f5 100644
--- a/content/browser/speculation_rules/speculation_host_impl.cc
+++ b/content/browser/speculation_rules/speculation_host_impl.cc
@@ -48,8 +48,8 @@
     std::vector<blink::mojom::SpeculationCandidatePtr> candidates) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  // Only handle messages from the main frame of the primary frame tree.
-  if (!render_frame_host()->IsCurrent())
+  // Only handle messages from an active main frame.
+  if (!render_frame_host()->IsActive())
     return;
   if (render_frame_host()->GetParent())
     return;
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index da7b464..5572f096 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -494,6 +494,24 @@
 bool IsWindowPlacementGranted(RenderFrameHost* host) {
   auto* controller =
       PermissionControllerImpl::FromBrowserContext(host->GetBrowserContext());
+
+  // If the frame's URL is about:blank, its origin may have been inherited.
+  // Unfortunately, many PermissionControllerDelegate implementations of
+  // `GetPermissionStatusForFrame()` use `GetLastCommittedURL()` which does not
+  // consider origin inheritance. In case the URL is about:blank, manually check
+  // the permission status ourselves by deriving a GURL from the Origin.
+  // TODO(crbug.com/698985): Resolve GetLastCommitted[URL|Origin]() usage and
+  // remove this workaround without regressing crbug.com/1210669.
+  if (host->GetLastCommittedURL().IsAboutBlank()) {
+    return controller &&
+           controller->GetPermissionStatus(
+               PermissionType::WINDOW_PLACEMENT,
+               host->GetLastCommittedOrigin().GetURL(),
+               host->GetMainFrame()->GetLastCommittedOrigin().GetURL()) ==
+               blink::mojom::PermissionStatus::GRANTED;
+  }
+
+  // TODO(crbug.com/698985): Resolve GetLastCommitted[URL|Origin]() usage.
   return controller && controller->GetPermissionStatusForFrame(
                            PermissionType::WINDOW_PLACEMENT, host,
                            host->GetLastCommittedURL()) ==
@@ -607,8 +625,8 @@
                         rfh);
   if (!rfh)
     return nullptr;
-  if (!rfh->IsCurrent() && base::FeatureList::IsEnabled(
-                               kCheckWebContentsAccessFromNonCurrentFrame)) {
+  if (!rfh->IsActive() && base::FeatureList::IsEnabled(
+                              kCheckWebContentsAccessFromNonCurrentFrame)) {
     // TODO(crbug.com/1059903): return nullptr here eventually.
     base::debug::DumpWithoutCrashing();
   }
@@ -4434,7 +4452,7 @@
   // Prevent frames that are not active (e.g. a prerendering page) from opening
   // new windows, tabs, popups, etc.
   if (params.disposition != WindowOpenDisposition::CURRENT_TAB &&
-      source_render_frame_host && !source_render_frame_host->IsCurrent()) {
+      source_render_frame_host && !source_render_frame_host->IsActive()) {
     return nullptr;
   }
 
@@ -6221,7 +6239,7 @@
   // navigation occurs while a page is still loading, the initial page
   // may stop loading and send us updated favicon URLs after the navigation
   // for the new page has committed.
-  if (!source->IsCurrent())
+  if (!source->IsActive())
     return;
 
   observers_.NotifyObservers(&WebContentsObserver::DidUpdateFaviconURL, source,
@@ -6768,7 +6786,7 @@
   javascript_dialog_dismiss_notifier_ =
       std::make_unique<JavaScriptDialogDismissNotifier>();
 
-  bool should_suppress = !render_frame_host->IsCurrent() ||
+  bool should_suppress = !render_frame_host->IsActive() ||
                          (delegate_ && delegate_->ShouldSuppressDialogs(this));
   bool has_non_devtools_handlers = delegate_ && dialog_manager_;
   bool has_handlers = page_handlers.size() || has_non_devtools_handlers;
diff --git a/content/browser/webauth/authenticator_common.cc b/content/browser/webauth/authenticator_common.cc
index 096c88f7..de397d1 100644
--- a/content/browser/webauth/authenticator_common.cc
+++ b/content/browser/webauth/authenticator_common.cc
@@ -851,7 +851,7 @@
 }
 
 bool AuthenticatorCommon::IsFocused() const {
-  return GetRenderFrameHost()->IsCurrent() &&
+  return GetRenderFrameHost()->IsActive() &&
          GetWebAuthenticationDelegate()->IsFocused(
              WebContents::FromRenderFrameHost(GetRenderFrameHost()));
 }
diff --git a/content/browser/webauth/authenticator_impl.cc b/content/browser/webauth/authenticator_impl.cc
index cef91cf..173a30c 100644
--- a/content/browser/webauth/authenticator_impl.cc
+++ b/content/browser/webauth/authenticator_impl.cc
@@ -21,9 +21,9 @@
 void AuthenticatorImpl::Create(
     RenderFrameHost* render_frame_host,
     mojo::PendingReceiver<blink::mojom::Authenticator> receiver) {
-  // Avoid creating the service if the RenderFrameHost isn't current, e.g. if a
+  // Avoid creating the service if the RenderFrameHost isn't active, e.g. if a
   // request arrives during a navigation.
-  if (!render_frame_host->IsCurrent()) {
+  if (!render_frame_host->IsActive()) {
     return;
   }
 
diff --git a/content/browser/worker_host/shared_worker_instance_unittest.cc b/content/browser/worker_host/shared_worker_instance_unittest.cc
index 4969284..24633c1 100644
--- a/content/browser/worker_host/shared_worker_instance_unittest.cc
+++ b/content/browser/worker_host/shared_worker_instance_unittest.cc
@@ -36,9 +36,9 @@
     blink::StorageKey storage_key;
     if (GURL(url).SchemeIs(url::kDataScheme)) {
       storage_key =
-          blink::StorageKey(url::Origin::Create(GURL("http://example.com/")));
+          blink::StorageKey::CreateFromStringForTesting("http://example.com/");
     } else {
-      storage_key = blink::StorageKey(url::Origin::Create(GURL(url)));
+      storage_key = blink::StorageKey::CreateFromStringForTesting(url);
     }
     return instance.Matches(GURL(url), std::string(name), storage_key);
   }
@@ -275,7 +275,7 @@
     SharedWorkerInstance instance(
         GURL("http://example.com/w.js"), blink::mojom::ScriptType::kClassic,
         network::mojom::CredentialsMode::kSameOrigin, "name",
-        blink::StorageKey(url::Origin::Create(GURL("http://example.com/"))),
+        blink::StorageKey::CreateFromStringForTesting("http://example.com/"),
         address_space,
         blink::mojom::SharedWorkerCreationContextType::kNonsecure);
     EXPECT_EQ(address_space, instance.creation_address_space());
diff --git a/content/common/debug_utils.h b/content/common/debug_utils.h
index f1b6387..8ca913b 100644
--- a/content/common/debug_utils.h
+++ b/content/common/debug_utils.h
@@ -27,9 +27,12 @@
 enum class DebugScenario {
   kDebugSameDocNavigationDocIdMismatch = 1,
 
+  // A non-main frame navigation with old_page_info set was detected.
+  kDebugNonMainFrameWithOldPageInfo = 2,
+
   // After making changes, you MUST update the histograms xml by running:
   // "python tools/metrics/histograms/update_debug_scenarios.py"
-  kMaxValue = kDebugSameDocNavigationDocIdMismatch
+  kMaxValue = kDebugNonMainFrameWithOldPageInfo
 };
 
 // The tracing categories enabled for debugging navigation scenarios can be
diff --git a/content/common/partition_alloc_support.cc b/content/common/partition_alloc_support.cc
index 0ef12d72..83907d6 100644
--- a/content/common/partition_alloc_support.cc
+++ b/content/common/partition_alloc_support.cc
@@ -51,18 +51,18 @@
 }
 
 bool EnablePCScanForMallocPartitionsIfNeeded() {
-#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && PA_ALLOW_PCSCAN
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
   DCHECK(base::FeatureList::GetInstance());
   if (base::FeatureList::IsEnabled(base::features::kPartitionAllocPCScan)) {
     base::allocator::EnablePCScan(/*dcscan*/ false);
     return true;
   }
-#endif
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
   return false;
 }
 
 bool EnablePCScanForMallocPartitionsInBrowserProcessIfNeeded() {
-#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && PA_ALLOW_PCSCAN
+#if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
   DCHECK(base::FeatureList::GetInstance());
   if (base::FeatureList::IsEnabled(
           base::features::kPartitionAllocPCScanBrowserOnly)) {
@@ -75,7 +75,7 @@
     base::allocator::EnablePCScan(dcscan_wanted);
     return true;
   }
-#endif
+#endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && defined(PA_ALLOW_PCSCAN)
   return false;
 }
 
diff --git a/content/public/browser/frame_service_base.h b/content/public/browser/frame_service_base.h
index af3218d..92646a4 100644
--- a/content/public/browser/frame_service_base.h
+++ b/content/public/browser/frame_service_base.h
@@ -67,8 +67,8 @@
   // tab-level state doesn't get queried or updated when the frame is
   // not current.
   // Use WebContents::From(render_frame_host()) instead, but please keep in mind
-  // that the render_frame_host() might not be current. See
-  // RenderFrameHost::IsCurrent for details.
+  // that the render_frame_host() might not be active. See
+  // RenderFrameHost::IsActive() for details.
   using WebContentsObserver::web_contents;
 
   // WebContentsObserver implementation.
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 973d115..9cb4c16 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -235,7 +235,7 @@
   // NOTE: The result might be different from
   // WebContents::FromRenderFrameHost(this)->GetMainFrame().
   // This function (RenderFrameHost::GetMainFrame) is the preferred API in
-  // almost all of the cases. See RenderFrameHost::IsCurrent for the details.
+  // almost all of the cases. See RenderFrameHost::IsActive for the details.
   virtual RenderFrameHost* GetMainFrame() = 0;
 
   // Returns a vector of all RenderFrameHosts in the subtree rooted at |this|.
@@ -622,33 +622,22 @@
   // new RenderFrameHosts when they reach the kPendingCommit state.
   virtual LifecycleState GetLifecycleState() = 0;
 
-  // Returns true if this RenderFrameHost is currently in the frame tree for its
-  // page. Specifically, this is when the RenderFrameHost and all of its
-  // ancestors are the current RenderFrameHost in their respective
-  // FrameTreeNodes.
+  // Returns true if the document hosted in this RenderFrameHost is committed
+  // and lives inside a page presented to the user for the WebContents it is in
+  // (e.g., not a prerendered or back-forward cached page). Only active RFHs
+  // should show UI elements (e.g., prompts, color picker, etc) to the user, so
+  // this method should be checked before showing some UI on behalf of a given
+  // RenderFrameHost (in particular, inside handlers for IPCs from a renderer
+  // process) or when crossing document/tab boundary in general, e.g., when
+  // using WebContents::FromRenderFrameHost.
   //
-  // For instance, during a navigation, if a new RenderFrameHost replaces this
-  // RenderFrameHost, IsCurrent() becomes false for this frame and its
-  // children even if the children haven't been replaced.
-  //
-  // After a RenderFrameHost has been replaced in its frame, it will either:
-  //  1) Enter the BackForwardCache.
-  //  2) Start running unload handlers and will be deleted after this ("pending
-  //  deletion").
-  // In both cases, IsCurrent() becomes false for this frame and all its
-  // children.
-  //
-  // This method should be called before trying to display some UI to the user
-  // on behalf of the given RenderFrameHost (or when crossing document / tab
-  // boundary in general, e.g. when using WebContents::FromRenderFrameHost) to
-  // check if the given RenderFrameHost is currently being displayed in a given
-  // tab.
-  //
-  // TODO(https://crbug.com/1184622): Rename IsCurrent to a more suitable name
-  // and update this comment accordingly considering the new feature Prerender2,
-  // where it is possible to have current RenderFrameHosts in multiple frame
-  // trees.
-  virtual bool IsCurrent() = 0;
+  // IsActive() is generally the same as GetLifecycleState() == kActive, except
+  // during a small window in RenderFrameHostManager::CommitPending which
+  // happens before updating the next LifecycleState of old RenderFrameHost. Due
+  // to this, IsActive() is preferred instead of using LifecycleState::kActive.
+  // TODO(crbug.com/1177198): Make IsActive and GetLifecycleState() == kActive
+  // always match.
+  virtual bool IsActive() = 0;
 
   // Returns true iff the RenderFrameHost is inactive, i.e., when the
   // RenderFrameHost is either in BackForwardCache, Prerendering, or pending
@@ -656,7 +645,7 @@
   // RenderFrameHosts can properly handle events and events processing shouldn't
   // or can't be deferred until the RenderFrameHost becomes active again.
   // Callers that only want to check whether a RenderFrameHost is active or not
-  // should use IsCurrent() instead.
+  // should use IsActive() instead.
 
   // Additionally, this method has a side effect for back-forward cache and
   // prerendering, where the document is prevented from ever becoming active
@@ -672,11 +661,10 @@
   // |IsInactiveAndDisallowActivation()| returns false along with terminating
   // the renderer process.
 
-  // Note that if |IsInactiveAndDisallowActivation()| returns true, then
-  // IsCurrent() returns false.
-  //
-  // TODO(https://crbug.com/1175866): Rename this method with a more suitable
-  // name considering all document states.
+  // The return value of IsInactiveAndDisallowActivation() is the opposite of
+  // IsActive() except in some uncommon cases:
+  // - The "small window" referred to in the IsActive() documentation.
+  // - For speculative and pending commit RenderFrameHosts, as mentioned above.
   virtual bool IsInactiveAndDisallowActivation() = 0;
 
   // Get the number of proxies to this frame, in all processes. Exposed for
diff --git a/content/public/test/render_frame_host_test_support.h b/content/public/test/render_frame_host_test_support.h
index 135d49b..4b51e23 100644
--- a/content/public/test/render_frame_host_test_support.h
+++ b/content/public/test/render_frame_host_test_support.h
@@ -12,7 +12,7 @@
 
 // Forces RenderFrameHost to be left in pending deletion, so it would not be
 // deleted. This is done to ensure that the tests have a way to reliably get a
-// RenderFrameHost which is not current (see RenderFrameHost::IsCurrent) and
+// RenderFrameHost which is inactive (see RenderFrameHost::IsActive) and
 // test that they handle it correctly.
 void LeaveInPendingDeletionState(RenderFrameHost* rfh);
 
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 543da4c..08d697c6 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -55,6 +55,7 @@
 #include "cc/trees/ukm_manager.h"
 #include "content/common/associated_interfaces.mojom.h"
 #include "content/common/content_navigation_policy.h"
+#include "content/common/debug_utils.h"
 #include "content/common/frame.mojom.h"
 #include "content/common/navigation_client.mojom.h"
 #include "content/common/navigation_gesture.h"
@@ -2530,7 +2531,8 @@
 }
 
 void RenderFrameImpl::SetOldPageLifecycleStateFromNewPageCommitIfNeeded(
-    const mojom::OldPageInfo* old_page_info) {
+    const mojom::OldPageInfo* old_page_info,
+    const GURL& new_page_url) {
   if (!old_page_info)
     return;
   RenderFrameImpl* old_main_render_frame = RenderFrameImpl::FromRoutingID(
@@ -2541,8 +2543,38 @@
     // we should check if it still exists.
     return;
   }
-  CHECK(IsMainFrame());
-  CHECK(old_main_render_frame->IsMainFrame());
+  if (!IsMainFrame() && !old_main_render_frame->IsMainFrame()) {
+    // This shouldn't happen because `old_page_info` should only be set on
+    // cross-BrowsingInstance navigations, which can only happen on main frames.
+    // However, we got some reports of this happening (see
+    // https://crbug.com/1207271).
+    SCOPED_CRASH_KEY_BOOL("old_page_info", "new_is_main_frame", IsMainFrame());
+    SCOPED_CRASH_KEY_STRING256("old_page_info", "new_url", new_page_url.spec());
+    SCOPED_CRASH_KEY_BOOL("old_page_info", "old_is_main_frame",
+                          old_main_render_frame->IsMainFrame());
+    SCOPED_CRASH_KEY_STRING256("old_page_info", "old_url",
+                               old_main_render_frame->GetLoadingUrl().spec());
+    SCOPED_CRASH_KEY_NUMBER("old_page_info", "old_routing_id",
+                            old_page_info->routing_id_for_old_main_frame);
+    SCOPED_CRASH_KEY_BOOL(
+        "old_page_info", "old_is_frozen",
+        old_page_info->new_lifecycle_state_for_old_page->is_frozen);
+    SCOPED_CRASH_KEY_BOOL("old_page_info", "old_is_in_bfcache",
+                          old_page_info->new_lifecycle_state_for_old_page
+                              ->is_in_back_forward_cache);
+    SCOPED_CRASH_KEY_BOOL(
+        "old_page_info", "old_is_hidden",
+        old_page_info->new_lifecycle_state_for_old_page->visibility ==
+            PageVisibilityState::kHidden);
+    SCOPED_CRASH_KEY_BOOL(
+        "old_page_info", "old_pagehide_dispatch",
+        old_page_info->new_lifecycle_state_for_old_page->pagehide_dispatch ==
+            blink::mojom::PagehideDispatch::kNotDispatched);
+    CaptureTraceForNavigationDebugScenario(
+        DebugScenario::kDebugNonMainFrameWithOldPageInfo);
+    NOTREACHED();
+    return;
+  }
   DCHECK_EQ(old_page_info->new_lifecycle_state_for_old_page->visibility,
             PageVisibilityState::kHidden);
   DCHECK_NE(old_page_info->new_lifecycle_state_for_old_page->pagehide_dispatch,
@@ -2578,7 +2610,7 @@
       this, kMayReplaceInitialEmptyDocument);
 
   SetOldPageLifecycleStateFromNewPageCommitIfNeeded(
-      commit_params->old_page_info.get());
+      commit_params->old_page_info.get(), common_params->url);
 
   bool was_initiated_in_this_frame =
       navigation_client_impl_ &&
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index d21bf88..9636a1b 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1021,7 +1021,8 @@
   // implemented later.
   // TODO(crbug.com/1110744): Support unload-in-commit.
   void SetOldPageLifecycleStateFromNewPageCommitIfNeeded(
-      const mojom::OldPageInfo* old_page_info);
+      const mojom::OldPageInfo* old_page_info,
+      const GURL& new_page_url);
 
   // Updates the state when asked to commit a history navigation.  Sets
   // |item_for_history_navigation| and |load_type| to the appropriate values for
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py
index 076e9323..e741aa2 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -132,12 +132,16 @@
       if os_name == 'android' or os_name == 'chromeos':
         browser_args.remove(cba.DISABLE_GPU)
 
-    # Reduce number of video buffers when running tests on Fuchsia to
-    # workaround crbug.com/1203580
-    # TODO(https://crbug.com/1203580): Remove this once the bug is resolved.
     if cls._finder_options.browser_type == 'web-engine-shell':
+      # Reduce number of video buffers when running tests on Fuchsia to
+      # workaround crbug.com/1203580
+      # TODO(https://crbug.com/1203580): Remove this once the bug is resolved.
       browser_args.append('--double-buffer-compositing')
 
+      # Increase GPU watchdog timeout to 60 seconds to avoid flake when
+      # running in emulator on bots.
+      browser_args.append('--gpu-watchdog-timeout-seconds=60')
+
     return browser_args
 
   @classmethod
diff --git a/content/test/web_contents_observer_consistency_checker.cc b/content/test/web_contents_observer_consistency_checker.cc
index be10621..be23074 100644
--- a/content/test/web_contents_observer_consistency_checker.cc
+++ b/content/test/web_contents_observer_consistency_checker.cc
@@ -130,9 +130,9 @@
     EnsureStableParentValue(old_host);
     CHECK_EQ(old_host->GetParent(), new_host->GetParent());
     GlobalRoutingID routing_pair = GetRoutingPair(old_host);
-    // If the navigation requires a new RFH, IsCurrent on old host should be
+    // If the navigation requires a new RFH, IsActive on old host should be
     // false.
-    CHECK(!old_host->IsCurrent());
+    CHECK(!old_host->IsActive());
     bool old_did_exist = !!current_hosts_.erase(routing_pair);
     if (!old_did_exist) {
       CHECK(false)
diff --git a/extensions/browser/app_window/app_window.cc b/extensions/browser/app_window/app_window.cc
index 491fd8f0..fcb2c6c5 100644
--- a/extensions/browser/app_window/app_window.cc
+++ b/extensions/browser/app_window/app_window.cc
@@ -446,11 +446,11 @@
 }
 
 bool AppWindow::ShouldShowStaleContentOnEviction(content::WebContents* source) {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if defined(OS_CHROMEOS)
   return true;
 #else
   return false;
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
+#endif  // defined(OS_CHROMEOS)
 }
 
 bool AppWindow::OnMessageReceived(const IPC::Message& message,
diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc
index c68035f..728cfcf 100644
--- a/extensions/browser/event_router.cc
+++ b/extensions/browser/event_router.cc
@@ -287,6 +287,44 @@
                            RegisteredEventType::kServiceWorker);
 }
 
+void EventRouter::AddFilteredListenerForMainThread(
+    const std::string& extension_id,
+    const std::string& event_name,
+    base::Value filter,
+    bool add_lazy_listener) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  auto* process = RenderProcessHost::FromID(receivers_.current_context());
+  if (!process)
+    return;
+
+  AddFilteredEventListener(event_name, process, extension_id, absl::nullopt,
+                           base::Value::AsDictionaryValue(filter),
+                           add_lazy_listener);
+}
+
+void EventRouter::AddFilteredListenerForServiceWorker(
+    const std::string& extension_id,
+    const GURL& worker_scope_url,
+    const std::string& event_name,
+    int64_t service_worker_version_id,
+    int32_t worker_thread_id,
+    base::Value filter,
+    bool add_lazy_listener) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  auto* process = RenderProcessHost::FromID(receivers_.current_context());
+  if (!process)
+    return;
+
+  ServiceWorkerIdentifier sw_identifier;
+  sw_identifier.scope = worker_scope_url;
+  sw_identifier.thread_id = worker_thread_id;
+  sw_identifier.version_id = service_worker_version_id;
+
+  AddFilteredEventListener(event_name, process, extension_id, sw_identifier,
+                           base::Value::AsDictionaryValue(filter),
+                           add_lazy_listener);
+}
+
 void EventRouter::RemoveListenerForMainThread(
     mojom::EventListenerParamPtr param,
     const std::string& event_name) {
diff --git a/extensions/browser/event_router.h b/extensions/browser/event_router.h
index a813648..0375138 100644
--- a/extensions/browser/event_router.h
+++ b/extensions/browser/event_router.h
@@ -161,6 +161,19 @@
                                        const GURL& worker_scope_url,
                                        const std::string& name) override;
 
+  void AddFilteredListenerForMainThread(const std::string& extension_id,
+                                        const std::string& name,
+                                        base::Value filter,
+                                        bool add_lazy_listener) override;
+
+  void AddFilteredListenerForServiceWorker(const std::string& extension_id,
+                                           const GURL& worker_scope_url,
+                                           const std::string& name,
+                                           int64_t service_worker_version_id,
+                                           int32_t worker_thread_id,
+                                           base::Value filter,
+                                           bool add_lazy_listener) override;
+
   void RemoveListenerForMainThread(mojom::EventListenerParamPtr param,
                                    const std::string& name) override;
 
diff --git a/extensions/browser/extension_message_filter.cc b/extensions/browser/extension_message_filter.cc
index d27bed1..2e4527cd 100644
--- a/extensions/browser/extension_message_filter.cc
+++ b/extensions/browser/extension_message_filter.cc
@@ -71,30 +71,30 @@
 }
 
 // Returns true if `source_endpoint` can be legitimately claimed/used by
-// `process`.  Otherwise reports a bad IPC message for `filter` and returns
-// false (expecting the caller to not take any action based on the rejected,
-// untrustworthy `source_endpoint`).
-bool IsValidMessagingSource(ExtensionMessageFilter* filter,
-                            RenderProcessHost& process,
+// `process`.  Otherwise reports a bad IPC message and returns false (expecting
+// the caller to not take any action based on the rejected, untrustworthy
+// `source_endpoint`).
+bool IsValidMessagingSource(RenderProcessHost& process,
                             const MessagingEndpoint& source_endpoint) {
   switch (source_endpoint.type) {
     case MessagingEndpoint::Type::kNativeApp:
       // Requests for channels initiated by native applications don't originate
       // from renderer processes.
       bad_message::ReceivedBadMessage(
-          filter, bad_message::EMF_INVALID_CHANNEL_SOURCE_TYPE);
+          &process, bad_message::EMF_INVALID_CHANNEL_SOURCE_TYPE);
       return false;
 
     case MessagingEndpoint::Type::kExtension:
       if (!source_endpoint.extension_id.has_value()) {
         bad_message::ReceivedBadMessage(
-            filter, bad_message::EMF_NO_EXTENSION_ID_FOR_EXTENSION_SOURCE);
+            &process, bad_message::EMF_NO_EXTENSION_ID_FOR_EXTENSION_SOURCE);
         return false;
       }
       if (!CanRendererHostExtensionOrigin(
               process.GetID(), source_endpoint.extension_id.value())) {
         bad_message::ReceivedBadMessage(
-            filter, bad_message::EMF_INVALID_EXTENSION_ID_FOR_EXTENSION_SOURCE);
+            &process,
+            bad_message::EMF_INVALID_EXTENSION_ID_FOR_EXTENSION_SOURCE);
         return false;
       }
       return true;
@@ -108,11 +108,10 @@
 }
 
 // Returns true if `source_context` can be legitimately claimed/used by
-// `render_process_id`.  Otherwise reports a bad IPC message for `filter` and
-// returns false (expecting the caller to not take any action based on the
-// rejected, untrustworthy `source_context`).
-bool IsValidSourceContext(ExtensionMessageFilter* filter,
-                          int render_process_id,
+// `render_process_id`.  Otherwise reports a bad IPC message and returns false
+// (expecting the caller to not take any action based on the rejected,
+// untrustworthy `source_context`).
+bool IsValidSourceContext(RenderProcessHost& process,
                           const PortContext& source_context) {
   if (source_context.is_for_service_worker()) {
     const PortContext::WorkerContext& worker_context =
@@ -123,10 +122,10 @@
     // exists using ProcessManager::HasServiceWorker) might incorrectly return
     // false=invalid-IPC for IPCs from workers that were recently torn down /
     // made inactive.
-    if (!CanRendererHostExtensionOrigin(render_process_id,
+    if (!CanRendererHostExtensionOrigin(process.GetID(),
                                         worker_context.extension_id)) {
       bad_message::ReceivedBadMessage(
-          filter, bad_message::EMF_INVALID_EXTENSION_ID_FOR_WORKER_CONTEXT);
+          &process, bad_message::EMF_INVALID_EXTENSION_ID_FOR_WORKER_CONTEXT);
       return false;
     }
   }
@@ -134,6 +133,50 @@
   return true;
 }
 
+base::debug::CrashKeyString* GetTargetIdCrashKey() {
+  static auto* crash_key = base::debug::AllocateCrashKeyString(
+      "ExternalConnectionInfo::target_id", base::debug::CrashKeySize::Size64);
+  return crash_key;
+}
+
+base::debug::CrashKeyString* GetSourceOriginCrashKey() {
+  static auto* crash_key = base::debug::AllocateCrashKeyString(
+      "ExternalConnectionInfo::source_origin",
+      base::debug::CrashKeySize::Size256);
+  return crash_key;
+}
+
+base::debug::CrashKeyString* GetSourceUrlCrashKey() {
+  static auto* crash_key = base::debug::AllocateCrashKeyString(
+      "ExternalConnectionInfo::source_url", base::debug::CrashKeySize::Size256);
+  return crash_key;
+}
+
+class ScopedExternalConnectionInfoCrashKeys {
+ public:
+  explicit ScopedExternalConnectionInfoCrashKeys(
+      const ExtensionMsg_ExternalConnectionInfo& info)
+      : target_id_(GetTargetIdCrashKey(), info.target_id),
+        source_endpoint_(info.source_endpoint),
+        source_origin_(GetSourceOriginCrashKey(),
+                       base::OptionalOrNullptr(info.source_origin)),
+        source_url_(GetSourceUrlCrashKey(),
+                    info.source_url.possibly_invalid_spec()) {}
+
+  ~ScopedExternalConnectionInfoCrashKeys() = default;
+
+  ScopedExternalConnectionInfoCrashKeys(
+      const ScopedExternalConnectionInfoCrashKeys&) = delete;
+  ScopedExternalConnectionInfoCrashKeys& operator=(
+      const ScopedExternalConnectionInfoCrashKeys&) = delete;
+
+ private:
+  base::debug::ScopedCrashKeyString target_id_;
+  extensions::debug::ScopedMessagingEndpointCrashKeys source_endpoint_;
+  url::debug::ScopedOriginCrashKey source_origin_;
+  base::debug::ScopedCrashKeyString source_url_;
+};
+
 }  // namespace
 
 ExtensionMessageFilter::ExtensionMessageFilter(int render_process_id,
@@ -170,7 +213,6 @@
     const IPC::Message& message,
     BrowserThread::ID* thread) {
   switch (message.type()) {
-    case ExtensionHostMsg_AddFilteredListener::ID:
     case ExtensionHostMsg_RemoveFilteredListener::ID:
     case ExtensionHostMsg_WakeEventPage::ID:
     case ExtensionHostMsg_OpenChannelToExtension::ID:
@@ -193,8 +235,6 @@
 bool ExtensionMessageFilter::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(ExtensionMessageFilter, message)
-    IPC_MESSAGE_HANDLER(ExtensionHostMsg_AddFilteredListener,
-                        OnExtensionAddFilteredListener)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_RemoveFilteredListener,
                         OnExtensionRemoveFilteredListener)
     IPC_MESSAGE_HANDLER(ExtensionHostMsg_WakeEventPage,
@@ -212,24 +252,6 @@
   return handled;
 }
 
-void ExtensionMessageFilter::OnExtensionAddFilteredListener(
-    const std::string& extension_id,
-    const std::string& event_name,
-    absl::optional<ServiceWorkerIdentifier> sw_identifier,
-    const base::DictionaryValue& filter,
-    bool lazy) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (!browser_context_)
-    return;
-
-  RenderProcessHost* process = RenderProcessHost::FromID(render_process_id_);
-  if (!process)
-    return;
-
-  GetEventRouter()->AddFilteredEventListener(event_name, process, extension_id,
-                                             sw_identifier, filter, lazy);
-}
-
 void ExtensionMessageFilter::OnExtensionRemoveFilteredListener(
     const std::string& extension_id,
     const std::string& event_name,
@@ -309,8 +331,9 @@
   if (!process)
     return;
 
-  if (!IsValidMessagingSource(this, *process, info.source_endpoint) ||
-      !IsValidSourceContext(this, render_process_id_, source_context)) {
+  ScopedExternalConnectionInfoCrashKeys info_crash_keys(info);
+  if (!IsValidMessagingSource(*process, info.source_endpoint) ||
+      !IsValidSourceContext(*process, source_context)) {
     return;
   }
 
diff --git a/extensions/browser/extension_message_filter.h b/extensions/browser/extension_message_filter.h
index f1fdfd60..2692366 100644
--- a/extensions/browser/extension_message_filter.h
+++ b/extensions/browser/extension_message_filter.h
@@ -63,12 +63,6 @@
   bool OnMessageReceived(const IPC::Message& message) override;
 
   // Message handlers on the UI thread.
-  void OnExtensionAddFilteredListener(
-      const std::string& extension_id,
-      const std::string& event_name,
-      absl::optional<ServiceWorkerIdentifier> sw_identifier,
-      const base::DictionaryValue& filter,
-      bool lazy);
   void OnExtensionRemoveFilteredListener(
       const std::string& extension_id,
       const std::string& event_name,
diff --git a/extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.cc b/extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.cc
index c13559fe..f13384f 100644
--- a/extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.cc
+++ b/extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.cc
@@ -194,7 +194,7 @@
   // picked, a specualtive RenderFrameHost might be deleted. Do not abort the
   // stream in that case.
   if (frame_tree_node_id_ != content::RenderFrameHost::kNoFrameTreeNodeId &&
-      !render_frame_host->IsCurrent())
+      !render_frame_host->IsActive())
     return;
 
   AbortStream();
diff --git a/extensions/common/api/_behavior_features.json b/extensions/common/api/_behavior_features.json
index a6675f8..c37b29c 100644
--- a/extensions/common/api/_behavior_features.json
+++ b/extensions/common/api/_behavior_features.json
@@ -38,8 +38,7 @@
     "allowlist": [
       "05D1DBD6E8B9C4690FFA7D50E6F60C5290DC662A"   // crbug.com/941107
     ]
-  },
-  {
+  }, {
     // Strict restrictions for stable channel.
     // For now only allowlisted apps.
     "channel": "stable",
@@ -54,6 +53,17 @@
       "6B748A5C005F21B7CBCF4170C2F883E435DEB511"   // CSSI Smart Card Middleware
     ]
   }, {
+    // https://crbug.com/1194693
+    "channel": "stable",
+    "component_extensions_auto_granted": false,
+    "extension_types": ["login_screen_extension"],
+    "location": "policy",
+    "platforms": ["chromeos"],
+    "allowlist": [
+      "6B748A5C005F21B7CBCF4170C2F883E435DEB511",   // CSSI Smart Card Middleware
+      "075FF17D52ED6E3C2E5EC4D99F188E7A25AF47EA"    // Beta CSSI Smart Card Middleware
+    ]
+  }, {
     // Explicitly allowlist extensions that should always be available
     // on a sign-in screen. Mind that some of them will be closed when signin is
     // performed. See for reference |kNonRiskyExtensionsIdsHashes| in
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index 4f8ce754..d9e0ea31 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -548,6 +548,18 @@
       "extension_types": ["extension", "legacy_packaged_app", "platform_app"]
     },
     {
+      // https://crbug.com/1194693
+      "channel": "stable",
+      "component_extensions_auto_granted": false,
+      "extension_types": ["login_screen_extension"],
+      "location": "policy",
+      "platforms": ["chromeos"],
+      "allowlist": [
+        "6B748A5C005F21B7CBCF4170C2F883E435DEB511",   // CSSI Smart Card Middleware
+        "075FF17D52ED6E3C2E5EC4D99F188E7A25AF47EA"    // Beta CSSI Smart Card Middleware
+      ]
+    },
+    {
       "channel": "stable",
       "dependencies": ["behavior:imprivata_login_screen_extension"],
       "extension_types": ["login_screen_extension"]
diff --git a/extensions/common/api/messaging/messaging_endpoint.cc b/extensions/common/api/messaging/messaging_endpoint.cc
index 173ee634..e81aa92 100644
--- a/extensions/common/api/messaging/messaging_endpoint.cc
+++ b/extensions/common/api/messaging/messaging_endpoint.cc
@@ -6,8 +6,68 @@
 
 #include <utility>
 
+#include "base/notreached.h"
+#include "base/strings/string_number_conversions.h"
+
 namespace extensions {
 
+namespace {
+
+base::debug::CrashKeyString* GetMessagingSourceTypeCrashKey() {
+  static auto* crash_key = base::debug::AllocateCrashKeyString(
+      "MessagingSource::type", base::debug::CrashKeySize::Size32);
+  return crash_key;
+}
+
+base::debug::CrashKeyString* GetMessagingSourceExtensionIdCrashKey() {
+  static auto* crash_key = base::debug::AllocateCrashKeyString(
+      "MessagingSource::extension_id", base::debug::CrashKeySize::Size64);
+  return crash_key;
+}
+
+base::debug::CrashKeyString* GetMessagingSourceNativeAppNameCrashKey() {
+  static auto* crash_key = base::debug::AllocateCrashKeyString(
+      "MessagingSource::native_app_name", base::debug::CrashKeySize::Size64);
+  return crash_key;
+}
+
+const char* ConvertMessagingSourceTypeToString(
+    const MessagingEndpoint::Type& type) {
+  switch (type) {
+    case MessagingEndpoint::Type::kExtension:
+      return "Extension";
+    case MessagingEndpoint::Type::kTab:
+      return "Tab";
+    case MessagingEndpoint::Type::kNativeApp:
+      return "NativeApp";
+  }
+  NOTREACHED();
+  return "<unrecognized enum value>";
+}
+
+base::debug::ScopedCrashKeyString CreateExtensionIdOrNativeAppNameScopedKey(
+    const MessagingEndpoint& endpoint) {
+  switch (endpoint.type) {
+    case MessagingEndpoint::Type::kExtension:
+    case MessagingEndpoint::Type::kTab:
+      return base::debug::ScopedCrashKeyString(
+          GetMessagingSourceExtensionIdCrashKey(),
+          endpoint.extension_id.value_or("<base::nullopt>"));
+
+    case MessagingEndpoint::Type::kNativeApp:
+      return base::debug::ScopedCrashKeyString(
+          GetMessagingSourceNativeAppNameCrashKey(),
+          endpoint.native_app_name.value_or("<base::nullopt>"));
+  }
+
+  NOTREACHED();
+  return base::debug::ScopedCrashKeyString(
+      GetMessagingSourceExtensionIdCrashKey(),
+      endpoint.extension_id.value_or("<unrecognized MessagingEndpoint::Type>"));
+}
+
+}  // namespace
+
 // static
 MessagingEndpoint MessagingEndpoint::ForExtension(ExtensionId extension_id) {
   MessagingEndpoint messaging_endpoint;
@@ -51,4 +111,17 @@
 
 MessagingEndpoint::~MessagingEndpoint() = default;
 
+namespace debug {
+
+ScopedMessagingEndpointCrashKeys::ScopedMessagingEndpointCrashKeys(
+    const MessagingEndpoint& endpoint)
+    : type_(GetMessagingSourceTypeCrashKey(),
+            ConvertMessagingSourceTypeToString(endpoint.type)),
+      extension_id_or_app_name_(
+          CreateExtensionIdOrNativeAppNameScopedKey(endpoint)) {}
+
+ScopedMessagingEndpointCrashKeys::~ScopedMessagingEndpointCrashKeys() = default;
+
+}  // namespace debug
+
 }  // namespace extensions
diff --git a/extensions/common/api/messaging/messaging_endpoint.h b/extensions/common/api/messaging/messaging_endpoint.h
index ad35d80..060b303 100644
--- a/extensions/common/api/messaging/messaging_endpoint.h
+++ b/extensions/common/api/messaging/messaging_endpoint.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/debug/crash_logging.h"
 #include "extensions/common/extension_id.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -52,6 +53,25 @@
   absl::optional<std::string> native_app_name;
 };
 
+namespace debug {
+
+class ScopedMessagingEndpointCrashKeys {
+ public:
+  explicit ScopedMessagingEndpointCrashKeys(const MessagingEndpoint& endpoint);
+  ~ScopedMessagingEndpointCrashKeys();
+
+  ScopedMessagingEndpointCrashKeys(const ScopedMessagingEndpointCrashKeys&) =
+      delete;
+  ScopedMessagingEndpointCrashKeys& operator=(
+      const ScopedMessagingEndpointCrashKeys&) = delete;
+
+ private:
+  base::debug::ScopedCrashKeyString type_;
+  base::debug::ScopedCrashKeyString extension_id_or_app_name_;
+};
+
+}  // namespace debug
+
 }  // namespace extensions
 
 #endif  // EXTENSIONS_COMMON_API_MESSAGING_MESSAGING_ENDPOINT_H_
diff --git a/extensions/common/extension_messages.h b/extensions/common/extension_messages.h
index ebd7829..15e96f1a 100644
--- a/extensions/common/extension_messages.h
+++ b/extensions/common/extension_messages.h
@@ -360,18 +360,6 @@
 
 // Messages sent from the renderer to the browser:
 
-// Notify the browser that the given extension added a listener to instances of
-// the named event that satisfy the filter.
-// If |sw_identifier| is specified, it implies that the listener is for a
-// service worker, and the param is used to identify the worker.
-IPC_MESSAGE_CONTROL5(
-    ExtensionHostMsg_AddFilteredListener,
-    std::string /* extension_id */,
-    std::string /* name */,
-    absl::optional<ServiceWorkerIdentifier> /* sw_identifier */,
-    base::DictionaryValue /* filter */,
-    bool /* lazy */)
-
 // Notify the browser that the given extension is no longer interested in
 // instances of the named event that satisfy the filter.
 // If |sw_identifier| is specified, it implies that the listener is for a
diff --git a/extensions/common/mojom/event_router.mojom b/extensions/common/mojom/event_router.mojom
index e7f1399..c5ea2cb 100644
--- a/extensions/common/mojom/event_router.mojom
+++ b/extensions/common/mojom/event_router.mojom
@@ -4,6 +4,7 @@
 
 module extensions.mojom;
 
+import "mojo/public/mojom/base/values.mojom";
 import "url/mojom/url.mojom";
 
 union EventListenerParam {
@@ -40,6 +41,22 @@
                                   url.mojom.Url worker_scope_url,
                                   string event_name);
 
+  // Notifies the browser that the given extension added a listener to instances
+  // of the named event that satisfy the filter.
+  AddFilteredListenerForMainThread(string extension_id,
+                                   string event_name,
+                                   mojo_base.mojom.DictionaryValue filter,
+                                   bool add_lazy_listener);
+
+  // Notifies the browser that the given extension added a listener for a
+  // service worker to instances of the named event that satisfy the filter.
+  AddFilteredListenerForServiceWorker(string extension_id,
+                                      url.mojom.Url worker_scope_url,
+                                      string event_name,
+                                      int64 service_worker_version_id,
+                                      int32 worker_thread_id,
+                                      mojo_base.mojom.DictionaryValue filter,
+                                      bool add_lazy_listener);
 
   // Notifies the browser that the given |extension_id| or |listener_url|
   // removed a listener to an event. If it has |extension_id| in |param|, the
diff --git a/extensions/renderer/ipc_message_sender.cc b/extensions/renderer/ipc_message_sender.cc
index 13e592f..0241b12 100644
--- a/extensions/renderer/ipc_message_sender.cc
+++ b/extensions/renderer/ipc_message_sender.cc
@@ -115,8 +115,8 @@
     DCHECK(!context->IsForServiceWorker());
     DCHECK_EQ(kMainThreadId, content::WorkerThread::GetCurrentId());
 
-    render_thread_->Send(new ExtensionHostMsg_AddFilteredListener(
-        context->GetExtensionID(), event_name, absl::nullopt, filter, is_lazy));
+    GetEventRouter()->AddFilteredListenerForMainThread(
+        context->GetExtensionID(), event_name, filter.Clone(), is_lazy);
   }
 
   void SendRemoveFilteredEventListenerIPC(ScriptContext* context,
@@ -314,12 +314,11 @@
     DCHECK_NE(kMainThreadId, content::WorkerThread::GetCurrentId());
     DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId,
               context->service_worker_version_id());
-    ServiceWorkerIdentifier sw_identifier;
-    sw_identifier.scope = context->service_worker_scope();
-    sw_identifier.thread_id = content::WorkerThread::GetCurrentId();
-    sw_identifier.version_id = context->service_worker_version_id();
-    dispatcher_->Send(new ExtensionHostMsg_AddFilteredListener(
-        context->GetExtensionID(), event_name, sw_identifier, filter, is_lazy));
+
+    dispatcher_->SendAddEventFilteredListener(
+        context->GetExtensionID(), context->service_worker_scope(), event_name,
+        context->service_worker_version_id(),
+        content::WorkerThread::GetCurrentId(), filter.Clone(), is_lazy);
   }
 
   void SendRemoveFilteredEventListenerIPC(ScriptContext* context,
diff --git a/extensions/renderer/worker_thread_dispatcher.cc b/extensions/renderer/worker_thread_dispatcher.cc
index 60a116f..1c17b70 100644
--- a/extensions/renderer/worker_thread_dispatcher.cc
+++ b/extensions/renderer/worker_thread_dispatcher.cc
@@ -88,6 +88,21 @@
       extension_id, scope, event_name);
 }
 
+// Calls mojom::EventRouter::AddFilteredListenerForServiceWorker(). It should be
+// called on the IO thread.
+void AddEventFilteredListenerOnIO(const std::string& extension_id,
+                                  const GURL& scope,
+                                  const std::string& event_name,
+                                  int64_t service_worker_version_id,
+                                  int worker_thread_id,
+                                  base::Value filter,
+                                  bool add_lazy_listener) {
+  auto* dispatcher = WorkerThreadDispatcher::Get();
+  dispatcher->GetEventRouterOnIO()->AddFilteredListenerForServiceWorker(
+      extension_id, scope, event_name, service_worker_version_id,
+      worker_thread_id, std::move(filter), add_lazy_listener);
+}
+
 }  // namespace
 
 WorkerThreadDispatcher::WorkerThreadDispatcher() {}
@@ -208,6 +223,21 @@
                                 event_name));
 }
 
+void WorkerThreadDispatcher::SendAddEventFilteredListener(
+    const std::string& extension_id,
+    const GURL& scope,
+    const std::string& event_name,
+    int64_t service_worker_version_id,
+    int worker_thread_id,
+    base::Value filter,
+    bool add_lazy_listener) {
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::BindOnce(&AddEventFilteredListenerOnIO, extension_id, scope,
+                     event_name, service_worker_version_id, worker_thread_id,
+                     std::move(filter), add_lazy_listener));
+}
+
 void WorkerThreadDispatcher::SendRemoveEventListener(
     const std::string& extension_id,
     const GURL& scope,
diff --git a/extensions/renderer/worker_thread_dispatcher.h b/extensions/renderer/worker_thread_dispatcher.h
index 412034f..54dcb87c 100644
--- a/extensions/renderer/worker_thread_dispatcher.h
+++ b/extensions/renderer/worker_thread_dispatcher.h
@@ -107,6 +107,16 @@
                                 const GURL& scope,
                                 const std::string& event_name);
 
+  // Posts mojom::EventRouter::AddFilteredListenerForServiceWorker to the IO
+  // thread to call it with GetEventRouterOnIO().
+  void SendAddEventFilteredListener(const std::string& extension_id,
+                                    const GURL& scope,
+                                    const std::string& event_name,
+                                    int64_t service_worker_version_id,
+                                    int worker_thread_id,
+                                    base::Value filter,
+                                    bool add_lazy_listener);
+
   // Posts mojom::EventRouter::RemoveListenerForServiceWorker to the IO thread
   // to call it with GetEventRouterOnIO().
   void SendRemoveEventListener(const std::string& extension_id,
diff --git a/fuchsia/runners/BUILD.gn b/fuchsia/runners/BUILD.gn
index befa967..3b89746c 100644
--- a/fuchsia/runners/BUILD.gn
+++ b/fuchsia/runners/BUILD.gn
@@ -138,7 +138,6 @@
   "lib/VkLayer_image_pipe_swapchain.so",
   "lib/libEGL.so",
   "lib/libGLESv2.so",
-  "lib/libfuchsia_egl.so",
   "lib/libtrace-engine.so",
   "lib/libvulkan.so",
   "ui/webui/resources/js/load_time_data.js",
@@ -148,6 +147,11 @@
   "lib/libswiftshader_libGLESv2.so",
 ]
 
+# This file is still required for component and coverage builds.
+if (!is_component_build && !fuchsia_code_coverage) {
+  _web_instance_host_deps_files_to_exclude += [ "lib/libfuchsia_egl.so" ]
+}
+
 cr_fuchsia_package("cast_runner_pkg") {
   binary = ":cast_runner_exe"
   package_name_override = "cast_runner"
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 061eece..5c56253 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -24520,7 +24520,7 @@
       }
       experiments {
         key: "luci.buildbucket.use_bbagent"
-        value: 50
+        value: 100
       }
       experiments {
         key: "luci.use_realms"
@@ -35176,6 +35176,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -35235,6 +35239,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -35294,6 +35302,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -35353,6 +35365,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -35412,6 +35428,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -35475,6 +35495,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37372,6 +37396,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37441,6 +37469,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37510,6 +37542,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37579,6 +37615,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37648,6 +37688,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37717,6 +37761,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37786,6 +37834,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37855,6 +37907,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37924,6 +37980,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -37993,6 +38053,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38062,6 +38126,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38130,6 +38198,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38200,6 +38272,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38270,6 +38346,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38339,6 +38419,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38408,6 +38492,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38477,6 +38565,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38546,6 +38638,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38615,6 +38711,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38684,6 +38784,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38753,6 +38857,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38823,6 +38931,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38893,6 +39005,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -38961,6 +39077,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39030,6 +39150,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39099,6 +39223,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39168,6 +39296,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39237,6 +39369,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39306,6 +39442,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39375,6 +39515,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39444,6 +39588,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39513,6 +39661,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39582,6 +39734,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39651,6 +39807,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39720,6 +39880,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39789,6 +39953,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39858,6 +40026,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39927,6 +40099,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -39996,6 +40172,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40064,6 +40244,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40132,6 +40316,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40200,6 +40388,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40268,6 +40460,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40336,6 +40532,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40404,6 +40604,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40473,6 +40677,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40542,6 +40750,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40611,6 +40823,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40680,6 +40896,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40749,6 +40969,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40817,6 +41041,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40886,6 +41114,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -40955,6 +41187,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41023,6 +41259,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41092,6 +41332,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41161,6 +41405,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41229,6 +41477,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41298,6 +41550,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41363,6 +41619,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41433,6 +41693,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41501,6 +41765,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41570,6 +41838,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41638,6 +41910,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41707,6 +41983,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41776,6 +42056,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41845,6 +42129,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41913,6 +42201,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -41982,6 +42274,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42050,6 +42346,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42119,6 +42419,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42187,6 +42491,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42256,6 +42564,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42325,6 +42637,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42396,6 +42712,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42463,6 +42783,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42534,6 +42858,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42605,6 +42933,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42676,6 +43008,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42747,6 +43083,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42818,6 +43158,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42886,6 +43230,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -42954,6 +43302,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43023,6 +43375,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43092,6 +43448,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43161,6 +43521,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43230,6 +43594,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43299,6 +43667,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43368,6 +43740,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43437,6 +43813,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43506,6 +43886,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43575,6 +43959,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43643,6 +44031,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43711,6 +44103,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43779,6 +44175,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43847,6 +44247,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43912,6 +44316,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -43977,6 +44385,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44042,6 +44454,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44107,6 +44523,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44172,6 +44592,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44237,6 +44661,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44302,6 +44730,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44367,6 +44799,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44432,6 +44868,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44497,6 +44937,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44562,6 +45006,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44627,6 +45075,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44692,6 +45144,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44757,6 +45213,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44822,6 +45282,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44887,6 +45351,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -44952,6 +45420,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45017,6 +45489,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45082,6 +45558,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45147,6 +45627,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45212,6 +45696,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45277,6 +45765,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45342,6 +45834,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45407,6 +45903,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45472,6 +45972,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45537,6 +46041,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45602,6 +46110,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45667,6 +46179,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45732,6 +46248,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45797,6 +46317,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45865,6 +46389,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -45933,6 +46461,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46001,6 +46533,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46069,6 +46605,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46137,6 +46677,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46205,6 +46749,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46273,6 +46821,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46341,6 +46893,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46409,6 +46965,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46477,6 +47037,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46545,6 +47109,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46613,6 +47181,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46681,6 +47253,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46750,6 +47326,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46819,6 +47399,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46888,6 +47472,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -46957,6 +47545,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47026,6 +47618,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47095,6 +47691,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47164,6 +47764,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47233,6 +47837,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47302,6 +47910,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47371,6 +47983,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47440,6 +48056,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47509,6 +48129,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47578,6 +48202,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47643,6 +48271,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47708,6 +48340,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47773,6 +48409,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47841,6 +48481,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47909,6 +48553,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -47978,6 +48626,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48051,6 +48703,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48127,6 +48783,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48204,6 +48864,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48280,6 +48944,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48357,6 +49025,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48433,6 +49105,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48509,6 +49185,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48585,6 +49265,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48662,6 +49346,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48738,6 +49426,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48814,6 +49506,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48890,6 +49586,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -48966,6 +49666,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49034,6 +49738,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49103,6 +49811,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49172,6 +49884,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49242,6 +49958,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49310,6 +50030,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49378,6 +50102,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49446,6 +50174,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49515,6 +50247,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49584,6 +50320,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49653,6 +50393,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49722,6 +50466,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49791,6 +50539,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49860,6 +50612,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49929,6 +50685,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -49998,6 +50758,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50067,6 +50831,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50136,6 +50904,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50205,6 +50977,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50273,6 +51049,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50342,6 +51122,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50411,6 +51195,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50481,6 +51269,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50550,6 +51342,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50619,6 +51415,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50688,6 +51488,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50755,6 +51559,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50824,6 +51632,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50893,6 +51705,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -50962,6 +51778,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51030,6 +51850,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51099,6 +51923,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51168,6 +51996,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51237,6 +52069,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51306,6 +52142,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51375,6 +52215,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51443,6 +52287,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51512,6 +52360,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51581,6 +52433,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51650,6 +52506,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51718,6 +52578,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51787,6 +52651,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51856,6 +52724,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51926,6 +52798,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -51996,6 +52872,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52066,6 +52946,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52135,6 +53019,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52201,6 +53089,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52267,6 +53159,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52333,6 +53229,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52399,6 +53299,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52468,6 +53372,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52537,6 +53445,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52606,6 +53518,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52675,6 +53591,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52744,6 +53664,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52813,6 +53737,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52882,6 +53810,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -52951,6 +53883,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53019,6 +53955,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53088,6 +54028,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53157,6 +54101,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53226,6 +54174,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53295,6 +54247,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53364,6 +54320,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53433,6 +54393,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53502,6 +54466,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53571,6 +54539,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53640,6 +54612,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53712,6 +54688,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53781,6 +54761,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53854,6 +54838,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53923,6 +54911,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -53991,6 +54983,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54059,6 +55055,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54128,6 +55128,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54197,6 +55201,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54266,6 +55274,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54335,6 +55347,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54404,6 +55420,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54472,6 +55492,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54540,6 +55564,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54609,6 +55637,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54680,6 +55712,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54751,6 +55787,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54823,6 +55863,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54894,6 +55938,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -54966,6 +56014,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55037,6 +56089,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55109,6 +56165,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55182,6 +56242,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55255,6 +56319,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55323,6 +56391,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55394,6 +56466,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55465,6 +56541,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55537,6 +56617,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55609,6 +56693,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55681,6 +56769,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55753,6 +56845,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55825,6 +56921,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55897,6 +56997,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -55969,6 +57073,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56041,6 +57149,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56113,6 +57225,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56185,6 +57301,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56257,6 +57377,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56329,6 +57453,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56401,6 +57529,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56473,6 +57605,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56545,6 +57681,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56617,6 +57757,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56688,6 +57832,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56759,6 +57907,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56830,6 +57982,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56899,6 +58055,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -56968,6 +58128,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57037,6 +58201,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57106,6 +58274,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57175,6 +58347,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57247,6 +58423,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57319,6 +58499,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57391,6 +58575,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57463,6 +58651,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57536,6 +58728,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57609,6 +58805,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57686,6 +58886,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57757,6 +58961,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57829,6 +59037,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57902,6 +59114,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -57972,6 +59188,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58042,6 +59262,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58112,6 +59336,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58182,6 +59410,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58252,6 +59484,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58322,6 +59558,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58392,6 +59632,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58465,6 +59709,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58538,6 +59786,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58611,6 +59863,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58684,6 +59940,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58756,6 +60016,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58829,6 +60093,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58903,6 +60171,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -58975,6 +60247,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59048,6 +60324,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59121,6 +60401,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59194,6 +60478,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59267,6 +60555,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59340,6 +60632,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59413,6 +60709,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59486,6 +60786,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59559,6 +60863,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59632,6 +60940,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59705,6 +61017,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59778,6 +61094,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59849,6 +61169,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
@@ -59922,6 +61246,10 @@
         value: 100
       }
       experiments {
+        key: "luci.buildbucket.use_bbagent"
+        value: 10
+      }
+      experiments {
         key: "luci.use_realms"
         value: 100
       }
diff --git a/infra/config/lib/try.star b/infra/config/lib/try.star
index dd935fa2..6d6684ba 100644
--- a/infra/config/lib/try.star
+++ b/infra/config/lib/try.star
@@ -118,6 +118,9 @@
     experiments.setdefault("chromium.resultdb.result_sink", 100)
     experiments.setdefault("chromium.resultdb.result_sink.junit_tests", 100)
 
+    # Migrate executable to bbagent incrementally.
+    experiments.setdefault("luci.buildbucket.use_bbagent", 10)
+
     merged_resultdb_bigquery_exports = [
         resultdb.export_test_results(
             bq_table = "luci-resultdb.chromium.try_test_results",
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 27b3998..0348334 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -182,6 +182,24 @@
   ]
 }
 
+source_set("first_run_app_state_agent") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "first_run_app_state_agent.h",
+    "first_run_app_state_agent.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/app/application_delegate:app_state_header",
+    "//ios/chrome/app/application_delegate:application_delegate_internal",
+    "//ios/chrome/browser/ui:feature_flags",
+    "//ios/chrome/browser/ui/first_run:utils",
+    "//ios/chrome/browser/ui/main:main",
+    "//ios/chrome/browser/ui/main:observing_scene_agent",
+    "//ios/chrome/browser/ui/main:scene",
+  ]
+}
+
 source_set("app_internal") {
   configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
@@ -204,6 +222,7 @@
     ":app_metrics_app_state_agent",
     ":blocking_scene_commands",
     ":content_suggestions_scheduler_app_state_agent",
+    ":first_run_app_state_agent",
     ":mode",
     ":safe_mode_app_state_agent",
     ":tests_hook",
diff --git a/ios/chrome/app/application_delegate/app_state.h b/ios/chrome/app/application_delegate/app_state.h
index 5220db2..459a810 100644
--- a/ios/chrome/app/application_delegate/app_state.h
+++ b/ios/chrome/app/application_delegate/app_state.h
@@ -97,6 +97,10 @@
 // The initialization stage the app is currently at.
 @property(nonatomic, readonly) InitStage initStage;
 
+// This flag is set when the first scene has initialized its UI and never
+// resets.
+@property(nonatomic, readonly) BOOL firstSceneHasInitializedUI;
+
 // Saves the launchOptions to be used from -newTabFromLaunchOptions. If the
 // application is in background, initialize the browser to basic. If not, launch
 // the browser.
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index eec175d..55c6562 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -129,7 +129,7 @@
 // never reset.
 @property(nonatomic, assign) BOOL firstSceneHasActivated;
 
-// This flag is set when the first scene has initialized its UI and never reset.
+// Redefined as readwrite.
 @property(nonatomic, assign) BOOL firstSceneHasInitializedUI;
 
 // The current blocker target if any.
diff --git a/ios/chrome/app/application_delegate/app_state_observer.h b/ios/chrome/app/application_delegate/app_state_observer.h
index fce07c8..4c65676e 100644
--- a/ios/chrome/app/application_delegate/app_state_observer.h
+++ b/ios/chrome/app/application_delegate/app_state_observer.h
@@ -28,6 +28,16 @@
   // The app is initializing the browser objects for the browser UI (e.g., the
   // browser state).
   InitStageBrowserObjectsForUI,
+  // If there are connected scenes, the app is creating browsers and starting
+  // the root coordinators. The BVCs and Tab switchers are created here. This
+  // is what is considered the normal UI.
+  //
+  // The stage is no-op for regular startups (no FRE, no Safe Mode) in which
+  // case the app will continue its transition to InitStageFinal and the UI is
+  // initialized when the scene transitions to the foreground.
+  InitStageNormalUI,
+  // TODO(crbug.com/1198246): Decouple FRE from Browser views to be able to go
+  // through this stage before InitStageNormalUI.
   // The app is considering presenting the FRE UI. Will remain in that state
   // when presenting the FRE.
   InitStageFirstRun,
diff --git a/ios/chrome/app/application_delegate/app_state_unittest.mm b/ios/chrome/app/application_delegate/app_state_unittest.mm
index 0452144..5ce46dd 100644
--- a/ios/chrome/app/application_delegate/app_state_unittest.mm
+++ b/ios/chrome/app/application_delegate/app_state_unittest.mm
@@ -98,6 +98,9 @@
     case InitStageBrowserObjectsForUI:
       [appState queueTransitionToNextInitStage];
       break;
+    case InitStageNormalUI:
+      [appState queueTransitionToNextInitStage];
+      break;
     case InitStageFirstRun:
       [appState queueTransitionToNextInitStage];
       break;
diff --git a/ios/chrome/app/application_delegate/fake_startup_information.mm b/ios/chrome/app/application_delegate/fake_startup_information.mm
index aadc32cc..daf8f47 100644
--- a/ios/chrome/app/application_delegate/fake_startup_information.mm
+++ b/ios/chrome/app/application_delegate/fake_startup_information.mm
@@ -14,7 +14,7 @@
 @implementation FakeStartupInformation
 
 @synthesize appLaunchTime = _appLaunchTime;
-@synthesize isPresentingFirstRunUI = _isPresentingFirstRunUI;
+@synthesize isFirstRun = _isFirstRun;
 @synthesize isColdStart = _isColdStart;
 @synthesize restoreHelper = _restoreHelper;
 
diff --git a/ios/chrome/app/application_delegate/startup_information.h b/ios/chrome/app/application_delegate/startup_information.h
index b7109f3..734b60a 100644
--- a/ios/chrome/app/application_delegate/startup_information.h
+++ b/ios/chrome/app/application_delegate/startup_information.h
@@ -15,9 +15,8 @@
 // Contains information about the startup.
 @protocol StartupInformation<NSObject>
 
-// Whether First Run UI (terms of service & sync sign-in) is being presented
-// in a modal dialog.
-@property(nonatomic, readonly) BOOL isPresentingFirstRunUI;
+// Whether the app is starting in first run.
+@property(nonatomic, assign) BOOL isFirstRun;
 // Whether the current session began from a cold start. NO if the app has
 // entered the background at least once since start up.
 @property(nonatomic) BOOL isColdStart;
diff --git a/ios/chrome/app/application_delegate/url_opener_unittest.mm b/ios/chrome/app/application_delegate/url_opener_unittest.mm
index 248088d..6ee5d95 100644
--- a/ios/chrome/app/application_delegate/url_opener_unittest.mm
+++ b/ios/chrome/app/application_delegate/url_opener_unittest.mm
@@ -40,7 +40,7 @@
 @interface StubStartupInformation : NSObject <StartupInformation>
 @end
 @implementation StubStartupInformation
-@synthesize isPresentingFirstRunUI = _isPresentingFirstRunUI;
+@synthesize isFirstRun = _isFirstRun;
 @synthesize isColdStart = _isColdStart;
 @synthesize appLaunchTime = _appLaunchTime;
 @synthesize restoreHelper = _restoreHelper;
diff --git a/ios/chrome/app/first_run_app_state_agent.h b/ios/chrome/app/first_run_app_state_agent.h
new file mode 100644
index 0000000..bd83dcf
--- /dev/null
+++ b/ios/chrome/app/first_run_app_state_agent.h
@@ -0,0 +1,18 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_APP_FIRST_RUN_APP_STATE_AGENT_H_
+#define IOS_CHROME_APP_FIRST_RUN_APP_STATE_AGENT_H_
+
+#import "ios/chrome/app/application_delegate/app_state_agent.h"
+
+@class AppState;
+
+// App state agent that displays the first run UI when needed and handles the
+// InitStageFirstRun stage.
+@interface FirstRunAppAgent : NSObject <AppStateAgent>
+
+@end
+
+#endif  // IOS_CHROME_APP_FIRST_RUN_APP_STATE_AGENT_H_
diff --git a/ios/chrome/app/first_run_app_state_agent.mm b/ios/chrome/app/first_run_app_state_agent.mm
new file mode 100644
index 0000000..b3e83a7
--- /dev/null
+++ b/ios/chrome/app/first_run_app_state_agent.mm
@@ -0,0 +1,115 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/app/first_run_app_state_agent.h"
+
+#import "base/logging.h"
+#import "ios/chrome/app/application_delegate/app_state.h"
+#import "ios/chrome/app/application_delegate/app_state_observer.h"
+#include "ios/chrome/app/application_delegate/startup_information.h"
+#include "ios/chrome/browser/ui/first_run/first_run_util.h"
+#import "ios/chrome/browser/ui/main/scene_controller.h"
+#import "ios/chrome/browser/ui/main/scene_state.h"
+#import "ios/chrome/browser/ui/main/scene_state_observer.h"
+#include "ios/chrome/browser/ui/ui_feature_flags.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface FirstRunAppAgent () <AppStateObserver>
+
+// The app state for the app.
+@property(nonatomic, weak, readonly) AppState* appState;
+
+// The scene that is chosen for presenting the FRE on.
+@property(nonatomic, strong) SceneState* presentingSceneState;
+
+@end
+
+@implementation FirstRunAppAgent
+
+- (void)dealloc {
+  [_appState removeObserver:self];
+}
+
+#pragma mark - AppStateAgent
+
+- (void)setAppState:(AppState*)appState {
+  // This should only be called once!
+  DCHECK(!_appState);
+
+  _appState = appState;
+  [appState addObserver:self];
+}
+
+#pragma mark - AppStateObserver
+
+- (void)appState:(AppState*)appState
+    willTransitionToInitStage:(InitStage)nextInitStage {
+  if (nextInitStage != InitStageNormalUI) {
+    return;
+  }
+
+  // Determine whether the app has to go through startup at first run before
+  // starting the UI initialization to make the information available on time.
+  self.appState.startupInformation.isFirstRun =
+      ShouldPresentFirstRunExperience();
+}
+
+- (void)appState:(AppState*)appState
+    didTransitionFromInitStage:(InitStage)previousInitStage {
+  if (self.appState.initStage != InitStageFirstRun) {
+    return;
+  }
+
+  if (!self.appState.startupInformation.isFirstRun) {
+    // Skip the FRE because it wasn't determined to be needed.
+    [self.appState queueTransitionToNextInitStage];
+    return;
+  }
+
+  // Cannot show the FRE UI immediately because there is no scene state to
+  // present from.
+  if (!self.presentingSceneState) {
+    return;
+  }
+
+  [self showFirstRun:self.presentingSceneState];
+}
+
+- (void)appState:(AppState*)appState
+    firstSceneHasInitializedUI:(SceneState*)sceneState {
+  // Select the first scene that the app declares as initialized to present
+  // the FRE UI on.
+  self.presentingSceneState = sceneState;
+
+  if (self.appState.initStage != InitStageFirstRun) {
+    return;
+  }
+
+  if (!self.appState.startupInformation.isFirstRun) {
+    // Skip the FRE because it wasn't determined to be needed.
+    return;
+  }
+
+  [self showFirstRun:sceneState];
+}
+
+#pragma mark - internal
+
+- (void)showFirstRun:(SceneState*)sceneState {
+  DCHECK(self.appState.initStage == InitStageFirstRun);
+  // There must be a designated presenting scene before showing the first run
+  // UI.
+  DCHECK(self.presentingSceneState);
+
+  if (base::FeatureList::IsEnabled(kEnableFREUIModuleIOS)) {
+    [sceneState.controller showFirstRunUI];
+  } else {
+    [sceneState.controller showLegacyFirstRunUI];
+  }
+}
+
+@end
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm
index 0042d3a..32f4805 100644
--- a/ios/chrome/app/main_controller.mm
+++ b/ios/chrome/app/main_controller.mm
@@ -36,6 +36,7 @@
 #import "ios/chrome/app/blocking_scene_commands.h"
 #import "ios/chrome/app/content_suggestions_scheduler_app_state_agent.h"
 #import "ios/chrome/app/deferred_initialization_runner.h"
+#import "ios/chrome/app/first_run_app_state_agent.h"
 #import "ios/chrome/app/memory_monitor.h"
 #import "ios/chrome/app/safe_mode_app_state_agent.h"
 #import "ios/chrome/app/spotlight/spotlight_manager.h"
@@ -94,7 +95,6 @@
 #import "ios/chrome/browser/ui/appearance/appearance_customization.h"
 #import "ios/chrome/browser/ui/commands/browser_commands.h"
 #import "ios/chrome/browser/ui/commands/command_dispatcher.h"
-#import "ios/chrome/browser/ui/first_run/first_run_util.h"
 #import "ios/chrome/browser/ui/first_run/welcome_to_chrome_view_controller.h"
 #import "ios/chrome/browser/ui/main/browser_view_wrangler.h"
 #import "ios/chrome/browser/ui/main/scene_delegate.h"
@@ -364,6 +364,7 @@
 // - StartupInformation
 @synthesize isColdStart = _isColdStart;
 @synthesize appLaunchTime = _appLaunchTime;
+@synthesize isFirstRun = _isFirstRun;
 
 #pragma mark - Application lifecycle
 
@@ -588,11 +589,24 @@
 - (void)appState:(AppState*)appState
     firstSceneHasInitializedUI:(SceneState*)sceneState {
   DCHECK(self.appState.initStage > InitStageSafeMode);
+
+  if (self.appState.initStage <= InitStageNormalUI) {
+    return;
+  }
+
+  // TODO(crbug.com/1213955): Pass the scene to this method to make sure that
+  // the chosen scene is initialized.
   [self startUpAfterFirstWindowCreated];
 }
 
 - (void)appState:(AppState*)appState
     didTransitionFromInitStage:(InitStage)previousInitStage {
+  // TODO(crbug.com/1213955): Remove this once the bug fixed.
+  if (previousInitStage == InitStageNormalUI &&
+      appState.firstSceneHasInitializedUI) {
+    [self startUpAfterFirstWindowCreated];
+  }
+
   switch (appState.initStage) {
     case InitStageStart:
       [appState queueTransitionToNextInitStage];
@@ -614,11 +628,13 @@
       [self startUpBrowserForegroundInitialization];
       [appState queueTransitionToNextInitStage];
       break;
+    case InitStageNormalUI:
+      // Scene controllers use this stage to create the normal UI if needed.
+      // There is no specific agent (other than SceneController) handling
+      // this stage.
+      [appState queueTransitionToNextInitStage];
+      break;
     case InitStageFirstRun:
-      // TODO(crbug.com/1178821): Move this to the FRE agent.
-      if (!ShouldPresentFirstRunExperience()) {
-        [appState queueTransitionToNextInitStage];
-      }
       break;
     case InitStageFinal:
       break;
@@ -628,6 +644,7 @@
 - (void)addPostSafeModeAgents {
   [self.appState addAgent:[[ContentSuggestionsSchedulerAppAgent alloc] init]];
   [self.appState addAgent:[[IncognitoUsageAppStateAgent alloc] init]];
+  [self.appState addAgent:[[FirstRunAppAgent alloc] init]];
 }
 
 #pragma mark - Property implementation.
@@ -664,14 +681,6 @@
 
 #pragma mark - StartupInformation implementation.
 
-- (BOOL)isPresentingFirstRunUI {
-  BOOL isPresentingFirstRunUI = NO;
-  for (SceneState* scene in self.appState.connectedScenes) {
-    isPresentingFirstRunUI |= scene.presentingFirstRunUI;
-  }
-
-  return isPresentingFirstRunUI;
-}
 
 - (FirstUserActionRecorder*)firstUserActionRecorder {
   return _firstUserActionRecorder.get();
diff --git a/ios/chrome/browser/ui/main/scene_controller.h b/ios/chrome/browser/ui/main/scene_controller.h
index 3b0ba373..146dc90 100644
--- a/ios/chrome/browser/ui/main/scene_controller.h
+++ b/ios/chrome/browser/ui/main/scene_controller.h
@@ -41,6 +41,14 @@
 // Return YES if incognito mode is forced by enterprise policy.
 - (BOOL)isIncognitoForced;
 
+// TODO(crbug.com/1210256): Remove this once it is migrated to the agent.
+// Shows the new first run UI.
+- (void)showFirstRunUI;
+
+// TODO(crbug.com/1210256): Remove this once it is migrated to the agent.
+// Shows the legacy first run UI.
+- (void)showLegacyFirstRunUI;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_MAIN_SCENE_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm
index 9dbecf8..afc1ac09 100644
--- a/ios/chrome/browser/ui/main/scene_controller.mm
+++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -669,7 +669,7 @@
 // in one place.
 - (void)transitionToSceneActivationLevel:(SceneActivationLevel)level
                             appInitStage:(InitStage)appInitStage {
-  if (appInitStage < InitStageFirstRun) {
+  if (appInitStage < InitStageNormalUI) {
     // Nothing per-scene should happen before the app completes the global
     // setup, like executing Safe mode, or creating the main BrowserState.
     return;
@@ -960,18 +960,12 @@
         browser->GetCommandDispatcher(), ApplicationCommands);
     [applicationHandler openURLInNewTab:command];
   }
+  [self maybeShowDefaultBrowserPromo];
+}
 
-  // If this is first run, show the first run UI on top of the new tab.
-  // If this isn't first run, check if the sign-in promo needs to display.
-  if (firstRun &&
-      !self.sceneState.appState.startupInformation.isPresentingFirstRunUI) {
-    if (base::FeatureList::IsEnabled(kEnableFREUIModuleIOS)) {
-      [self showFirstRunUI];
-    } else {
-      [self showLegacyFirstRunUI];
-    }
-    // Do not ever show the 'restore' infobar during first run.
-    self.sceneState.appState.startupInformation.restoreHelper = nil;
+- (void)maybeShowDefaultBrowserPromo {
+  if (self.sceneState.appState.startupInformation.isFirstRun) {
+    return;
   }
 
   // If skipping first run, not in Safe Mode, no post opening action and the
@@ -981,8 +975,7 @@
   if (self.startupParameters) {
     postOpeningAction = self.startupParameters.postOpeningAction;
   }
-  if (!firstRun && self.sceneState.appState.initStage > InitStageSafeMode &&
-      postOpeningAction == NO_ACTION &&
+  if (postOpeningAction == NO_ACTION &&
       !self.sceneState.appState.postCrashLaunch &&
       !IsChromeLikelyDefaultBrowser() && !UserInPromoCooldown()) {
     // Show the Default Browser promo UI if the user's past behavior fits
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
index 62ba4e5..f1ef69a 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-b165ec4bd7a5979a32893cd52ecd39e61cc65339
\ No newline at end of file
+80d880da89fe7fc68a618886bd840bc78d01d01c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
index f133e9b..9827862 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-36f7829f239fab66d5af0bde8373c1c97186821f
\ No newline at end of file
+8bcef9b3c1aa525536ef20b10a1054017dedc298
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
index a0051aab..a9a9f6d 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-21c7a1e4734ce5005e50a4f07419bb693470d473
\ No newline at end of file
+629d6740591e37667b9995417f7aeb2b29c95587
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
index c468ff2..f1239fc 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-165894cdafef013703fb57c6dca18ded340f5718
\ No newline at end of file
+ef969cf9d08ba945db9890c769262947680a7d88
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
index 05f1959..7952ecaf 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-0471019a3826bd272307ab625bf5bb3de791a8eb
\ No newline at end of file
+57d24f8ee73d102cda79ec03d64e7da852cc5ae8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
index 6e07618..10239a9 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-bcf5330f3f49eda35b79d1f397e97b280d4074ec
\ No newline at end of file
+61ed8258672a79ec3477e7afd75b0aa8a3a677e5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
index a2fd07b..05c3a860 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-ab01efc21b39b87bb64ae359b65ac97c68af8f04
\ No newline at end of file
+da41f59f4997f7fd8202073ae4d0fb7c72661fc3
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
index 4ae4442c..809989d 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-9be3d6042aa1ec76bebc57b2d1a8380abcd058d2
\ No newline at end of file
+2be29d17378a10cdc5c7add3be9e81801c86ee5a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
index 81df60e3..8b59dfc6 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@
-975c899c4005d936f8734d84cdf0d3470658c282
\ No newline at end of file
+b11445102234dc0d2f242f86b39dde8cd5aabd02
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
index f48b80ef..ce71ef1 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@
-9deb5535c74ff72c62913f93ee134338bbf3599c
\ No newline at end of file
+f85353736c7022b91f5ba0e38fdf7064e1642ac6
\ No newline at end of file
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index c27fd384..7c9793c 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -806,7 +806,7 @@
 
 // Enable WebRTC actions for the Media Session API.
 const base::Feature kMediaSessionWebRTC{"MediaSessionWebRTC",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enables flash to be ducked by audio focus. This is enabled on Chrome OS which
 // has audio focus enabled.
diff --git a/media/gpu/h264_dpb.cc b/media/gpu/h264_dpb.cc
index e89ed45..0e95581f 100644
--- a/media/gpu/h264_dpb.cc
+++ b/media/gpu/h264_dpb.cc
@@ -54,6 +54,10 @@
   return nullptr;
 }
 
+D3D11H264Picture* H264Picture::AsD3D11H264Picture() {
+  return nullptr;
+}
+
 H264DPB::H264DPB() : max_num_pics_(0) {}
 H264DPB::~H264DPB() = default;
 
diff --git a/media/gpu/h264_dpb.h b/media/gpu/h264_dpb.h
index 1395f9e..36abd4a898 100644
--- a/media/gpu/h264_dpb.h
+++ b/media/gpu/h264_dpb.h
@@ -23,6 +23,7 @@
 
 class V4L2H264Picture;
 class VaapiH264Picture;
+class D3D11H264Picture;
 
 // A picture (a frame or a field) in the H.264 spec sense.
 // See spec at http://www.itu.int/rec/T-REC-H.264
@@ -40,6 +41,7 @@
 
   virtual V4L2H264Picture* AsV4L2H264Picture();
   virtual VaapiH264Picture* AsVaapiH264Picture();
+  virtual D3D11H264Picture* AsD3D11H264Picture();
 
   // Values calculated per H.264 specification or taken from slice header.
   // See spec for more details on each (some names have been converted from
diff --git a/media/gpu/windows/d3d11_h264_accelerator.cc b/media/gpu/windows/d3d11_h264_accelerator.cc
index 20acf54..ba3bf54d 100644
--- a/media/gpu/windows/d3d11_h264_accelerator.cc
+++ b/media/gpu/windows/d3d11_h264_accelerator.cc
@@ -54,6 +54,8 @@
   D3D11PictureBuffer* picture;
   size_t picture_index_;
 
+  D3D11H264Picture* AsD3D11H264Picture() override { return this; }
+
  protected:
   ~D3D11H264Picture() override;
 };
@@ -104,10 +106,12 @@
 
   HRESULT hr;
   for (;;) {
+    D3D11H264Picture* d3d11_pic = pic->AsD3D11H264Picture();
+    if (!d3d11_pic)
+      return DecoderStatus::kFail;
     hr = video_context_->DecoderBeginFrame(
-        video_decoder_.Get(),
-        static_cast<D3D11H264Picture*>(pic.get())->picture->output_view().Get(),
-        0, nullptr);
+        video_decoder_.Get(), d3d11_pic->picture->output_view().Get(), 0,
+        nullptr);
 
     if (hr == E_PENDING || hr == D3DERR_WASSTILLDRAWING) {
       // Hardware is busy.  We should make the call again.
@@ -123,7 +127,7 @@
   }
 
   sps_ = *sps;
-  for (size_t i = 0; i < 16; i++) {
+  for (size_t i = 0; i < media::kRefFrameMaxCount; i++) {
     ref_frame_list_[i].bPicEntry = 0xFF;
     field_order_cnt_list_[i][0] = 0;
     field_order_cnt_list_[i][1] = 0;
@@ -136,8 +140,19 @@
 
   int i = 0;
   for (auto it = dpb.begin(); it != dpb.end(); i++, it++) {
-    D3D11H264Picture* our_ref_pic = static_cast<D3D11H264Picture*>(it->get());
-    if (!our_ref_pic->ref)
+    // The DPB is supposed to have a maximum of 16 pictures in it, but there's
+    // nothing actually stopping it from having more. If we run into this case,
+    // something is clearly wrong, and we should just fail decoding rather than
+    // try to sort out which pictures really shouldn't be included.
+    if (i >= media::kRefFrameMaxCount)
+      return DecoderStatus::kFail;
+
+    D3D11H264Picture* our_ref_pic = it->get()->AsD3D11H264Picture();
+    // How does a non-d3d11 picture get here you might ask? The decoder
+    // inserts blank H264Picture objects that we can't use as part of filling
+    // gaps in frame numbers. If we see one, it's not a reference picture
+    // anyway, so skip it.
+    if (!our_ref_pic || !our_ref_pic->ref)
       continue;
     ref_frame_list_[i].Index7Bits = our_ref_pic->picture_index_;
     ref_frame_list_[i].AssociatedFlag = our_ref_pic->long_term;
@@ -284,9 +299,8 @@
 }
 
 void D3D11H264Accelerator::PicParamsFromPic(DXVA_PicParams_H264* pic_param,
-                                            scoped_refptr<H264Picture> pic) {
-  pic_param->CurrPic.Index7Bits =
-      static_cast<D3D11H264Picture*>(pic.get())->picture_index_;
+                                            D3D11H264Picture* pic) {
+  pic_param->CurrPic.Index7Bits = pic->picture_index_;
   pic_param->RefPicFlag = pic->ref;
   pic_param->frame_num = pic->frame_num;
 
@@ -319,7 +333,11 @@
   if (!PicParamsFromPPS(&pic_param, pps))
     return DecoderStatus::kFail;
   PicParamsFromSliceHeader(&pic_param, slice_hdr);
-  PicParamsFromPic(&pic_param, std::move(pic));
+
+  D3D11H264Picture* d3d11_pic = pic->AsD3D11H264Picture();
+  if (!d3d11_pic)
+    return DecoderStatus::kFail;
+  PicParamsFromPic(&pic_param, d3d11_pic);
 
   memcpy(pic_param.RefFrameList, ref_frame_list_,
          sizeof pic_param.RefFrameList);
@@ -582,9 +600,8 @@
 }
 
 bool D3D11H264Accelerator::OutputPicture(scoped_refptr<H264Picture> pic) {
-  D3D11H264Picture* our_pic = static_cast<D3D11H264Picture*>(pic.get());
-
-  return client_->OutputResult(our_pic, our_pic->picture);
+  D3D11H264Picture* our_pic = pic->AsD3D11H264Picture();
+  return our_pic && client_->OutputResult(our_pic, our_pic->picture);
 }
 
 void D3D11H264Accelerator::RecordFailure(const std::string& reason,
diff --git a/media/gpu/windows/d3d11_h264_accelerator.h b/media/gpu/windows/d3d11_h264_accelerator.h
index 5e888cb7..59198167 100644
--- a/media/gpu/windows/d3d11_h264_accelerator.h
+++ b/media/gpu/windows/d3d11_h264_accelerator.h
@@ -28,6 +28,8 @@
 
 namespace media {
 
+constexpr int kRefFrameMaxCount = 16;
+
 class D3D11H264Accelerator;
 class MediaLog;
 
@@ -75,8 +77,7 @@
   void PicParamsFromSliceHeader(DXVA_PicParams_H264* pic_param,
                                 const H264SliceHeader* pps);
 
-  void PicParamsFromPic(DXVA_PicParams_H264* pic_param,
-                        scoped_refptr<H264Picture> pic);
+  void PicParamsFromPic(DXVA_PicParams_H264* pic_param, D3D11H264Picture* pic);
 
   void SetVideoDecoder(ComD3D11VideoDecoder video_decoder);
 
@@ -98,10 +99,10 @@
 
   // This information set at the beginning of a frame and saved for processing
   // all the slices.
-  DXVA_PicEntry_H264 ref_frame_list_[16];
+  DXVA_PicEntry_H264 ref_frame_list_[kRefFrameMaxCount];
   H264SPS sps_;
-  INT field_order_cnt_list_[16][2];
-  USHORT frame_num_list_[16];
+  INT field_order_cnt_list_[kRefFrameMaxCount][2];
+  USHORT frame_num_list_[kRefFrameMaxCount];
   UINT used_for_reference_flags_;
   USHORT non_existing_frame_flags_;
 
diff --git a/mojo/public/js/mojo_bindings_resources.grd b/mojo/public/js/mojo_bindings_resources.grd
index 1f8e8bb6..cca7888 100644
--- a/mojo/public/js/mojo_bindings_resources.grd
+++ b/mojo/public/js/mojo_bindings_resources.grd
@@ -90,6 +90,11 @@
           use_base_dir="false"
           resource_path="mojo/mojo/public/mojom/base/text_direction.mojom-lite.js"
           type="BINDATA" />
+      <include name="IDR_MOJO_TEXT_DIRECTION_MOJOM_WEBUI_JS"
+          file="${root_gen_dir}/mojom-webui/mojo/public/mojom/base/text_direction.mojom-webui.js"
+          use_base_dir="false"
+          resource_path="mojo/mojo/public/mojom/base/text_direction.mojom-webui.js"
+          type="BINDATA" />
       <include name="IDR_MOJO_UNGUESSABLE_TOKEN_MOJOM_WEBUI_JS"
           file="${root_gen_dir}/mojom-webui/mojo/public/mojom/base/unguessable_token.mojom-webui.js"
           use_base_dir="false"
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 72788fd..c8d5ea05 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -231,8 +231,6 @@
     "base/sockaddr_storage.cc",
     "base/sockaddr_storage.h",
     "base/sys_addrinfo.h",
-    "base/test_data_stream.cc",
-    "base/test_data_stream.h",
     "base/transport_info.cc",
     "base/transport_info.h",
     "base/upload_bytes_element_reader.cc",
@@ -1051,8 +1049,6 @@
     "url_request/url_request_netlog_params.h",
     "url_request/url_request_redirect_job.cc",
     "url_request/url_request_redirect_job.h",
-    "url_request/url_request_test_job.cc",
-    "url_request/url_request_test_job.h",
     "url_request/url_request_throttler_entry.cc",
     "url_request/url_request_throttler_entry.h",
     "url_request/url_request_throttler_entry_interface.h",
@@ -2057,6 +2053,8 @@
     "base/mock_network_change_notifier.h",
     "base/test_completion_callback.cc",
     "base/test_completion_callback.h",
+    "base/test_data_stream.cc",
+    "base/test_data_stream.h",
     "cert/mock_cert_verifier.cc",
     "cert/mock_cert_verifier.h",
     "cert/mock_client_cert_verifier.cc",
@@ -2167,6 +2165,8 @@
     "test/url_request/url_request_mock_data_job.h",
     "url_request/test_url_fetcher_factory.cc",
     "url_request/test_url_fetcher_factory.h",
+    "url_request/url_request_test_job.cc",
+    "url_request/url_request_test_job.h",
     "url_request/url_request_test_util.cc",
     "url_request/url_request_test_util.h",
   ]
diff --git a/net/base/test_data_stream.h b/net/base/test_data_stream.h
index 9432d3e..d51bf72 100644
--- a/net/base/test_data_stream.h
+++ b/net/base/test_data_stream.h
@@ -5,15 +5,12 @@
 #ifndef NET_BASE_TEST_DATA_STREAM_H_
 #define NET_BASE_TEST_DATA_STREAM_H_
 
-#include <string.h>  // for memcpy().
-#include "net/base/net_export.h"
-
 // This is a class for generating an infinite stream of data which can be
 // verified independently to be the correct stream of data.
 
 namespace net {
 
-class NET_EXPORT TestDataStream {
+class TestDataStream {
  public:
   TestDataStream();
 
diff --git a/net/url_request/url_request_test_job.h b/net/url_request/url_request_test_job.h
index 9c5906c..6d8b158 100644
--- a/net/url_request/url_request_test_job.h
+++ b/net/url_request/url_request_test_job.h
@@ -9,7 +9,6 @@
 
 #include "base/memory/weak_ptr.h"
 #include "net/base/load_timing_info.h"
-#include "net/base/net_export.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_job.h"
 
@@ -37,7 +36,7 @@
 //
 // Optionally, you can also construct test jobs that advance automatically
 // without having to call ProcessOnePendingMessage.
-class NET_EXPORT_PRIVATE URLRequestTestJob : public URLRequestJob {
+class URLRequestTestJob : public URLRequestJob {
  public:
   // Constructs a job to return one of the canned responses depending on the
   // request url.
diff --git a/services/network/crash_keys.h b/services/network/crash_keys.h
index b76bf773..eab0abe 100644
--- a/services/network/crash_keys.h
+++ b/services/network/crash_keys.h
@@ -6,7 +6,6 @@
 #define SERVICES_NETWORK_CRASH_KEYS_H_
 
 #include "base/debug/crash_logging.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "url/origin.h"
 
 namespace network {
diff --git a/services/network/public/cpp/trust_token_http_headers.h b/services/network/public/cpp/trust_token_http_headers.h
index 6906478..5518c4b 100644
--- a/services/network/public/cpp/trust_token_http_headers.h
+++ b/services/network/public/cpp/trust_token_http_headers.h
@@ -28,6 +28,13 @@
 
 // As a request header, provides the version of Trust Token being used in the
 // Sec-Trust-Token header.
+//
+// Alongside signed requests, provides the "major" Trust Tokens protocol
+// version, for instance "TrustTokenV3" for the protocol versions
+// "TrustTokenV3VOPRF" and "TrustTokenV3PMB"). This is a tentative addition to
+// make it easy to adapt to a breaking change in the signature payload's format
+// without having to feature-detect implicitly by trying detect structural
+// chracteristics of the old and new formats: see crbug.com/1209728.
 constexpr char kTrustTokensSecTrustTokenVersionHeader[] =
     "Sec-Trust-Token-Version";
 
diff --git a/services/network/public/mojom/trust_tokens.mojom b/services/network/public/mojom/trust_tokens.mojom
index c0ec622..93f1e8e 100644
--- a/services/network/public/mojom/trust_tokens.mojom
+++ b/services/network/public/mojom/trust_tokens.mojom
@@ -15,6 +15,9 @@
 // model; they just use different cryptosystems to generate tokens' signatures.
 // TODO(crbug/1133969): Schema versioning needs to be implemented for future
 // versions that need to clear the database on schema changes.
+//
+// NOTE: When updating this enum, you probably also need to update
+// kTrustTokensMajorVersion in trust_token_parameterization.h.
 enum TrustTokenProtocolVersion {
   kTrustTokenV3Pmb,
   kTrustTokenV3Voprf,
diff --git a/services/network/trust_tokens/trust_token_parameterization.h b/services/network/trust_tokens/trust_token_parameterization.h
index 5302b20..cf90422 100644
--- a/services/network/trust_tokens/trust_token_parameterization.h
+++ b/services/network/trust_tokens/trust_token_parameterization.h
@@ -67,6 +67,16 @@
 // Returns the maximum number of keys supported by a protocol version.
 size_t TrustTokenMaxKeysForVersion(mojom::TrustTokenProtocolVersion version);
 
+// This is a representation of the current "major" version, a notion which is
+// not totally well-defined but roughly corresponds to each substantial
+// collection of backwards-incompatible functional changes. We send it along
+// with signed requests in the Sec-Trust-Token-Version header, because the
+// "minor" version (the specifics of the underlying issue and redemption crypto)
+// deso not affect signed request processing. As of writing in June 2021, it's
+// not for sure that the "major" version will stay around as a concept for the
+// long haul.
+constexpr char kTrustTokensMajorVersion[] = "TrustTokenV3";
+
 }  // namespace network
 
 #endif  // SERVICES_NETWORK_TRUST_TOKENS_TRUST_TOKEN_PARAMETERIZATION_H_
diff --git a/services/network/trust_tokens/trust_token_request_signing_helper.cc b/services/network/trust_tokens/trust_token_request_signing_helper.cc
index f89075127..f1c9961c 100644
--- a/services/network/trust_tokens/trust_token_request_signing_helper.cc
+++ b/services/network/trust_tokens/trust_token_request_signing_helper.cc
@@ -25,6 +25,7 @@
 #include "services/network/public/cpp/trust_token_parameterization.h"
 #include "services/network/public/mojom/trust_tokens.mojom-shared.h"
 #include "services/network/trust_tokens/proto/public.pb.h"
+#include "services/network/trust_tokens/trust_token_parameterization.h"
 #include "services/network/trust_tokens/trust_token_request_canonicalizer.h"
 #include "services/network/trust_tokens/trust_token_store.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
@@ -472,6 +473,10 @@
     return;
   }
 
+  request->SetExtraRequestHeaderByName(kTrustTokensSecTrustTokenVersionHeader,
+                                       kTrustTokensMajorVersion,
+                                       /*overwrite=*/true);
+
   LogOutcome(net_log_, "Success");
   std::move(done).Run(mojom::TrustTokenOperationStatus::kOk);
 }
@@ -523,7 +528,7 @@
           net::structured_headers::Item(
               signer_->GetAlgorithmIdentifier(),
               net::structured_headers::Item::ItemType::kStringType),
-              {});
+          {});
 
   std::vector<net::structured_headers::ParameterizedItem> keys_and_signatures;
   for (const auto& kv : signatures_per_issuer) {
diff --git a/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc b/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc
index cc8948b..eafada4e 100644
--- a/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc
+++ b/services/network/trust_tokens/trust_token_request_signing_helper_unittest.cc
@@ -402,6 +402,36 @@
       Header("Sec-Time", StrEq(base::TimeToISO8601(base::Time::Now()))));
 }
 
+TEST_F(TrustTokenRequestSigningHelperTest, ProvidesMajorVersionHeader) {
+  std::unique_ptr<TrustTokenStore> store = TrustTokenStore::CreateForTesting();
+
+  TrustTokenRequestSigningHelper::Params params(
+      *SuitableTrustTokenOrigin::Create(GURL("https://issuer.com")),
+      *SuitableTrustTokenOrigin::Create(GURL("https://toplevel.com")));
+  params.sign_request_data = mojom::TrustTokenSignRequestData::kHeadersOnly;
+  params.should_add_timestamp = true;
+
+  TrustTokenRedemptionRecord my_record;
+  my_record.set_public_key("key");
+  my_record.set_body("look at me, I'm an RR body");
+  store->SetRedemptionRecord(params.issuers.front(), params.toplevel,
+                             my_record);
+
+  TrustTokenRequestSigningHelper helper(
+      store.get(), std::move(params), std::make_unique<FakeSigner>(),
+      std::make_unique<TrustTokenRequestCanonicalizer>());
+
+  auto my_request = MakeURLRequest("https://destination.com/");
+  my_request->set_initiator(url::Origin::Create(GURL("https://issuer.com/")));
+  mojom::TrustTokenOperationStatus result =
+      ExecuteBeginOperationAndWaitForResult(&helper, my_request.get());
+
+  EXPECT_EQ(result, mojom::TrustTokenOperationStatus::kOk);
+  // This test's expectation should change whenever the supported Trust Tokens
+  // major version changes.
+  EXPECT_THAT(*my_request, Header("Sec-Trust-Token-Version", "TrustTokenV3"));
+}
+
 // Test RR attachment without request signing:
 // - The two issuers with stored redemption records should appear in the header.
 // - A third issuer without a corresponding redemption record in storage
diff --git a/testing/buildbot/OWNERS b/testing/buildbot/OWNERS
index 81a2ac6..bd20440 100644
--- a/testing/buildbot/OWNERS
+++ b/testing/buildbot/OWNERS
@@ -28,6 +28,7 @@
 per-file *oobe*=file://chrome/browser/ui/webui/chromeos/login/OWNERS
 
 # iOS Owners
+per-file chromium.clang.json=file://infra/config/groups/ios/OWNERS
 per-file chromium.fyi.json=file://infra/config/groups/ios/OWNERS
 per-file chromium.mac.json=file://infra/config/groups/ios/OWNERS
 per-file *.pyl=file://infra/config/groups/ios/OWNERS
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 9a1519e..fc097b12 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -4689,7 +4689,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M91",
-              "revision": "version:91.0.4472.87"
+              "revision": "version:91.0.4472.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -4768,7 +4768,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M92",
-              "revision": "version:92.0.4515.40"
+              "revision": "version:92.0.4515.41"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -4926,7 +4926,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M91",
-              "revision": "version:91.0.4472.87"
+              "revision": "version:91.0.4472.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -5005,7 +5005,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M92",
-              "revision": "version:92.0.4515.40"
+              "revision": "version:92.0.4515.41"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json
index 00dbbdad..cce63db7 100644
--- a/testing/buildbot/chromium.android.json
+++ b/testing/buildbot/chromium.android.json
@@ -53852,7 +53852,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M91",
-              "revision": "version:91.0.4472.87"
+              "revision": "version:91.0.4472.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -53932,7 +53932,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M92",
-              "revision": "version:92.0.4515.40"
+              "revision": "version:92.0.4515.41"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54092,7 +54092,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M91",
-              "revision": "version:91.0.4472.87"
+              "revision": "version:91.0.4472.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54172,7 +54172,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M92",
-              "revision": "version:92.0.4515.40"
+              "revision": "version:92.0.4515.41"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54397,7 +54397,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M91",
-              "revision": "version:91.0.4472.87"
+              "revision": "version:91.0.4472.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54476,7 +54476,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M92",
-              "revision": "version:92.0.4515.40"
+              "revision": "version:92.0.4515.41"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54634,7 +54634,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M91",
-              "revision": "version:91.0.4472.87"
+              "revision": "version:91.0.4472.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54713,7 +54713,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M92",
-              "revision": "version:92.0.4515.40"
+              "revision": "version:92.0.4515.41"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -54938,7 +54938,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M91",
-              "revision": "version:91.0.4472.87"
+              "revision": "version:91.0.4472.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -55017,7 +55017,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M92",
-              "revision": "version:92.0.4515.40"
+              "revision": "version:92.0.4515.41"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -55175,7 +55175,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M91",
-              "revision": "version:91.0.4472.87"
+              "revision": "version:91.0.4472.88"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
@@ -55254,7 +55254,7 @@
             {
               "cipd_package": "chromium/testing/weblayer-x86",
               "location": "weblayer_instrumentation_test_M92",
-              "revision": "version:92.0.4515.40"
+              "revision": "version:92.0.4515.41"
             },
             {
               "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 090333c1..a5ee086 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -31,6 +31,36 @@
         "name": "check_network_annotations",
         "script": "check_network_annotations.py",
         "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "check_static_initializers",
+        "script": "check_static_initializers.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "checkdeps",
+        "script": "checkdeps.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "checkperms",
+        "script": "checkperms.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "headless_python_unittests",
+        "script": "headless_python_unittests.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "webkit_lint",
+        "script": "blink_lint_expectations.py",
+        "swarming": {}
       }
     ]
   },
@@ -1727,6 +1757,11 @@
         "name": "check_network_annotations",
         "script": "check_network_annotations.py",
         "swarming": {}
+      },
+      {
+        "name": "webkit_lint",
+        "script": "blink_lint_expectations.py",
+        "swarming": {}
       }
     ]
   },
@@ -59491,6 +59526,31 @@
         "name": "check_network_annotations",
         "script": "check_network_annotations.py",
         "swarming": {}
+      },
+      {
+        "name": "check_static_initializers",
+        "script": "check_static_initializers.py",
+        "swarming": {}
+      },
+      {
+        "name": "checkdeps",
+        "script": "checkdeps.py",
+        "swarming": {}
+      },
+      {
+        "name": "checkperms",
+        "script": "checkperms.py",
+        "swarming": {}
+      },
+      {
+        "name": "headless_python_unittests",
+        "script": "headless_python_unittests.py",
+        "swarming": {}
+      },
+      {
+        "name": "webkit_lint",
+        "script": "blink_lint_expectations.py",
+        "swarming": {}
       }
     ]
   },
@@ -63631,6 +63691,12 @@
     "scripts": [
       {
         "isolate_profile_data": true,
+        "name": "check_network_annotations",
+        "script": "check_network_annotations.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
         "name": "check_static_initializers",
         "script": "check_static_initializers.py",
         "swarming": {}
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index c5e1edc..7686000 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -26808,6 +26808,52 @@
           "--browser=android-chromium",
           "--passthrough",
           "-v",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gles --use-cmd-decoder=passthrough --force_high_performance_gpu",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
+        ],
+        "isolate_name": "telemetry_gpu_integration_test",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_gles_passthrough_tests",
+        "resultdb": {
+          "enable": true
+        },
+        "should_retry_with_patch": false,
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
+              "location": "bin",
+              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
+            }
+          ],
+          "containment_type": "AUTO",
+          "dimension_sets": [
+            {
+              "device_os": "R",
+              "device_os_type": "userdebug",
+              "device_type": "flame",
+              "os": "Android",
+              "pool": "chromium.tests.gpu"
+            }
+          ],
+          "idempotent": false,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl_conformance",
+          "--show-stdout",
+          "--browser=android-chromium",
+          "--passthrough",
+          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=validating --force_high_performance_gpu",
           "--webgl-conformance-version=2.0.1",
           "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index c55fbe4..c3c25fc 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -4592,6 +4592,36 @@
         "name": "check_network_annotations",
         "script": "check_network_annotations.py",
         "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "check_static_initializers",
+        "script": "check_static_initializers.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "checkdeps",
+        "script": "checkdeps.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "checkperms",
+        "script": "checkperms.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "headless_python_unittests",
+        "script": "headless_python_unittests.py",
+        "swarming": {}
+      },
+      {
+        "isolate_profile_data": true,
+        "name": "webkit_lint",
+        "script": "blink_lint_expectations.py",
+        "swarming": {}
       }
     ]
   },
@@ -4606,19 +4636,6 @@
       "sync_integration_tests"
     ]
   },
-  "Linux Builder Robocrop": {
-    "additional_compile_targets": [
-      "all"
-    ],
-    "scripts": [
-      {
-        "isolate_profile_data": true,
-        "name": "check_network_annotations",
-        "script": "check_network_annotations.py",
-        "swarming": {}
-      }
-    ]
-  },
   "Linux Ozone Tester (Headless)": {
     "gtest_tests": [
       {
@@ -11798,38 +11815,6 @@
         },
         "test_id_prefix": "ninja://:blink_web_tests/"
       }
-    ],
-    "scripts": [
-      {
-        "isolate_profile_data": true,
-        "name": "check_static_initializers",
-        "script": "check_static_initializers.py",
-        "swarming": {}
-      },
-      {
-        "isolate_profile_data": true,
-        "name": "checkdeps",
-        "script": "checkdeps.py",
-        "swarming": {}
-      },
-      {
-        "isolate_profile_data": true,
-        "name": "checkperms",
-        "script": "checkperms.py",
-        "swarming": {}
-      },
-      {
-        "isolate_profile_data": true,
-        "name": "headless_python_unittests",
-        "script": "headless_python_unittests.py",
-        "swarming": {}
-      },
-      {
-        "isolate_profile_data": true,
-        "name": "webkit_lint",
-        "script": "blink_lint_expectations.py",
-        "swarming": {}
-      }
     ]
   },
   "Linux Tests (dbg)(1)": {
@@ -13711,2267 +13696,6 @@
       }
     ]
   },
-  "Linux Tests Robocrop": {
-    "gtest_tests": [
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "absl_hardening_tests",
-        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "accessibility_unittests",
-        "test_id_prefix": "ninja://ui/accessibility:accessibility_unittests/"
-      },
-      {
-        "args": [
-          "angle_unittests"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "angle_unittests",
-        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
-        "use_isolated_scripts_api": true
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "app_shell_unittests",
-        "test_id_prefix": "ninja://extensions/shell:app_shell_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "aura_unittests",
-        "test_id_prefix": "ninja://ui/aura:aura_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "base_unittests",
-        "test_id_prefix": "ninja://base:base_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "base_util_unittests",
-        "test_id_prefix": "ninja://base/util:base_util_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_common_unittests",
-        "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_fuzzer_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_fuzzer_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_heap_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform/heap:blink_heap_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_platform_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "webkit_unit_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "boringssl_crypto_tests",
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "boringssl_ssl_tests",
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
-        },
-        "test": "browser_tests",
-        "test_id_prefix": "ninja://chrome/test:browser_tests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=-*UsingRealWebcam*"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "capture_unittests",
-        "test_id_prefix": "ninja://media/capture:capture_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cast_unittests",
-        "test_id_prefix": "ninja://media/cast:cast_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cc_unittests",
-        "test_id_prefix": "ninja://cc:cc_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chrome_app_unittests",
-        "test_id_prefix": "ninja://chrome/test:chrome_app_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chromedriver_unittests",
-        "test_id_prefix": "ninja://chrome/test/chromedriver:chromedriver_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "color_unittests",
-        "test_id_prefix": "ninja://ui/color:color_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "components_browsertests",
-        "test_id_prefix": "ninja://components:components_browsertests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "components_unittests",
-        "test_id_prefix": "ninja://components:components_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "compositor_unittests",
-        "test_id_prefix": "ninja://ui/compositor:compositor_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "args": [
-          "--enable-features=ProcessHostOnUI"
-        ],
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "process_host_on_ui_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "content_unittests",
-        "test_id_prefix": "ninja://content/test:content_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "crashpad_tests",
-        "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cronet_tests",
-        "test_id_prefix": "ninja://components/cronet:cronet_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cronet_unittests",
-        "test_id_prefix": "ninja://components/cronet:cronet_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "crypto_unittests",
-        "test_id_prefix": "ninja://crypto:crypto_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "dbus_unittests",
-        "test_id_prefix": "ninja://dbus:dbus_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "device_unittests",
-        "test_id_prefix": "ninja://device:device_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "display_unittests",
-        "test_id_prefix": "ninja://ui/display:display_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "events_unittests",
-        "test_id_prefix": "ninja://ui/events:events_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "extensions_browsertests",
-        "test_id_prefix": "ninja://extensions:extensions_browsertests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "extensions_unittests",
-        "test_id_prefix": "ninja://extensions:extensions_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "filesystem_service_unittests",
-        "test_id_prefix": "ninja://components/services/filesystem:filesystem_service_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gcm_unit_tests",
-        "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gfx_unittests",
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gin_unittests",
-        "test_id_prefix": "ninja://gin:gin_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "google_apis_unittests",
-        "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gpu_unittests",
-        "test_id_prefix": "ninja://gpu:gpu_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gtk_unittests",
-        "test_id_prefix": "ninja://ui/gtk:gtk_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gwp_asan_unittests",
-        "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "headless_browsertests",
-        "test_id_prefix": "ninja://headless:headless_browsertests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "headless_unittests",
-        "test_id_prefix": "ninja://headless:headless_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "interactive_ui_tests",
-        "test_id_prefix": "ninja://chrome/test:interactive_ui_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ipc_tests",
-        "test_id_prefix": "ninja://ipc:ipc_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "jingle_unittests",
-        "test_id_prefix": "ninja://jingle:jingle_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "latency_unittests",
-        "test_id_prefix": "ninja://ui/latency:latency_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "libjingle_xmpp_unittests",
-        "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "liburlpattern_unittests",
-        "test_id_prefix": "ninja://third_party/liburlpattern:liburlpattern_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "media_blink_unittests",
-        "test_id_prefix": "ninja://media/blink:media_blink_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "media_unittests",
-        "test_id_prefix": "ninja://media:media_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "message_center_unittests",
-        "test_id_prefix": "ninja://ui/message_center:message_center_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "midi_unittests",
-        "test_id_prefix": "ninja://media/midi:midi_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "mojo_core_unittests",
-        "test_id_prefix": "ninja://mojo/core:mojo_core_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "mojo_unittests",
-        "test_id_prefix": "ninja://mojo:mojo_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "nacl_helper_nonsfi_unittests",
-        "test_id_prefix": "ninja://components/nacl/loader:nacl_helper_nonsfi_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "nacl_loader_unittests",
-        "test_id_prefix": "ninja://components/nacl/loader:nacl_loader_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "native_theme_unittests",
-        "test_id_prefix": "ninja://ui/native_theme:native_theme_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "net_unittests",
-        "test_id_prefix": "ninja://net:net_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "openscreen_unittests",
-        "test_id_prefix": "ninja://chrome/browser/media/router:openscreen_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "pdf_unittests",
-        "test_id_prefix": "ninja://pdf:pdf_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "perfetto_unittests",
-        "test_id_prefix": "ninja://third_party/perfetto:perfetto_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ppapi_unittests",
-        "test_id_prefix": "ninja://ppapi:ppapi_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "printing_unittests",
-        "test_id_prefix": "ninja://printing:printing_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "remoting_unittests",
-        "test_id_prefix": "ninja://remoting:remoting_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sandbox_linux_unittests",
-        "test_id_prefix": "ninja://sandbox/linux:sandbox_linux_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "service_manager_unittests",
-        "test_id_prefix": "ninja://services/service_manager/tests:service_manager_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "services_unittests",
-        "test_id_prefix": "ninja://services:services_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "shell_dialogs_unittests",
-        "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "skia_unittests",
-        "test_id_prefix": "ninja://skia:skia_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "snapshot_unittests",
-        "test_id_prefix": "ninja://ui/snapshot:snapshot_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sql_unittests",
-        "test_id_prefix": "ninja://sql:sql_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "storage_unittests",
-        "test_id_prefix": "ninja://storage:storage_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sync_integration_tests",
-        "test_id_prefix": "ninja://chrome/test:sync_integration_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "traffic_annotation_auditor_unittests",
-        "test_id_prefix": "ninja://tools/traffic_annotation/auditor:traffic_annotation_auditor_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_base_unittests",
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_touch_selection_unittests",
-        "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "unit_tests",
-        "test_id_prefix": "ninja://chrome/test:unit_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "url_unittests",
-        "test_id_prefix": "ninja://url:url_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "views_unittests",
-        "test_id_prefix": "ninja://ui/views:views_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "vr_common_unittests",
-        "test_id_prefix": "ninja://chrome/browser/vr:vr_common_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "vr_pixeltests",
-        "test_id_prefix": "ninja://chrome/browser/vr:vr_pixeltests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "weblayer_browsertests",
-        "test_id_prefix": "ninja://weblayer/test:weblayer_browsertests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "weblayer_unittests",
-        "test_id_prefix": "ninja://weblayer/test:weblayer_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "wm_unittests",
-        "test_id_prefix": "ninja://ui/wm:wm_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "wtf_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "x11_unittests",
-        "test_id_prefix": "ninja://ui/platform_window/x11:x11_unittests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "name": "xr_browser_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "xr_browser_tests",
-        "test_id_prefix": "ninja://chrome/test:xr_browser_tests/"
-      },
-      {
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_gtest_merge.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "zlib_unittests",
-        "test_id_prefix": "ninja://third_party/zlib:zlib_unittests/"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "isolate_name": "blink_python_tests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "blink_python_tests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://:blink_python_tests/"
-      },
-      {
-        "args": [
-          "--num-retries=3"
-        ],
-        "isolate_name": "blink_web_tests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "blink_web_tests",
-        "resultdb": {
-          "enable": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://:blink_web_tests/"
-      },
-      {
-        "args": [
-          "--test-type=integration"
-        ],
-        "isolate_name": "chromedriver_py_tests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "chromedriver_py_tests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://chrome/test/chromedriver:chromedriver_py_tests/"
-      },
-      {
-        "isolate_name": "chromedriver_replay_unittests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "chromedriver_replay_unittests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://chrome/test/chromedriver:chromedriver_replay_unittests/"
-      },
-      {
-        "isolate_name": "content_shell_crash_test",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "content_shell_crash_test",
-        "resultdb": {
-          "enable": true,
-          "result_format": "single"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://content/shell:content_shell_crash_test/"
-      },
-      {
-        "isolate_name": "flatbuffers_unittests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "flatbuffers_unittests",
-        "resultdb": {
-          "enable": true,
-          "result_format": "single"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://third_party/flatbuffers:flatbuffers_unittests/"
-      },
-      {
-        "isolate_name": "grit_python_unittests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "grit_python_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://tools/grit:grit_python_unittests/"
-      },
-      {
-        "isolate_name": "metrics_python_tests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "metrics_python_tests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://tools/metrics:metrics_python_tests/"
-      },
-      {
-        "isolate_name": "mojo_python_unittests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "mojo_python_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://mojo/public/tools:mojo_python_unittests/"
-      },
-      {
-        "args": [
-          "--additional-driver-flag",
-          "--disable-site-isolation-trials",
-          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/disable-site-isolation-trials",
-          "--num-retries=3"
-        ],
-        "isolate_name": "blink_web_tests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "not_site_per_process_blink_web_tests",
-        "resultdb": {
-          "enable": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
-        },
-        "test_id_prefix": "ninja://:blink_web_tests/"
-      },
-      {
-        "isolate_name": "telemetry_gpu_unittests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_gpu_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_unittests/"
-      },
-      {
-        "args": [
-          "--extra-browser-args=--enable-crashpad",
-          "--xvfb",
-          "--jobs=1"
-        ],
-        "isolate_name": "telemetry_perf_unittests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_perf_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 12
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_perf_unittests/"
-      },
-      {
-        "args": [
-          "--jobs=1",
-          "--extra-browser-args=--disable-gpu"
-        ],
-        "isolate_name": "telemetry_unittests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_unittests",
-        "resultdb": {
-          "enable": true
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "idempotent": false,
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 8
-        },
-        "test_id_prefix": "ninja://chrome/test:telemetry_unittests/"
-      },
-      {
-        "args": [
-          "--gtest-benchmark-name=views_perftests"
-        ],
-        "isolate_name": "views_perftests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [
-            "--smoke-test-mode"
-          ],
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "views_perftests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://ui/views:views_perftests/"
-      },
-      {
-        "args": [
-          "--num-retries=3",
-          "--additional-driver-flag=--enable-gpu-rasterization",
-          "--additional-driver-flag=--enable-features=UseSkiaRenderer,Vulkan",
-          "--additional-driver-flag=--enable-oop-rasterization",
-          "--additional-driver-flag=--use-vulkan=swiftshader",
-          "--additional-driver-flag=--disable-vulkan-fallback-to-gl-for-testing",
-          "--fuzzy-diff",
-          "--skipped=always",
-          "--test-list=../../testing/buildbot/filters/gpu.skiarenderer_vulkan_blink_web_tests.filter",
-          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-features=UseSkiaRenderer",
-          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/enable-gpu-rasterization",
-          "--additional-expectations=../../third_party/blink/web_tests/FlagExpectations/use-vulkan=swiftshader"
-        ],
-        "isolate_name": "blink_web_tests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "vulkan_swiftshader_blink_web_tests",
-        "resultdb": {
-          "enable": true
-        },
-        "results_handler": "layout tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test_id_prefix": "ninja://:blink_web_tests/"
-      },
-      {
-        "isolate_name": "webdriver_wpt_tests",
-        "isolate_profile_data": true,
-        "merge": {
-          "args": [
-            "--verbose"
-          ],
-          "script": "//third_party/blink/tools/merge_web_test_results.py"
-        },
-        "name": "webdriver_tests_suite",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "os": "Ubuntu-16.04",
-              "pool": "chromium.tests.robocrop"
-            }
-          ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
-        },
-        "test_id_prefix": "ninja://:webdriver_wpt_tests/"
-      }
-    ],
-    "scripts": [
-      {
-        "isolate_profile_data": true,
-        "name": "check_static_initializers",
-        "script": "check_static_initializers.py",
-        "swarming": {}
-      },
-      {
-        "isolate_profile_data": true,
-        "name": "checkdeps",
-        "script": "checkdeps.py",
-        "swarming": {}
-      },
-      {
-        "isolate_profile_data": true,
-        "name": "checkperms",
-        "script": "checkperms.py",
-        "swarming": {}
-      },
-      {
-        "isolate_profile_data": true,
-        "name": "headless_python_unittests",
-        "script": "headless_python_unittests.py",
-        "swarming": {}
-      },
-      {
-        "isolate_profile_data": true,
-        "name": "webkit_lint",
-        "script": "blink_lint_expectations.py",
-        "swarming": {}
-      }
-    ]
-  },
   "Network Service Linux": {
     "gtest_tests": [
       {
@@ -18148,6 +15872,11 @@
     ],
     "scripts": [
       {
+        "name": "check_network_annotations",
+        "script": "check_network_annotations.py",
+        "swarming": {}
+      },
+      {
         "name": "check_static_initializers",
         "script": "check_static_initializers.py",
         "swarming": {}
@@ -20202,6 +17931,11 @@
     ],
     "scripts": [
       {
+        "name": "check_network_annotations",
+        "script": "check_network_annotations.py",
+        "swarming": {}
+      },
+      {
         "name": "check_static_initializers",
         "script": "check_static_initializers.py",
         "swarming": {}
@@ -22219,6 +19953,11 @@
     ],
     "scripts": [
       {
+        "name": "check_network_annotations",
+        "script": "check_network_annotations.py",
+        "swarming": {}
+      },
+      {
         "name": "check_static_initializers",
         "script": "check_static_initializers.py",
         "swarming": {}
diff --git a/testing/buildbot/chromium.win.json b/testing/buildbot/chromium.win.json
index f0b994c1b..2c1e0b9 100644
--- a/testing/buildbot/chromium.win.json
+++ b/testing/buildbot/chromium.win.json
@@ -1553,6 +1553,11 @@
         "name": "check_network_annotations",
         "script": "check_network_annotations.py",
         "swarming": {}
+      },
+      {
+        "name": "webkit_lint",
+        "script": "blink_lint_expectations.py",
+        "swarming": {}
       }
     ]
   },
@@ -1565,6 +1570,11 @@
         "name": "check_network_annotations",
         "script": "check_network_annotations.py",
         "swarming": {}
+      },
+      {
+        "name": "webkit_lint",
+        "script": "blink_lint_expectations.py",
+        "swarming": {}
       }
     ]
   },
@@ -3951,14 +3961,6 @@
         },
         "test_id_prefix": "ninja://ui/views:views_perftests/"
       }
-    ],
-    "scripts": [
-      {
-        "isolate_profile_data": true,
-        "name": "webkit_lint",
-        "script": "blink_lint_expectations.py",
-        "swarming": {}
-      }
     ]
   },
   "Win10 Tests x64 (dbg)": {
@@ -7697,13 +7699,6 @@
         },
         "test_id_prefix": "ninja://ui/views:views_perftests/"
       }
-    ],
-    "scripts": [
-      {
-        "name": "webkit_lint",
-        "script": "blink_lint_expectations.py",
-        "swarming": {}
-      }
     ]
   },
   "Win7 Tests (dbg)(1)": {
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index fd2932c..d1eb3a0 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -1002,13 +1002,6 @@
       },
     },
   },
-  'robocrop-swarming-pool': {
-    'swarming': {
-      'dimensions': {
-        'pool': 'chromium.tests.robocrop',
-      },
-    },
-  },
   'sailfish': {
     # Pixel 1
     'swarming': {
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index d7a82cbb..ffc017fb6 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -3155,13 +3155,6 @@
       'Mac FYI Retina Release (NVIDIA)',
     ],
   },
-  'webgl2_conformance_gles_passthrough_tests': {
-    'remove_from': [
-      # Currently only enough Pixel 4 capacity to run either validating or
-      # passthrough on the trybot, not both.
-      'Optional Android Release (Pixel 4)',
-    ],
-  },
   'webgl2_conformance_validating_tests': {
     'remove_from': [
       # The Mac NVIDIA Retina bots don't have the capacity to run
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 0016dec..557d8188 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -425,12 +425,6 @@
       },
     },
 
-    'check_network_annotations_script': {
-      'check_network_annotations': {
-        'script': 'check_network_annotations.py',
-      },
-    },
-
     'chrome_public_wpt': {
       'chrome_public_wpt': {
         'swarming': {
@@ -889,6 +883,9 @@
     },
 
     'chromium_linux_scripts': {
+      'check_network_annotations': {
+        'script': 'check_network_annotations.py',
+      },
       'check_static_initializers': {
         'script': 'check_static_initializers.py',
       },
@@ -915,12 +912,6 @@
       },
     },
 
-    'chromium_scripts': {
-      'webkit_lint': {
-        'script': 'blink_lint_expectations.py',
-      },
-    },
-
     'chromium_swarm_android_gtests': {
       'base_unittests': {},
       'chrome_modern_public_bundle_fake_modules_smoke_test': {},
@@ -1104,6 +1095,15 @@
       },
     },
 
+    'chromium_win_scripts': {
+      'check_network_annotations': {
+        'script': 'check_network_annotations.py',
+      },
+      'webkit_lint': {
+        'script': 'blink_lint_expectations.py',
+      },
+    },
+
     'client_v8_chromium_gtests': {
       'app_shell_unittests': {},
       'browser_tests': {
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index b2a6ba1..c419183 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -424,7 +424,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M92',
-          'revision': 'version:92.0.4515.40',
+          'revision': 'version:92.0.4515.41',
         }
       ],
     },
@@ -448,7 +448,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M91',
-          'revision': 'version:91.0.4472.87',
+          'revision': 'version:91.0.4472.88',
         }
       ],
     },
@@ -496,7 +496,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M92',
-          'revision': 'version:92.0.4515.40',
+          'revision': 'version:92.0.4515.41',
         }
       ],
     },
@@ -520,7 +520,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M91',
-          'revision': 'version:91.0.4472.87',
+          'revision': 'version:91.0.4472.88',
         }
       ],
     },
@@ -568,7 +568,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M92',
-          'revision': 'version:92.0.4515.40',
+          'revision': 'version:92.0.4515.41',
         }
       ],
     },
@@ -592,7 +592,7 @@
         {
           'cipd_package': 'chromium/testing/weblayer-x86',
           'location': 'weblayer_instrumentation_test_M91',
-          'revision': 'version:91.0.4472.87',
+          'revision': 'version:91.0.4472.88',
         }
       ],
     },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 01b2e3d..f7a88da 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -2529,7 +2529,7 @@
           'all'
         ],
         'test_suites': {
-          'scripts': 'check_network_annotations_script',
+          'scripts': 'chromium_linux_scripts',
         }
       },
       'Linux Builder (j-500) (n2) (reclient)': {
@@ -2704,7 +2704,7 @@
           'pdf_fuzzers'
         ],
         'test_suites': {
-          'scripts': 'check_network_annotations_script',
+          'scripts': 'chromium_win_scripts',
         },
       },
       'android-backuprefptr-arm-fyi-rel': {
@@ -3096,7 +3096,7 @@
           'all',
         ],
         'test_suites': {
-          'scripts': 'check_network_annotations_script',
+          'scripts': 'chromium_linux_scripts',
         },
       },
       'linux-chromium-tests-staging-tests': {
@@ -4842,7 +4842,7 @@
           'all'
         ],
         'test_suites': {
-          'scripts': 'check_network_annotations_script',
+          'scripts': 'chromium_linux_scripts',
         }
       },
       'Linux Builder (dbg)': {
@@ -4856,17 +4856,6 @@
           'sync_integration_tests'
         ]
       },
-      'Linux Builder Robocrop': {
-        'mixins': [
-          'isolate_profile_data',
-        ],
-        'additional_compile_targets': [
-          'all'
-        ],
-        'test_suites': {
-          'scripts': 'check_network_annotations_script',
-        }
-      },
       'Linux Ozone Tester (Headless)': {
         'mixins': [
           'linux-bionic',
@@ -4923,7 +4912,6 @@
         'test_suites': {
           'gtest_tests': 'chromium_linux_gtests',
           'isolated_scripts': 'chromium_linux_rel_isolated_scripts',
-          'scripts': 'chromium_linux_scripts',
         },
       },
       'Linux Tests (dbg)(1)': {
@@ -4935,18 +4923,6 @@
           'isolated_scripts': 'chromium_linux_dbg_isolated_scripts',
         },
       },
-      'Linux Tests Robocrop': {
-        'mixins': [
-          'isolate_profile_data',
-          'linux-xenial',
-          'robocrop-swarming-pool',
-        ],
-        'test_suites': {
-          'gtest_tests': 'chromium_linux_gtests',
-          'isolated_scripts': 'chromium_linux_rel_isolated_scripts',
-          'scripts': 'chromium_linux_scripts',
-        },
-      },
       # For documentation, see //services/network/README.md.
       'Network Service Linux': {
         'mixins': [
@@ -5981,7 +5957,7 @@
           'pdf_fuzzers'
         ],
         'test_suites': {
-          'scripts': 'check_network_annotations_script',
+          'scripts': 'chromium_win_scripts',
         },
       },
       'Win x64 Builder': {
@@ -5989,7 +5965,7 @@
           'pdf_fuzzers'
         ],
         'test_suites': {
-          'scripts': 'check_network_annotations_script',
+          'scripts': 'chromium_win_scripts',
         },
       },
       # 'Win10 Tests x64' and 'Win7 Tests (1)' should have the same set of
@@ -6004,7 +5980,6 @@
         'test_suites': {
           'gtest_tests': 'chromium_win10_gtests',
           'isolated_scripts': 'chromium_win_rel_isolated_scripts',
-          'scripts': 'chromium_scripts',
         },
       },
       'Win10 Tests x64 (dbg)': {
@@ -6030,7 +6005,6 @@
         'test_suites': {
           'gtest_tests': 'chromium_win_gtests',
           'isolated_scripts': 'chromium_win_rel_isolated_scripts',
-          'scripts': 'chromium_scripts',
         },
       },
       'Win7 Tests (dbg)(1)': {
diff --git a/testing/merge_scripts/code_coverage/merge_js_lib.py b/testing/merge_scripts/code_coverage/merge_js_lib.py
index c6eb347..92ab75f 100644
--- a/testing/merge_scripts/code_coverage/merge_js_lib.py
+++ b/testing/merge_scripts/code_coverage/merge_js_lib.py
@@ -258,3 +258,32 @@
 
   with open(output_path, 'w') as merged_coverage_file:
     return merged_coverage_file.write(json.dumps(coverage_by_path))
+
+
+def write_parsed_scripts(parsed_scripts_dir):
+  """Extract parsed script contents and write back to original folder structure.
+
+  Args:
+    parsed_scripts_dir (str): Directory that contains the raw JavaScript v8
+        coverage files that are identified by their ".js.json" suffix.
+  """
+  scripts = _get_paths_with_suffix(parsed_scripts_dir, '.js.json')
+  output_dir = os.path.join(parsed_scripts_dir, 'parsed')
+
+  if not scripts:
+    logging.info('No raw scripts found in %s', parsed_scripts_dir)
+    return
+
+  for file_path in scripts:
+    script_data = _parse_json_file(file_path)
+
+    if not script_data['url'].startswith('//'):
+      continue
+
+    source_path = os.path.normpath(script_data['url'].replace('//', ''))
+    source_directory = os.path.join(output_dir, os.path.dirname(source_path))
+    if not os.path.exists(source_directory):
+      os.makedirs(source_directory)
+
+    with open(os.path.join(output_dir, source_path), 'w') as f:
+      f.write(script_data['text'])
diff --git a/testing/merge_scripts/code_coverage/merge_js_lib_test.py b/testing/merge_scripts/code_coverage/merge_js_lib_test.py
index 073d98ca..f5d9892 100755
--- a/testing/merge_scripts/code_coverage/merge_js_lib_test.py
+++ b/testing/merge_scripts/code_coverage/merge_js_lib_test.py
@@ -3,8 +3,12 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import unittest
 import mock
+import os
+import shutil
+import tempfile
+import unittest
+
 import merge_js_lib as merger
 
 
@@ -428,5 +432,57 @@
     output_segments = merger._merge_segments(segment_a, segment_b)
     self.assertListEqual(output_segments, expected_output_segments)
 
+  def test_write_parsed_scripts(self):
+    test_files = [{
+      'url': '//a/b/c/1.js',
+      'location': ['a', 'b', 'c', '1.js'],
+      'exists': True
+    }, {
+      'url': '//d/e/f/5.js',
+      'location': ['d', 'e', 'f', '5.js'],
+      'exists': True
+    }, {
+      'url': '//a/b/d/7.js',
+      'location': ['a', 'b', 'd', '7.js'],
+      'exists': True
+    }, {
+      'url': 'chrome://test_webui/file.js',
+      'exists': False
+    }, {
+      'url': 'file://testing/file.js',
+      'exists': False
+    }]
+
+    test_script_file = """{
+"text": "test\\ncontents\\n%d",
+"url": "%s"
+}"""
+
+    scripts_dir = None
+    expected_files = []
+
+    try:
+      scripts_dir = tempfile.mkdtemp()
+      for i, test_script in enumerate(test_files):
+        file_path = os.path.join(scripts_dir, '%d.js.json' % i)
+        with open(file_path, 'w') as f:
+          f.write(test_script_file % (i, test_script['url']))
+
+        expected_files.append(file_path)
+        if test_script['exists']:
+          expected_files.append(os.path.join(scripts_dir,
+              'parsed', *test_script['location']))
+
+      merger.write_parsed_scripts(scripts_dir)
+      actual_files = []
+
+      for root, _, files in os.walk(scripts_dir):
+        for file_name in files:
+          actual_files.append(os.path.join(root, file_name))
+
+      self.assertItemsEqual(expected_files, actual_files)
+    finally:
+      shutil.rmtree(scripts_dir)
+
 if __name__ == '__main__':
   unittest.main()
diff --git a/testing/test.gni b/testing/test.gni
index dcb883b..934f409 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -573,7 +573,7 @@
       ]
 
       if (_use_ash_chrome) {
-        executable_args += [ "--ash-chrome-path=ash_clang_x64/chrome" ]
+        executable_args += [ "--ash-chrome-path=ash_clang_x64/test_ash_chrome" ]
       }
 
       if (is_asan) {
@@ -613,8 +613,7 @@
       write_runtime_deps = _runtime_deps_file
       deps += [ ":$_gen_runner_target" ]
       if (_use_ash_chrome && also_build_ash_chrome) {
-        data_deps +=
-            [ "//chrome:chrome(//build/toolchain/linux:ash_clang_x64)" ]
+        data_deps += [ "//chrome/test:test_ash_chrome(//build/toolchain/linux:ash_clang_x64)" ]
       }
 
       if (use_rts) {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 5a801a17..2a420f7 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1194,6 +1194,38 @@
             ]
         }
     ],
+    "BackForwardCacheDesktop": [
+        {
+            "platforms": [
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledForSpecificDomain",
+                    "params": {
+                        "TimeToLiveInBackForwardCacheInSeconds": "180",
+                        "blocked_websites": "https://domain1.org,https://domain2.org",
+                        "cache_size": "6",
+                        "check_eligibility_after_pagehide": "true",
+                        "enable_same_site": "false",
+                        "file_system_api_supported": "true",
+                        "foreground_cache_size": "2",
+                        "geolocation_supported": "true",
+                        "max_buffered_bytes": "100000",
+                        "max_buffered_bytes_per_process": "512000",
+                        "service_worker_supported": "true"
+                    },
+                    "enable_features": [
+                        "BackForwardCache",
+                        "FreezeWhileKeepActive",
+                        "LoadingTasksUnfreezable"
+                    ]
+                }
+            ]
+        }
+    ],
     "BackForwardCacheMemoryControls": [
         {
             "platforms": [
@@ -8116,7 +8148,9 @@
                 "windows",
                 "android",
                 "mac",
-                "chromeos_lacros"
+                "chromeos_lacros",
+                "chromeos",
+                "ios"
             ],
             "experiments": [
                 {
diff --git a/third_party/blink/common/storage_key/storage_key.cc b/third_party/blink/common/storage_key/storage_key.cc
index dea66ac5..5f052c8 100644
--- a/third_party/blink/common/storage_key/storage_key.cc
+++ b/third_party/blink/common/storage_key/storage_key.cc
@@ -8,10 +8,16 @@
 
 namespace blink {
 
+// static
 StorageKey StorageKey::Deserialize(const std::string& in) {
   return StorageKey(url::Origin::Create(GURL(in)));
 }
 
+// static
+StorageKey StorageKey::CreateFromStringForTesting(const std::string& origin) {
+  return Deserialize(origin);
+}
+
 std::string StorageKey::Serialize() const {
   DCHECK(!opaque());
   return origin_.GetURL().spec();
diff --git a/third_party/blink/common/storage_key/storage_key_unittest.cc b/third_party/blink/common/storage_key/storage_key_unittest.cc
index 0a25fba..8a8d2e6b8 100644
--- a/third_party/blink/common/storage_key/storage_key_unittest.cc
+++ b/third_party/blink/common/storage_key/storage_key_unittest.cc
@@ -93,6 +93,21 @@
   EXPECT_TRUE(key4.opaque());
 }
 
+// Test that string -> StorageKey test function performs as expected.
+TEST(BlinkStorageKeyTest, CreateFromStringForTesting) {
+  std::string example = "https://example.com/";
+  std::string wrong = "I'm not a valid URL.";
+
+  StorageKey key1 = StorageKey::CreateFromStringForTesting(example);
+  StorageKey key2 = StorageKey::CreateFromStringForTesting(wrong);
+  StorageKey key3 = StorageKey::CreateFromStringForTesting(std::string());
+
+  EXPECT_FALSE(key1.opaque());
+  EXPECT_EQ(key1, StorageKey(url::Origin::Create(GURL(example))));
+  EXPECT_TRUE(key2.opaque());
+  EXPECT_TRUE(key3.opaque());
+}
+
 // Test that a StorageKey, constructed by deserializing another serialized
 // StorageKey, is equivalent to the original.
 TEST(BlinkStorageKeyTest, SerializeDeserialize) {
diff --git a/third_party/blink/public/blink_resources.grd b/third_party/blink/public/blink_resources.grd
index 65bdebc..5440815 100644
--- a/third_party/blink/public/blink_resources.grd
+++ b/third_party/blink/public/blink_resources.grd
@@ -9,7 +9,7 @@
   <release seq="1">
     <includes>
       <!-- GRIT minimizes all CSS and Javascript -->
-      <include name="IDR_UASTYLE_HTML_CSS" file="../renderer/core/html/resources/html.css" type="BINDATA" compress="gzip"/>
+      <include name="IDR_UASTYLE_HTML_CSS" file="../renderer/core/html/resources/html.css" flattenhtml="true" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_QUIRKS_CSS" file="../renderer/core/html/resources/quirks.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_VIEW_SOURCE_CSS" file="../renderer/core/css/view-source.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_THEME_CHROMIUM_ANDROID_CSS" file="../renderer/core/html/resources/android.css" type="BINDATA" compress="gzip"/>
@@ -19,9 +19,7 @@
         <include name="IDR_UASTYLE_THEME_MAC_CSS" file="../renderer/core/html/resources/mac.css" type="BINDATA" compress="gzip"/>
       </if>
       <include name="IDR_UASTYLE_THEME_INPUT_MULTIPLE_FIELDS_CSS" file="../renderer/core/html/resources/input_multiple_fields.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_THEME_WIN_CSS" file="../renderer/core/html/resources/win.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_THEME_WIN_QUIRKS_CSS" file="../renderer/core/html/resources/win_quirks.css" type="BINDATA" compress="gzip"/>
-      <include name="IDR_UASTYLE_THEME_CONTROLS_REFRESH_CSS" file="../renderer/core/html/resources/controls_refresh.css" flattenhtml="true" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_THEME_FORCED_COLORS_CSS" file="../renderer/core/html/resources/forced_colors.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_POPUP_CSS" file="../renderer/core/css/popup.css" type="BINDATA" compress="gzip"/>
       <include name="IDR_UASTYLE_SVG_CSS" file="../renderer/core/css/svg.css" type="BINDATA" compress="gzip"/>
diff --git a/third_party/blink/public/common/storage_key/storage_key.h b/third_party/blink/public/common/storage_key/storage_key.h
index 99470bb5..0f6efa1 100644
--- a/third_party/blink/public/common/storage_key/storage_key.h
+++ b/third_party/blink/public/common/storage_key/storage_key.h
@@ -32,6 +32,11 @@
   // serialized.
   static StorageKey Deserialize(const std::string& in);
 
+  // Transforms a string into a StorageKey if possible (and an opaque StorageKey
+  // if not). Currently calls Deserialize, but this may change in future.
+  // For use in tests only.
+  static StorageKey CreateFromStringForTesting(const std::string& origin);
+
   // Serializes the `StorageKey` into a string.
   // This function will return the spec url of the underlying Origin. Do not
   // call if `this` is opaque.
diff --git a/third_party/blink/renderer/core/clipboard/data_transfer.cc b/third_party/blink/renderer/core/clipboard/data_transfer.cc
index 5f9a4f1..769b4b0 100644
--- a/third_party/blink/renderer/core/clipboard/data_transfer.cc
+++ b/third_party/blink/renderer/core/clipboard/data_transfer.cc
@@ -51,6 +51,7 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/drag_image.h"
 #include "third_party/blink/renderer/core/page/page.h"
+#include "third_party/blink/renderer/core/paint/cull_rect_updater.h"
 #include "third_party/blink/renderer/core/paint/paint_info.h"
 #include "third_party/blink/renderer/core/paint/paint_layer.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_painter.h"
@@ -124,6 +125,13 @@
         layer->GetLayoutObject()
             .AbsoluteToLocalQuad(FloatQuad(absolute_bounding_box))
             .BoundingBox();
+    absl::optional<OverriddenCullRectScope> cull_rect_scope;
+    if (RuntimeEnabledFeatures::CullRectUpdateEnabled()) {
+      FloatRect cull_rect = bounding_box;
+      cull_rect.Move(
+          FloatSize(layer->GetLayoutObject().FirstFragment().PaintOffset()));
+      cull_rect_scope.emplace(*layer, CullRect(EnclosingIntRect(cull_rect)));
+    }
     PaintLayerPaintingInfo painting_info(
         layer, CullRect(EnclosingIntRect(bounding_box)),
         kGlobalPaintFlattenCompositingLayers, PhysicalOffset());
diff --git a/third_party/blink/renderer/core/clipboard/data_transfer_test.cc b/third_party/blink/renderer/core/clipboard/data_transfer_test.cc
index 5762720..ec1ee8c 100644
--- a/third_party/blink/renderer/core/clipboard/data_transfer_test.cc
+++ b/third_party/blink/renderer/core/clipboard/data_transfer_test.cc
@@ -13,16 +13,19 @@
 #include "third_party/blink/renderer/core/page/drag_image.h"
 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
+#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
 
 namespace blink {
 
-class DataTransferTest : public RenderingTest {
+class DataTransferTest : public PaintTestConfigurations, public RenderingTest {
  protected:
   Page& GetPage() const { return *GetDocument().GetPage(); }
   LocalFrame& GetFrame() const { return *GetDocument().GetFrame(); }
 };
 
-TEST_F(DataTransferTest, NodeImage) {
+INSTANTIATE_PAINT_TEST_SUITE_P(DataTransferTest);
+
+TEST_P(DataTransferTest, NodeImage) {
   SetBodyInnerHTML(R"HTML(
     <style>
       #sample { width: 100px; height: 100px; }
@@ -35,7 +38,7 @@
   EXPECT_EQ(IntSize(100, 100), image->Size());
 }
 
-TEST_F(DataTransferTest, NodeImageWithNestedElement) {
+TEST_P(DataTransferTest, NodeImageWithNestedElement) {
   SetBodyInnerHTML(R"HTML(
     <style>
       div { -webkit-user-drag: element }
@@ -52,7 +55,7 @@
       << "Descendants node should have :-webkit-drag.";
 }
 
-TEST_F(DataTransferTest, NodeImageWithPsuedoClassWebKitDrag) {
+TEST_P(DataTransferTest, NodeImageWithPsuedoClassWebKitDrag) {
   SetBodyInnerHTML(R"HTML(
     <style>
       #sample { width: 100px; height: 100px; }
@@ -67,7 +70,7 @@
       << ":-webkit-drag should affect dragged image.";
 }
 
-TEST_F(DataTransferTest, NodeImageWithoutDraggedLayoutObject) {
+TEST_P(DataTransferTest, NodeImageWithoutDraggedLayoutObject) {
   SetBodyInnerHTML(R"HTML(
     <style>
       #sample { width: 100px; height: 100px; }
@@ -81,7 +84,7 @@
   EXPECT_EQ(nullptr, image.get()) << ":-webkit-drag blows away layout object";
 }
 
-TEST_F(DataTransferTest, NodeImageWithChangingLayoutObject) {
+TEST_P(DataTransferTest, NodeImageWithChangingLayoutObject) {
   SetBodyInnerHTML(R"HTML(
     <style>
       #sample { color: blue; }
@@ -109,7 +112,7 @@
       << "#sample doesn't have :-webkit-drag.";
 }
 
-TEST_F(DataTransferTest, NodeImageExceedsViewportBounds) {
+TEST_P(DataTransferTest, NodeImageExceedsViewportBounds) {
   SetBodyInnerHTML(R"HTML(
     <style>
       * { margin: 0; }
@@ -122,7 +125,7 @@
   EXPECT_EQ(IntSize(800, 600), image->Size());
 }
 
-TEST_F(DataTransferTest, NodeImageUnderScrollOffset) {
+TEST_P(DataTransferTest, NodeImageUnderScrollOffset) {
   SetBodyInnerHTML(R"HTML(
     <style>
       * { margin: 0; }
@@ -153,7 +156,7 @@
             second_image->Size());
 }
 
-TEST_F(DataTransferTest, NodeImageSizeWithPageScaleFactor) {
+TEST_P(DataTransferTest, NodeImageSizeWithPageScaleFactor) {
   SetBodyInnerHTML(R"HTML(
     <style>
       * { margin: 0; }
@@ -184,7 +187,7 @@
       image_with_offset->Size());
 }
 
-TEST_F(DataTransferTest, NodeImageSizeWithPageScaleFactorTooLarge) {
+TEST_P(DataTransferTest, NodeImageSizeWithPageScaleFactorTooLarge) {
   SetBodyInnerHTML(R"HTML(
     <style>
       * { margin: 0; }
@@ -215,7 +218,7 @@
             image_with_offset->Size());
 }
 
-TEST_F(DataTransferTest, NodeImageWithPageScaleFactor) {
+TEST_P(DataTransferTest, NodeImageWithPageScaleFactor) {
   // #bluegreen is a 2x1 rectangle where the left pixel is blue and the right
   // pixel is green. The element is offset by a margin of 1px.
   SetBodyInnerHTML(R"HTML(
@@ -254,7 +257,7 @@
       EXPECT_EQ(expected_bitmap.getColor(x, y), bitmap.getColor(x, y));
 }
 
-TEST_F(DataTransferTest, NodeImageFullyOffscreen) {
+TEST_P(DataTransferTest, NodeImageFullyOffscreen) {
   SetBodyInnerHTML(R"HTML(
     <style>
     #target {
@@ -281,7 +284,7 @@
   EXPECT_EQ(IntSize(200, 100), image->Size());
 }
 
-TEST_F(DataTransferTest, NodeImageWithScrolling) {
+TEST_P(DataTransferTest, NodeImageWithScrolling) {
   SetBodyInnerHTML(R"HTML(
     <style>
     #target {
@@ -303,7 +306,7 @@
   EXPECT_EQ(IntSize(200, 100), image->Size());
 }
 
-TEST_F(DataTransferTest, NodeImageInOffsetStackingContext) {
+TEST_P(DataTransferTest, NodeImageInOffsetStackingContext) {
   SetBodyInnerHTML(R"HTML(
     <style>
       * { margin: 0; }
@@ -337,7 +340,7 @@
   }
 }
 
-TEST_F(DataTransferTest, NodeImageWithLargerPositionedDescendant) {
+TEST_P(DataTransferTest, NodeImageWithLargerPositionedDescendant) {
   SetBodyInnerHTML(R"HTML(
     <style>
       * { margin: 0; }
@@ -380,4 +383,22 @@
   }
 }
 
+TEST_P(DataTransferTest, NodeImageOutOfView) {
+  SetBodyInnerHTML(R"HTML(
+    <div id="drag" style="position: absolute; z-index: 1; top: -200px; left: 0;
+                          width: 100px; height: 100px; background: green">
+    </div>
+  )HTML");
+
+  auto image = DataTransfer::NodeImage(GetFrame(),
+                                       *GetDocument().getElementById("drag"));
+  EXPECT_EQ(IntSize(100, 100), image->Size());
+  Color green(0, 0x80, 0);
+  const SkBitmap& bitmap = image->Bitmap();
+  for (int x = 0; x < 10; ++x) {
+    for (int y = 0; y < 10; ++y)
+      ASSERT_EQ(green, bitmap.getColor(x, y));
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index b03fd6c9..b224b2b 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -4029,7 +4029,8 @@
   });
 
   {
-    OverriddenCullRectScope force_cull_rect(*this, cull_rect);
+    OverriddenCullRectScope force_cull_rect(*GetLayoutView()->Layer(),
+                                            cull_rect);
     PaintInternal(context, global_paint_flags, cull_rect);
   }
 
@@ -4052,7 +4053,8 @@
   });
 
   {
-    OverriddenCullRectScope force_cull_rect(*this, cull_rect);
+    OverriddenCullRectScope force_cull_rect(*GetLayoutView()->Layer(),
+                                            cull_rect);
     FramePainter(*this).PaintContents(context, global_paint_flags, cull_rect);
   }
 
@@ -4064,7 +4066,7 @@
 void LocalFrameView::PaintContentsForTest(const CullRect& cull_rect) {
   AllowThrottlingScope allow_throttling(*this);
   Lifecycle().AdvanceTo(DocumentLifecycle::kInPaint);
-  OverriddenCullRectScope force_cull_rect(*this, cull_rect);
+  OverriddenCullRectScope force_cull_rect(*GetLayoutView()->Layer(), cull_rect);
   if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
     PaintController& paint_controller = EnsurePaintController();
     if (GetLayoutView()->Layer()->SelfOrDescendantNeedsRepaint()) {
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc
index f3230f57..65e052d 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.cc
@@ -147,20 +147,23 @@
     //  The anchor is visible.
     HTMLAnchorElement* anchor_element =
         static_cast<HTMLAnchorElement*>(element);
-    auto msg = mojom::blink::AnchorElementEnteredViewport::New();
-    msg->anchor_id = AnchorElementId(*anchor_element);
-    base::TimeDelta time_entered_viewport =
-        base::TimeTicks::Now() - GetRootDocument(*anchor_element)
-                                     ->Loader()
-                                     ->GetTiming()
-                                     .NavigationStart();
-    msg->navigation_start_to_entered_viewport_ms =
-        static_cast<uint64_t>(time_entered_viewport.InMilliseconds());
-    entered_viewport_messages_.push_back(std::move(msg));
+    EnqueueEnteredViewport(*anchor_element);
     intersection_observer_->unobserve(element);
   }
 }
 
+void AnchorElementMetricsSender::EnqueueEnteredViewport(
+    const HTMLAnchorElement& element) {
+  auto msg = mojom::blink::AnchorElementEnteredViewport::New();
+  msg->anchor_id = AnchorElementId(element);
+  base::TimeDelta time_entered_viewport =
+      base::TimeTicks::Now() -
+      GetRootDocument(element)->Loader()->GetTiming().NavigationStart();
+  msg->navigation_start_to_entered_viewport_ms =
+      static_cast<uint64_t>(time_entered_viewport.InMilliseconds());
+  entered_viewport_messages_.push_back(std::move(msg));
+}
+
 void AnchorElementMetricsSender::DidFinishLifecycleUpdate(
     const LocalFrameView& local_frame_view) {
   // Check that layout is stable. If it is, we can report pending
@@ -192,16 +195,26 @@
         anchor_element.GetLayoutObject()->AbsoluteBoundingBoxRect().IsEmpty()) {
       continue;
     }
+    mojom::blink::AnchorElementMetricsPtr anchor_element_metrics =
+        CreateAnchorElementMetrics(anchor_element);
+
     int sampling_period = base::GetFieldTrialParamByFeatureAsInt(
         blink::features::kNavigationPredictor, "random_anchor_sampling_period",
         100);
     int random = base::RandInt(1, sampling_period);
     if (random == 1) {
       // This anchor element is sampled in.
-      intersection_observer_->observe(&anchor_element);
+      if (anchor_element_metrics->ratio_visible_area >=
+          INTERSECTION_RATIO_THRESHOLD) {
+        // The element is already visible.
+        EnqueueEnteredViewport(anchor_element);
+      } else {
+        // Observe the element until it becomes visible.
+        intersection_observer_->observe(&anchor_element);
+      }
     }
 
-    metrics.push_back(CreateAnchorElementMetrics(anchor_element));
+    metrics.push_back(std::move(anchor_element_metrics));
   }
   // Remove all anchors, including the ones that did not qualify. This means
   // that elements that are inserted in the DOM but have an empty bounding box
diff --git a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.h b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.h
index 5a872fcf..4e820305 100644
--- a/third_party/blink/renderer/core/html/anchor_element_metrics_sender.h
+++ b/third_party/blink/renderer/core/html/anchor_element_metrics_sender.h
@@ -91,6 +91,10 @@
   // be used to send messages. Returns true if associated, false otherwise.
   bool AssociateInterface();
 
+  // Creates an AnchorElementEnteredViewportPtr for the given element and
+  // enqueue it so that it gets reported after the next layout.
+  void EnqueueEnteredViewport(const HTMLAnchorElement& element);
+
   // Use WeakMember to make sure we don't leak memory on long-lived pages.
   HeapHashSet<WeakMember<HTMLAnchorElement>> anchor_elements_to_report_;
 
diff --git a/third_party/blink/renderer/core/html/resources/controls_refresh.css b/third_party/blink/renderer/core/html/resources/controls_refresh.css
deleted file mode 100644
index c22876f..0000000
--- a/third_party/blink/renderer/core/html/resources/controls_refresh.css
+++ /dev/null
@@ -1,266 +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.
- */
-
-/* These styles adjust the default styling for HTML elements as defined in
- * core/html/resources/html.css in order to provide an updated style when
- * using the refreshed controls UI.
- */
-
-/* This sheet is appended to html.css before parsing which means the selectors
-   below are in the default html namespace:
-
-   @namespace "http://www.w3.org/1999/xhtml"
-*/
-
-select,
-select:-internal-list-box,
-input,
-textarea {
-  background-color: -internal-light-dark(#ffffff, #3B3B3B);
-  border-color: -internal-light-dark(#767676, #858585);
-}
-
-a:-webkit-any-link:focus-visible {
-  outline-offset: 1px;
-}
-
-input:focus-visible, textarea:focus-visible, select:focus-visible {
-  outline-offset: 0;
-}
-
-input[type="checkbox" i]:focus-visible,
-input[type="radio" i]:focus-visible {
-  outline-offset: 2px;
-}
-
-input,
-input[type="email" i],
-input[type="number" i],
-input[type="password" i],
-input[type="search" i],
-input[type="tel" i],
-input[type="text" i],
-input[type="url" i] {
-  padding: 1px 2px;
-}
-
-input:disabled,
-select:disabled,
-textarea:disabled {
-  border-color: rgba(118, 118, 118, 0.3);
-}
-
-input:disabled,
-textarea:disabled {
-  background-color: -internal-light-dark(rgba(239, 239, 239, 0.3), rgba(59, 59, 59, 0.3));
-  color: -internal-light-dark(default, rgba(255, 255, 255, 0.6));
-}
-
-select:disabled {
-  opacity: 0.7;
-}
-
-input[type="button" i],
-input[type="submit" i],
-input[type="reset" i],
-input[type="color" i],
-input[type="file" i]::-webkit-file-upload-button,
-button {
-  background-color: -internal-light-dark(#efefef, #3B3B3B);
-  border-color: -internal-light-dark(#767676, #858585);
-  color: -internal-light-dark(black, white);
-}
-
-input[type="button" i]:disabled,
-input[type="submit" i]:disabled,
-input[type="reset" i]:disabled,
-input[type="color" i]:disabled,
-input[type="file" i]:disabled::-webkit-file-upload-button,
-button:disabled {
-  background-color: -internal-light-dark(rgba(239, 239, 239, 0.3), rgba(19, 1, 1, 0.3));
-  border-color: -internal-light-dark(rgba(118, 118, 118, 0.3), rgba(195, 195, 195, 0.3));
-  color: -internal-light-dark(rgba(16, 16, 16, 0.3), rgba(255, 255, 255, 0.3));
-}
-
-input[type="password" i]::-internal-reveal {
-  width: 1.3em;
-  height: 1.3em;
-  cursor: default;
-  flex: none;
-  background-image: -webkit-image-set(url(images/password_reveal_on.svg) 1x);
-  background-repeat: no-repeat;
-  background-position: center;
-  background-size: contain;
-  margin-left: 3px;
-  margin-right: 3px;
-}
-
-input[type="password" i]::-internal-reveal.reveal {
-  background-image: -webkit-image-set(url(images/password_reveal_off.svg) 1x);
-}
-
-input[type="password" i]::-internal-reveal:hover,
-input[type="password" i]::-internal-reveal:focus {
-  border-radius: 1px;
-  cursor: pointer;
-}
-
-input[type="range" i] {
-  color: -internal-light-dark(#101010, #ffffff);
-}
-
-input[type="range" i]:disabled {
-  color: #c5c5c5;
-}
-
-meter::-webkit-meter-inner-element:-internal-shadow-host-has-appearance {
-  display: grid;
-  grid-template-rows: 1fr [line1] 2fr [line2] 1fr;
-  position: relative;
-}
-
-meter::-webkit-meter-bar {
-  background: -internal-light-dark(#efefef, #3B3B3B);
-  border-width: thin;
-  grid-row-start: line1;
-  grid-row-end: line2;
-  border-style: solid;
-  border-color: -internal-light-dark(rgba(118, 118, 118, 0.3), #858585);
-  border-radius: 20px;
-  box-sizing: border-box;
-  align-self: unsafe center;
-  position: absolute;
-  overflow: hidden;
-}
-
-meter::-webkit-meter-optimum-value {
-  background: -internal-light-dark(#107c10, #74b374)
-}
-
-meter::-webkit-meter-suboptimum-value {
-  background: -internal-light-dark(#ffb900, #f2c812)
-}
-
-meter::-webkit-meter-even-less-good-value {
-  background: -internal-light-dark(#d83b01, #e98f6d)
-}
-
-input[type="date" i]::-webkit-calendar-picker-indicator,
-input[type="datetime-local" i]::-webkit-calendar-picker-indicator,
-input[type="month" i]::-webkit-calendar-picker-indicator,
-input[type="week" i]::-webkit-calendar-picker-indicator {
-  background-image: -internal-light-dark(-webkit-image-set(url(images/calendar_icon.svg) 1x), -webkit-image-set(url(images/calendar_icon_white.svg) 1x));
-  background-origin: content-box;
-  background-repeat: no-repeat;
-  background-size: contain;
-  height: 1.2em;
-  margin-inline-start: 24px;
-  opacity: 1;
-  outline: none;
-  padding-bottom: 2px;
-  padding-inline-start: 3px;
-  padding-inline-end: 3px;
-  padding-top: 2px;
-  width: 1.2em;
-}
-
-input[type="date" i]::-webkit-calendar-picker-indicator:focus-visible,
-input[type="datetime-local" i]::-webkit-calendar-picker-indicator:focus-visible,
-input[type="month" i]::-webkit-calendar-picker-indicator:focus-visible,
-input[type="week" i]::-webkit-calendar-picker-indicator:focus-visible {
-  outline: solid 2px -webkit-focus-ring-color;
-  outline-offset: -2px;
-}
-
-input[type="time" i]::-webkit-calendar-picker-indicator {
-  background-image: -internal-light-dark(-webkit-image-set(url(images/time_icon.svg) 1x), -webkit-image-set(url(images/time_icon_white.svg) 1x));
-  background-origin: content-box;
-  background-repeat: no-repeat;
-  background-size: contain;
-  height: 1.05em;
-  margin-inline-start: 8px;
-  opacity: 1;
-  outline: none;
-  padding-bottom: 3px;
-  padding-inline-start: 3px;
-  padding-inline-end: 3px;
-  padding-top: 3px;
-  width: 1.05em;
-}
-
-input[type="time" i]::-webkit-calendar-picker-indicator:focus-visible {
-  outline: solid 2px -webkit-focus-ring-color;
-  outline-offset: -2px;
-}
-
-input::-webkit-calendar-picker-indicator:hover {
-  background-color: initial;
-}
-
-/* -internal-list-box is how we specify select[multiple] */
-/* select[multiple] is an exception to the prohibition on sizes here
-   because it is one of the few controls with borders that grow on zoom
-   (to solve a nasty scrollbar location problem) */
-select:-internal-list-box {
-  border-radius: 2px;
-}
-
-select:-internal-list-box option:hover {
-  background-color: initial;
-}
-
-/* option selected, list-box not focused */
-select:-internal-list-box option:checked,
-select:-internal-list-box option:checked:hover {
-  background-color: -internal-light-dark(#cecece, #545454) !important;
-  color: -internal-light-dark(#101010, #FFFFFF) !important;
-}
-
-/* option selected, list-box focused */
-select:-internal-list-box:focus option:checked,
-select:-internal-list-box:focus option:checked:hover {
-  /* TODO(crbug.com/1129658): The dark mode color here should be handled
-     within LayoutTheme::ActiveListBoxSelectionBackgroundColor(). */
-  background-color: -internal-light-dark(-internal-active-list-box-selection, #99C8FF) !important;
-  color: -internal-light-dark(-internal-active-list-box-selection-text, #3B3B3B) !important;
-}
-
-select:-internal-list-box:focus option:checked:disabled {
-  /* TODO(crbug.com/1129658): The dark mode color here should be handled
-     within LayoutTheme::ActiveListBoxSelectionBackgroundColor(). */
-  background-color: -internal-light-dark(-internal-inactive-list-box-selection, rgba(59, 59, 59, 0.3)) !important;
-}
-
-select:-internal-list-box:disabled option:checked,
-select:-internal-list-box option:checked:disabled,
-select:-internal-list-box option:checked:disabled:hover {
-  color: -internal-light-dark(gray, #aaa) !important;
-}
-
-input::-webkit-datetime-edit-ampm-field:focus,
-input::-webkit-datetime-edit-day-field:focus,
-input::-webkit-datetime-edit-hour-field:focus,
-input::-webkit-datetime-edit-millisecond-field:focus,
-input::-webkit-datetime-edit-minute-field:focus,
-input::-webkit-datetime-edit-month-field:focus,
-input::-webkit-datetime-edit-second-field:focus,
-input::-webkit-datetime-edit-week-field:focus,
-input::-webkit-datetime-edit-year-field:focus {
-    background-color: -internal-light-dark(highlight, #99C8FF);
-    color: -internal-light-dark(highlighttext, #000000);
-    outline: none;
-}
-
-input::-webkit-datetime-edit-year-field[disabled],
-input::-webkit-datetime-edit-month-field[disabled],
-input::-webkit-datetime-edit-week-field[disabled],
-input::-webkit-datetime-edit-day-field[disabled],
-input::-webkit-datetime-edit-ampm-field[disabled],
-input::-webkit-datetime-edit-hour-field[disabled],
-input::-webkit-datetime-edit-millisecond-field[disabled],
-input::-webkit-datetime-edit-minute-field[disabled],
-input::-webkit-datetime-edit-second-field[disabled] {
-    color: -internal-light-dark(GrayText, #000000);
-}
diff --git a/third_party/blink/renderer/core/html/resources/html.css b/third_party/blink/renderer/core/html/resources/html.css
index 0e7e25a..c63962ac 100644
--- a/third_party/blink/renderer/core/html/resources/html.css
+++ b/third_party/blink/renderer/core/html/resources/html.css
@@ -445,17 +445,31 @@
     text-align: start;
 }
 
+textarea {
+  appearance: auto;
+  border: 1px solid -internal-light-dark(#767676, #858585);
+  column-count: initial !important;
+  -webkit-rtl-ordering: logical;
+  resize: auto;
+  cursor: text;
+  padding: 2px;
+  white-space: pre-wrap;
+  word-wrap: break-word;
+  background-color: -internal-light-dark(#ffffff, #3B3B3B);
+  font-family: monospace;
+}
+
 input[type="hidden" i] {
     display: none
 }
 
 input {
     appearance: auto;
-    padding: 1px;
-    background-color: -internal-light-dark(white, black);
-    border: 2px inset;
+    padding:1px 0;
+    border: 2px inset -internal-light-dark(#767676, #858585);
     -webkit-rtl-ordering: logical;
     cursor: text;
+    background-color: -internal-light-dark(#ffffff, #3B3B3B);
 }
 
 input[type="search" i] {
@@ -495,6 +509,7 @@
     flex: none;
     -webkit-user-modify: read-only !important;
     margin-inline-start: 1px;
+    margin-right: 3px;
     opacity: 0;
     pointer-events: none;
     user-select: none !important;
@@ -505,6 +520,21 @@
     pointer-events: auto;
 }
 
+input[type="search" i]::-webkit-search-results-decoration {
+  margin: auto 3px auto 2px;
+}
+
+input,
+input[type="email" i],
+input[type="number" i],
+input[type="password" i],
+input[type="search" i],
+input[type="tel" i],
+input[type="text" i],
+input[type="url" i] {
+  padding: 1px 2px;
+}
+
 input::-webkit-inner-spin-button {
     appearance: auto;
     display: inline-block;
@@ -521,24 +551,6 @@
     pointer-events: auto;
 }
 
-select {
-    border-radius: 5px;
-}
-
-textarea {
-    appearance: auto;
-    background-color: -internal-light-dark(white, black);
-    border: 1px solid;
-    column-count: initial !important;
-    -webkit-rtl-ordering: logical;
-    flex-direction: column;
-    resize: auto;
-    cursor: text;
-    padding: 2px;
-    white-space: pre-wrap;
-    word-wrap: break-word;
-}
-
 ::-webkit-input-placeholder {
     -webkit-text-security: none;
     color: #757575;
@@ -574,6 +586,10 @@
     overflow-anchor: none;
 }
 
+textarea::-internal-input-suggested {
+  font-family: monospace !important;
+}
+
 input[type="password" i] {
     -webkit-text-security: disc !important;
 }
@@ -582,6 +598,29 @@
     -webkit-text-security: disc !important;
 }
 
+input[type="password" i]::-internal-reveal {
+  width: 1.3em;
+  height: 1.3em;
+  cursor: default;
+  flex: none;
+  background-image: -webkit-image-set(url(images/password_reveal_on.svg) 1x);
+  background-repeat: no-repeat;
+  background-position: center;
+  background-size: contain;
+  margin-left: 3px;
+  margin-right: 3px;
+}
+
+input[type="password" i]::-internal-reveal.reveal {
+  background-image: -webkit-image-set(url(images/password_reveal_off.svg) 1x);
+}
+
+input[type="password" i]::-internal-reveal:hover,
+input[type="password" i]::-internal-reveal:focus {
+  border-radius: 1px;
+  cursor: pointer;
+}
+
 input[type="hidden" i], input[type="image" i], input[type="file" i] {
     appearance: none;
     padding: initial;
@@ -623,14 +662,19 @@
   color: -internal-light-dark(black, white) !important;
 }
 
-input[type="radio" i], input[type="checkbox" i] {
-    margin: 3px 0.5ex;
+input[type="radio" i],
+input[type="checkbox" i] {
+    margin:3px 3px 3px 4px;
     padding: initial;
     background-color: initial;
     border: initial;
     cursor: default;
 }
 
+input[type="radio" i] {
+  margin:3px 3px 0 5px;
+}
+
 input[type="button" i], input[type="submit" i], input[type="reset" i] {
     -internal-empty-line-height: fabricated;
     appearance: auto;
@@ -647,15 +691,28 @@
     font-size: inherit;
 }
 
-input[type="button" i], input[type="submit" i], input[type="reset" i], input[type="file" i]::-webkit-file-upload-button, button {
+input[type="button" i],
+input[type="submit" i],
+input[type="reset" i],
+input[type="file" i]::-webkit-file-upload-button,
+button {
     align-items: flex-start;
     text-align: center;
     cursor: default;
-    color: -internal-light-dark(ButtonText, #AAAAAA);
-    padding: 2px 6px 3px 6px;
-    border: 2px outset ButtonFace;
-    background-color: ButtonFace;
-    box-sizing: border-box
+    padding: 1px 6px;
+    border: 2px outset -internal-light-dark(#767676, #858585);
+    box-sizing: border-box;
+    background-color: -internal-light-dark(#efefef, #3B3B3B);
+    color: -internal-light-dark(black, white);
+}
+
+input[type="checkbox" i]:disabled,
+input[type="file" i]:disabled,
+input[type="hidden" i]:disabled,
+input[type="image" i]:disabled,
+input[type="radio" i]:disabled,
+input[type="range" i]:disabled {
+    background-color: initial;
 }
 
 input[type="range" i] {
@@ -663,11 +720,12 @@
     padding: initial;
     border: initial;
     margin: 2px;
-    color: #909090;
     cursor: default;
+    color: -internal-light-dark(#101010, #ffffff);
 }
 
-input[type="range" i]::-webkit-slider-container, input[type="range" i]::-webkit-media-slider-container {
+input[type="range" i]::-webkit-slider-container,
+input[type="range" i]::-webkit-media-slider-container {
     appearance: inherit;
     flex: 1;
     min-inline-size: 0;
@@ -694,16 +752,32 @@
     display: block;
 }
 
-input[type="range" i]::-webkit-slider-thumb, input[type="range" i]::-webkit-media-slider-thumb {
+input[type="range" i]::-webkit-slider-thumb,
+input[type="range" i]::-webkit-media-slider-thumb {
     appearance: auto;
     box-sizing: border-box;
     -webkit-user-modify: read-only !important;
     display: block;
 }
 
-input[type="button" i]:disabled, input[type="submit" i]:disabled, input[type="reset" i]:disabled,
-input[type="file" i]:disabled::-webkit-file-upload-button, button:disabled,
-select:disabled, optgroup:disabled, option:disabled,
+input[type="range" i]:disabled {
+  color: #c5c5c5;
+}
+
+input[type="button" i]:disabled,
+input[type="submit" i]:disabled,
+input[type="reset" i]:disabled,
+input[type="color" i]:disabled,
+input[type="file" i]:disabled::-webkit-file-upload-button,
+button:disabled {
+  background-color: -internal-light-dark(rgba(239, 239, 239, 0.3), rgba(19, 1, 1, 0.3));
+  border-color: -internal-light-dark(rgba(118, 118, 118, 0.3), rgba(195, 195, 195, 0.3));
+  color: -internal-light-dark(rgba(16, 16, 16, 0.3), rgba(255, 255, 255, 0.3));
+}
+
+select:disabled,
+optgroup:disabled,
+option:disabled,
 select[disabled]>option {
     color: -internal-light-dark(GrayText, #aaa);
 }
@@ -717,8 +791,15 @@
 }
 
 input:disabled, textarea:disabled {
-    color: -internal-light-dark(#545454, #aaa);
     cursor: default;
+    background-color: -internal-light-dark(rgba(239, 239, 239, 0.3), rgba(59, 59, 59, 0.3));
+    color: -internal-light-dark(#545454, #aaaaaa);
+}
+
+input:disabled,
+select:disabled,
+textarea:disabled {
+  border-color: rgba(118, 118, 118, 0.3);
 }
 
 option:-internal-spatial-navigation-focus {
@@ -756,11 +837,12 @@
     appearance: auto;
     width: 44px;
     height: 23px;
-    background-color: ButtonFace;
     /* Same as native_theme_base. */
-    border: 1px #a9a9a9 solid;
+    border: 1px solid -internal-light-dark(#767676, #858585);
     padding: 1px 2px;
     cursor: default;
+    background-color: -internal-light-dark(#efefef, #3B3B3B);
+    color: -internal-light-dark(black, white);
 }
 
 input[type="color" i]::-webkit-color-swatch-wrapper {
@@ -841,27 +923,87 @@
     appearance: auto;
     box-sizing: border-box;
     align-items: center;
-    border: 1px solid;
     white-space: pre;
     -webkit-rtl-ordering: logical;
     color: -internal-light-dark(black, white);
-    background-color: -internal-light-dark(white, black);
+    background-color: -internal-light-dark(#ffffff, #3B3B3B);
+    border: 1px solid -internal-light-dark(#767676, #858585);
     cursor: default;
+    border-radius: 0;
+}
+
+select:disabled {
+  opacity: 0.7;
+}
+
+/* -internal-list-box is how we specify select[multiple] */
+/* select[multiple] is an exception to the prohibition on sizes here
+   because it is one of the few controls with borders that grow on zoom
+   (to solve a nasty scrollbar location problem) */
+select:-internal-list-box {
+    appearance: auto;
+    align-items: flex-start;
+    overflow-x: hidden;
+    overflow-y: scroll;
+    vertical-align: text-bottom;
+    white-space: nowrap;
+    border-radius: 2px;
 }
 
 select:not(:-internal-list-box) {
     overflow: visible !important;
 }
 
-select:-internal-list-box {
-    appearance: auto;
-    align-items: flex-start;
-    border: 1px inset gray;
-    border-radius: initial;
-    overflow-x: hidden;
-    overflow-y: scroll;
-    vertical-align: text-bottom;
-    white-space: nowrap;
+select:-internal-list-box optgroup option:before {
+    content: "\00a0\00a0\00a0\00a0";;
+}
+
+select:-internal-list-box option,
+select:-internal-list-box optgroup {
+    line-height: initial !important;
+}
+
+/* option selected, list-box focused */
+select:-internal-list-box:focus option:checked,
+select:-internal-list-box:focus option:checked:hover {
+    /* TODO(crbug.com/1129658): The dark mode color here should be handled
+       within LayoutTheme::ActiveListBoxSelectionBackgroundColor(). */
+    background-color: -internal-light-dark(-internal-active-list-box-selection, #99C8FF) !important;
+    color: -internal-light-dark(-internal-active-list-box-selection-text, #3B3B3B) !important;
+}
+
+select:-internal-list-box:focus option:checked:disabled {
+    /* TODO(crbug.com/1129658): The dark mode color here should be handled
+       within LayoutTheme::ActiveListBoxSelectionBackgroundColor(). */
+    background-color: -internal-light-dark(-internal-inactive-list-box-selection, rgba(59, 59, 59, 0.3)) !important;
+}
+
+/* option selected, list-box not focused */
+select:-internal-list-box option:checked,
+select:-internal-list-box option:checked:hover {
+    background-color: -internal-light-dark(#cecece, #545454) !important;
+    color: -internal-light-dark(#101010, #FFFFFF) !important;
+}
+
+select:-internal-list-box:disabled option:checked,
+select:-internal-list-box option:checked:disabled,
+select:-internal-list-box option:checked:disabled:hover {
+    color: -internal-light-dark(gray, #aaa) !important;
+}
+
+select:-internal-list-box hr {
+    border-style: none;
+    margin-block-start: 0.5em;
+    margin-block-end: 0;
+}
+
+select:-internal-list-box:focus-visible option:-internal-multi-select-focus {
+    outline: auto 1px -webkit-focus-ring-color;
+    outline-offset: -1px;
+}
+
+select:-internal-list-box option:hover {
+    background-color: initial;
 }
 
 optgroup {
@@ -877,45 +1019,6 @@
     min-height: 1.2em;
 }
 
-select:-internal-list-box optgroup option:before {
-    content: "\00a0\00a0\00a0\00a0";;
-}
-
-select:-internal-list-box option,
-select:-internal-list-box optgroup {
-    line-height: initial !important;
-}
-
-select:-internal-list-box:focus option:checked {
-    background-color: -internal-active-list-box-selection !important;
-    color: -internal-active-list-box-selection-text !important;
-}
-
-select:-internal-list-box:focus option:checked:disabled {
-    background-color: -internal-inactive-list-box-selection !important;
-}
-
-select:-internal-list-box option:checked {
-    background-color: -internal-inactive-list-box-selection !important;
-    color: -internal-inactive-list-box-selection-text !important;
-}
-
-select:-internal-list-box:disabled option:checked,
-select:-internal-list-box option:checked:disabled {
-    color: gray !important;
-}
-
-select:-internal-list-box hr {
-    border-style: none;
-    margin-block-start: 0.5em;
-    margin-block-end: 0;
-}
-
-select:-internal-list-box:focus-visible option:-internal-multi-select-focus {
-    outline: auto 1px -webkit-focus-ring-color;
-    outline-offset: -1px;
-}
-
 /* selectmenu */
 
 selectmenu {
@@ -959,7 +1062,9 @@
 }
 
 meter::-webkit-meter-inner-element:-internal-shadow-host-has-appearance {
-    display: block;
+    display: grid;
+    grid-template-rows: 1fr [line1] 2fr [line2] 1fr;
+    position: relative;
 }
 
 meter::-internal-fallback:-internal-shadow-host-has-appearance {
@@ -967,32 +1072,39 @@
 }
 
 meter::-webkit-meter-bar {
-    background: linear-gradient(to bottom, #ddd, #eee 20%, #ccc 45%, #ccc 55%, #ddd);
     height: 100%;
     width: 100%;
     -webkit-user-modify: read-only !important;
+    background: -internal-light-dark(#efefef, #3B3B3B);
+    border: thin solid -internal-light-dark(rgba(118, 118, 118, 0.3), #858585);
+    grid-row-start: line1;
+    grid-row-end: line2;
+    border-radius: 20px;
     box-sizing: border-box;
+    align-self: unsafe center;
+    position: absolute;
+    overflow: hidden;
 }
 
 meter::-webkit-meter-optimum-value {
-    background: linear-gradient(to bottom, #ad7, #cea 20%, #7a3 45%, #7a3 55%, #ad7);
     height: 100%;
     -webkit-user-modify: read-only !important;
     box-sizing: border-box;
+    background: -internal-light-dark(#107c10, #74b374)
 }
 
 meter::-webkit-meter-suboptimum-value {
-    background: linear-gradient(to bottom, #fe7, #ffc 20%, #db3 45%, #db3 55%, #fe7);
     height: 100%;
     -webkit-user-modify: read-only !important;
     box-sizing: border-box;
+    background: -internal-light-dark(#ffb900, #f2c812)
 }
 
 meter::-webkit-meter-even-less-good-value {
-    background: linear-gradient(to bottom, #f77, #fcc 20%, #d44 45%, #d44 55%, #f77);
     height: 100%;
     -webkit-user-modify: read-only !important;
     box-sizing: border-box;
+    background: -internal-light-dark(#d83b01, #e98f6d)
 }
 
 /* progress */
@@ -1109,21 +1221,103 @@
 }
 
 input:focus-visible, textarea:focus-visible, select:focus-visible {
-    outline-offset: -2px
+    outline-offset: 0;
 }
 
 input[type="button" i]:focus-visible,
-input[type="checkbox" i]:focus-visible,
 input[type="file" i]:focus-visible,
 input[type="hidden" i]:focus-visible,
 input[type="image" i]:focus-visible,
-input[type="radio" i]:focus-visible,
 input[type="reset" i]:focus-visible,
 input[type="submit" i]:focus-visible,
 input[type="file" i]:focus-visible::-webkit-file-upload-button {
     outline-offset: 0
 }
 
+input[type="checkbox" i]:focus-visible,
+input[type="radio" i]:focus-visible {
+  outline-offset: 2px;
+}
+
+
+input[type="date" i]::-webkit-calendar-picker-indicator,
+input[type="datetime-local" i]::-webkit-calendar-picker-indicator,
+input[type="month" i]::-webkit-calendar-picker-indicator,
+input[type="week" i]::-webkit-calendar-picker-indicator {
+  background-image: -internal-light-dark(-webkit-image-set(url(images/calendar_icon.svg) 1x), -webkit-image-set(url(images/calendar_icon_white.svg) 1x));
+  background-origin: content-box;
+  background-repeat: no-repeat;
+  background-size: contain;
+  height: 1.2em;
+  margin-inline-start: 24px;
+  opacity: 1;
+  outline: none;
+  padding-bottom: 2px;
+  padding-inline-start: 3px;
+  padding-inline-end: 3px;
+  padding-top: 2px;
+  width: 1.2em;
+}
+
+input[type="date" i]::-webkit-calendar-picker-indicator:focus-visible,
+input[type="datetime-local" i]::-webkit-calendar-picker-indicator:focus-visible,
+input[type="month" i]::-webkit-calendar-picker-indicator:focus-visible,
+input[type="week" i]::-webkit-calendar-picker-indicator:focus-visible {
+  outline: solid 2px -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+
+input[type="time" i]::-webkit-calendar-picker-indicator {
+  background-image: -internal-light-dark(-webkit-image-set(url(images/time_icon.svg) 1x), -webkit-image-set(url(images/time_icon_white.svg) 1x));
+  background-origin: content-box;
+  background-repeat: no-repeat;
+  background-size: contain;
+  height: 1.05em;
+  margin-inline-start: 8px;
+  opacity: 1;
+  outline: none;
+  padding-bottom: 3px;
+  padding-inline-start: 3px;
+  padding-inline-end: 3px;
+  padding-top: 3px;
+  width: 1.05em;
+}
+
+input[type="time" i]::-webkit-calendar-picker-indicator:focus-visible {
+  outline: solid 2px -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+
+input::-webkit-calendar-picker-indicator:hover {
+  background-color: initial;
+}
+
+input::-webkit-datetime-edit-ampm-field:focus,
+input::-webkit-datetime-edit-day-field:focus,
+input::-webkit-datetime-edit-hour-field:focus,
+input::-webkit-datetime-edit-millisecond-field:focus,
+input::-webkit-datetime-edit-minute-field:focus,
+input::-webkit-datetime-edit-month-field:focus,
+input::-webkit-datetime-edit-second-field:focus,
+input::-webkit-datetime-edit-week-field:focus,
+input::-webkit-datetime-edit-year-field:focus {
+    background-color: -internal-light-dark(highlight, #99C8FF);
+    color: -internal-light-dark(highlighttext, #000000);
+    outline: none;
+}
+
+input::-webkit-datetime-edit-year-field[disabled],
+input::-webkit-datetime-edit-month-field[disabled],
+input::-webkit-datetime-edit-week-field[disabled],
+input::-webkit-datetime-edit-day-field[disabled],
+input::-webkit-datetime-edit-ampm-field[disabled],
+input::-webkit-datetime-edit-hour-field[disabled],
+input::-webkit-datetime-edit-millisecond-field[disabled],
+input::-webkit-datetime-edit-minute-field[disabled],
+input::-webkit-datetime-edit-second-field[disabled] {
+    color: -internal-light-dark(GrayText, #000000);
+}
+
 a:-webkit-any-link {
     color: -webkit-link;
     text-decoration: underline;
@@ -1138,6 +1332,10 @@
     cursor: text;
 }
 
+a:-webkit-any-link:focus-visible {
+  outline-offset: 1px;
+}
+
 /* HTML5 ruby elements */
 
 ruby, rt {
diff --git a/third_party/blink/renderer/core/html/resources/input_multiple_fields.css b/third_party/blink/renderer/core/html/resources/input_multiple_fields.css
index a42f783..e7d62ed 100644
--- a/third_party/blink/renderer/core/html/resources/input_multiple_fields.css
+++ b/third_party/blink/renderer/core/html/resources/input_multiple_fields.css
@@ -56,32 +56,6 @@
     padding: 1px;
 }
 
-/* Use highlight color. */
-input::-webkit-datetime-edit-ampm-field:focus,
-input::-webkit-datetime-edit-day-field:focus,
-input::-webkit-datetime-edit-hour-field:focus,
-input::-webkit-datetime-edit-millisecond-field:focus,
-input::-webkit-datetime-edit-minute-field:focus,
-input::-webkit-datetime-edit-month-field:focus,
-input::-webkit-datetime-edit-second-field:focus,
-input::-webkit-datetime-edit-week-field:focus,
-input::-webkit-datetime-edit-year-field:focus {
-    background-color: highlight;
-    color: highlighttext;
-}
-
-input::-webkit-datetime-edit-year-field[disabled],
-input::-webkit-datetime-edit-month-field[disabled],
-input::-webkit-datetime-edit-week-field[disabled],
-input::-webkit-datetime-edit-day-field[disabled],
-input::-webkit-datetime-edit-ampm-field[disabled],
-input::-webkit-datetime-edit-hour-field[disabled],
-input::-webkit-datetime-edit-millisecond-field[disabled],
-input::-webkit-datetime-edit-minute-field[disabled],
-input::-webkit-datetime-edit-second-field[disabled] {
-    color: GrayText;
-}
-
 /* If you update padding, border, or margin in the following ruleset, update
    DateTimeEditElement::customStyelForRenderer too. */
 input::-webkit-datetime-edit-text {
diff --git a/third_party/blink/renderer/core/html/resources/win.css b/third_party/blink/renderer/core/html/resources/win.css
deleted file mode 100644
index 8c9c1c3..0000000
--- a/third_party/blink/renderer/core/html/resources/win.css
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2008 Google Inc. All rights reserved.
- * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- * 
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/* These styles override the default styling for HTML elements as defined in
-   core/html/resources/tml.css. So far we have used this file exclusively
-   for making our form elements match Firefox's. */
-
-/* This sheet is appended to html.css before parsing which means the selectors
-   below are in the default html namespace:
-
-   @namespace "http://www.w3.org/1999/xhtml"
-*/
-
-/*
- * Update padding for all text-like types including unknown types.
- * Non-text types have input[type="foo" i] with padding properties in
- * html.css or input_multiple_fields.css.
- */
-input {
-    padding:1px 0;
-}
-
-input[type="search" i] {
-    padding:1px;
-}
-
-input[type="checkbox" i] {
-    margin:3px 3px 3px 4px;
-}
-
-input[type="radio" i] {
-    margin:3px 3px 0 5px;
-}
-
-input[type="range" i] {
-    color: #c4c4c4;
-}
-
-/* Not sure this is the right color. #EBEBE4 is what Firefox uses. */
-textarea:disabled,
-input:disabled {
-    background-color: -internal-light-dark(#ebebe4, #2d2d2d);
-}
-
-/* Cancel the above rule set for some input types. */
-input[type="button" i]:disabled,
-input[type="reset" i]:disabled,
-input[type="submit" i]:disabled {
-    background-color: ButtonFace;
-}
-input[type="checkbox" i]:disabled,
-input[type="file" i]:disabled,
-input[type="hidden" i]:disabled,
-input[type="image" i]:disabled,
-input[type="radio" i]:disabled,
-input[type="range" i]:disabled {
-    background-color: initial;
-}
-
-input[type="search" i]::-webkit-search-cancel-button {
-    margin-right: 3px;
-}
-
-input[type="search" i]::-webkit-search-results-decoration {
-    margin: auto 3px auto 2px;
-}
-
-input[type="button" i], input[type="submit" i], input[type="reset" i], input[type="file" i]::-webkit-file-upload-button, button {
-    padding: 1px 6px;
-}
-
-/* Windows selects are not rounded. Custom borders for them shouldn't be either. */
-select {
-    border-radius: 0;
-    /* Same as native_theme_base. */
-    border-color: #a9a9a9;
-}
-
-select:-internal-list-box {
-    /* Same as native_theme_base. */
-    border: 1px solid #a9a9a9;
-}
-
-textarea {
-    font-family: monospace;
-    /* Same as native_theme_base. */
-    border-color: #a9a9a9;
-}
-
-textarea::-internal-input-suggested {
-    font-family: monospace !important;
-}
diff --git a/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc b/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc
index a7ff21b..4457f0ea 100644
--- a/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_media_context_impl.cc
@@ -189,14 +189,15 @@
     WebString playerId,
     const InspectorPlayerProperties& props) {
   const auto& player = players_.find(playerId);
+  Vector<InspectorPlayerProperty> properties;
   if (player != players_.end()) {
     for (const auto& property : props)
-      player->value->properties.insert(property.name, property);
-  }
+      player->value->properties.Set(property.name, property);
 
-  Vector<InspectorPlayerProperty> vector =
-      Iter2Vector<InspectorPlayerProperty>(props);
-  probe::PlayerPropertiesChanged(GetSupplementable(), playerId, vector);
+    properties.AppendRange(player->value->properties.Values().begin(),
+                           player->value->properties.Values().end());
+  }
+  probe::PlayerPropertiesChanged(GetSupplementable(), playerId, properties);
 }
 
 void MediaInspectorContextImpl::NotifyPlayerMessages(
diff --git a/third_party/blink/renderer/core/layout/layout_theme_default.cc b/third_party/blink/renderer/core/layout/layout_theme_default.cc
index 90f28b1..f03e948 100644
--- a/third_party/blink/renderer/core/layout/layout_theme_default.cc
+++ b/third_party/blink/renderer/core/layout/layout_theme_default.cc
@@ -63,18 +63,11 @@
           ? UncompressResourceAsASCIIString(
                 IDR_UASTYLE_THEME_INPUT_MULTIPLE_FIELDS_CSS)
           : String();
-  String windows_style_sheet =
-      UncompressResourceAsASCIIString(IDR_UASTYLE_THEME_WIN_CSS);
-  String controls_refresh_style_sheet =
-      UncompressResourceAsASCIIString(IDR_UASTYLE_THEME_CONTROLS_REFRESH_CSS);
   StringBuilder builder;
-  builder.ReserveCapacity(
-      extra_style_sheet.length() + multiple_fields_style_sheet.length() +
-      windows_style_sheet.length() + controls_refresh_style_sheet.length());
+  builder.ReserveCapacity(extra_style_sheet.length() +
+                          multiple_fields_style_sheet.length());
   builder.Append(extra_style_sheet);
   builder.Append(multiple_fields_style_sheet);
-  builder.Append(windows_style_sheet);
-  builder.Append(controls_refresh_style_sheet);
   return builder.ToString();
 }
 
diff --git a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
index 0c43bb6d..db90b9f 100644
--- a/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_tree_as_text.cc
@@ -323,7 +323,7 @@
      << " pos=(" << box.X() << "," << box.Y() << ")"
      << " size=(" << box.Width() << "," << box.Height() << ")"
      << " baseline=" << box.BaselinePosition(kAlphabeticBaseline) << "/"
-     << box.BaselinePosition(kIdeographicBaseline);
+     << box.BaselinePosition(kCentralBaseline);
 }
 
 static void WriteInlineTextBox(WTF::TextStream& ts,
diff --git a/third_party/blink/renderer/core/layout/line/inline_box.cc b/third_party/blink/renderer/core/layout/line/inline_box.cc
index 51891cf..17f31021 100644
--- a/third_party/blink/renderer/core/layout/line/inline_box.cc
+++ b/third_party/blink/renderer/core/layout/line/inline_box.cc
@@ -147,7 +147,7 @@
       GetLineLayoutItem().DebugPointer(), X().ToFloat(), Y().ToFloat(),
       Width().ToFloat(), Height().ToFloat(),
       BaselinePosition(kAlphabeticBaseline).ToInt(),
-      BaselinePosition(kIdeographicBaseline).ToInt());
+      BaselinePosition(kCentralBaseline).ToInt());
 }
 #endif  // DCHECK_IS_ON()
 
diff --git a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
index f98ae0fa..0ce63a8 100644
--- a/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
+++ b/third_party/blink/renderer/core/layout/line/inline_flow_box.cc
@@ -556,7 +556,7 @@
                              .Style(IsFirstLineStyle())
                              ->GetFontDescription()
                              .IsVerticalAnyUpright())
-    return kIdeographicBaseline;
+    return kCentralBaseline;
   return kAlphabeticBaseline;
 }
 
diff --git a/third_party/blink/renderer/core/paint/cull_rect_updater.cc b/third_party/blink/renderer/core/paint/cull_rect_updater.cc
index bc3a8a1d..eff7798 100644
--- a/third_party/blink/renderer/core/paint/cull_rect_updater.cc
+++ b/third_party/blink/renderer/core/paint/cull_rect_updater.cc
@@ -54,13 +54,15 @@
       root_state_(root_layer.GetLayoutObject()
                       .FirstFragment()
                       .LocalBorderBoxProperties()
-                      .Unalias()) {
-  DCHECK(root_layer.IsRootLayer());
+                      .Unalias()) {}
+
+void CullRectUpdater::Update() {
+  DCHECK(root_layer_.IsRootLayer());
+  UpdateInternal(CullRect::Infinite());
 }
 
 void CullRectUpdater::UpdateInternal(const CullRect& input_cull_rect) {
   DCHECK(RuntimeEnabledFeatures::CullRectUpdateEnabled());
-  DCHECK(root_layer_.IsRootLayer());
   if (root_layer_.GetLayoutObject().GetFrameView()->ShouldThrottleRendering())
     return;
 
@@ -276,32 +278,28 @@
   return layer.SelfOrDescendantNeedsRepaint();
 }
 
-OverriddenCullRectScope::OverriddenCullRectScope(LocalFrameView& frame_view,
+OverriddenCullRectScope::OverriddenCullRectScope(PaintLayer& root_layer,
                                                  const CullRect& cull_rect)
-    : frame_view_(frame_view) {
+    : root_layer_(root_layer) {
   if (!RuntimeEnabledFeatures::CullRectUpdateEnabled())
     return;
 
-  PaintLayer* root_layer = frame_view_.GetLayoutView()->Layer();
-  DCHECK(root_layer);
-
-  if (frame_view.GetFrame().IsLocalRoot() &&
-      !root_layer->NeedsCullRectUpdate() &&
-      !root_layer->DescendantNeedsCullRectUpdate() &&
-      cull_rect ==
-          root_layer->GetLayoutObject().FirstFragment().GetCullRect()) {
+  if (root_layer.GetLayoutObject().GetFrame()->IsLocalRoot() &&
+      !root_layer.NeedsCullRectUpdate() &&
+      !root_layer.DescendantNeedsCullRectUpdate() &&
+      cull_rect == root_layer.GetLayoutObject().FirstFragment().GetCullRect()) {
     // The cull rects calculated during PrePaint are good.
     return;
   }
 
   updated_ = true;
-  root_layer->SetNeedsCullRectUpdate();
-  CullRectUpdater(*root_layer).UpdateInternal(cull_rect);
+  root_layer.SetNeedsCullRectUpdate();
+  CullRectUpdater(root_layer).UpdateInternal(cull_rect);
 }
 
 OverriddenCullRectScope::~OverriddenCullRectScope() {
   if (RuntimeEnabledFeatures::CullRectUpdateEnabled() && updated_)
-    frame_view_.GetLayoutView()->Layer()->SetNeedsCullRectUpdate();
+    root_layer_.SetNeedsCullRectUpdate();
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/cull_rect_updater.h b/third_party/blink/renderer/core/paint/cull_rect_updater.h
index 0e5d933..9d94cb9 100644
--- a/third_party/blink/renderer/core/paint/cull_rect_updater.h
+++ b/third_party/blink/renderer/core/paint/cull_rect_updater.h
@@ -14,7 +14,6 @@
 
 class FragmentData;
 class PaintLayer;
-class LocalFrameView;
 
 // This class is used for updating the cull rects of PaintLayer fragments (see:
 // |FragmentData::cull_rect_| and |FragmentData::contents_cull_rect_|.
@@ -30,7 +29,7 @@
  public:
   explicit CullRectUpdater(PaintLayer& root_layer);
 
-  void Update() { UpdateInternal(CullRect::Infinite()); }
+  void Update();
 
  private:
   friend class OverriddenCullRectScope;
@@ -57,18 +56,20 @@
 };
 
 // Used when painting with a custom top-level cull rect, e.g. when printing a
-// page. It temporarily overrides the cull rect on the PaintLayer of the
-// LocalFrameView and marks the PaintLayer as needing to recalculate the cull
-// rect when leaving this scope.
+// page. It temporarily overrides the cull rect on the PaintLayer (which must be
+// a stacking context) and marks the PaintLayer as needing to recalculate the
+// cull rect when leaving this scope.
+// TODO(crbug.com/1215251): Avoid repaint after the scope if the scope is used
+// to paint into a separate PaintController.
 class OverriddenCullRectScope {
   STACK_ALLOCATED();
 
  public:
-  OverriddenCullRectScope(LocalFrameView&, const CullRect&);
+  OverriddenCullRectScope(PaintLayer&, const CullRect&);
   ~OverriddenCullRectScope();
 
  private:
-  LocalFrameView& frame_view_;
+  PaintLayer& root_layer_;
   bool updated_ = false;
 };
 
diff --git a/third_party/blink/renderer/core/paint/text_decoration_info.cc b/third_party/blink/renderer/core/paint/text_decoration_info.cc
index d738254..3198313 100644
--- a/third_party/blink/renderer/core/paint/text_decoration_info.cc
+++ b/third_party/blink/renderer/core/paint/text_decoration_info.cc
@@ -28,7 +28,7 @@
       if (style.TextUnderlinePosition() & kTextUnderlinePositionFromFont)
         return ResolvedUnderlinePosition::kNearAlphabeticBaselineFromFont;
       return ResolvedUnderlinePosition::kNearAlphabeticBaselineAuto;
-    case kIdeographicBaseline: {
+    case kCentralBaseline: {
       // Compute language-appropriate default underline position.
       // https://drafts.csswg.org/css-text-decor-3/#default-stylesheet
       UScriptCode script = style.GetFontDescription().GetScript();
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index f984295..dd335202 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -1692,9 +1692,10 @@
   // https://www.w3.org/TR/css-inline-3/#dominant-baseline-property
 
   // Vertical flow (except 'text-orientation: sideways') uses ideographic
-  // baseline. https://drafts.csswg.org/css-writing-modes-3/#intro-baselines
+  // central baseline.
+  // https://drafts.csswg.org/css-writing-modes-3/#text-baselines
   return !GetFontDescription().IsVerticalAnyUpright() ? kAlphabeticBaseline
-                                                      : kIdeographicBaseline;
+                                                      : kCentralBaseline;
 }
 
 FontHeight ComputedStyle::GetFontHeight(FontBaseline baseline) const {
diff --git a/third_party/blink/renderer/modules/push_messaging/push_event.cc b/third_party/blink/renderer/modules/push_messaging/push_event.cc
index e400b35c..b75aaedc 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_event.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_event.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/push_messaging/push_event.h"
 
 #include "third_party/blink/renderer/bindings/modules/v8/v8_push_event_init.h"
+#include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 
 namespace blink {
 
@@ -22,11 +23,11 @@
     const ArrayBufferOrArrayBufferViewOrUSVString& message_data =
         initializer->data();
     if (message_data.IsArrayBuffer() || message_data.IsArrayBufferView()) {
-      DOMArrayBuffer* buffer =
-          message_data.IsArrayBufferView()
-              ? message_data.GetAsArrayBufferView()->buffer()
-              : message_data.GetAsArrayBuffer();
-      if (!base::CheckedNumeric<uint32_t>(buffer->ByteLength()).IsValid()) {
+      DOMArrayPiece array_piece =
+          message_data.IsArrayBuffer()
+              ? DOMArrayPiece(message_data.GetAsArrayBuffer())
+              : DOMArrayPiece(message_data.GetAsArrayBufferView().Get());
+      if (!base::CheckedNumeric<uint32_t>(array_piece.ByteLength()).IsValid()) {
         exception_state.ThrowRangeError(
             "The provided ArrayBuffer exceeds the maximum supported size "
             "(4294967295)");
diff --git a/third_party/blink/renderer/modules/push_messaging/push_message_data.cc b/third_party/blink/renderer/modules/push_messaging/push_message_data.cc
index 409aa11..24c865b 100644
--- a/third_party/blink/renderer/modules/push_messaging/push_message_data.cc
+++ b/third_party/blink/renderer/modules/push_messaging/push_message_data.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/bindings/modules/v8/array_buffer_or_array_buffer_view_or_usv_string.h"
 #include "third_party/blink/renderer/core/fileapi/blob.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
+#include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
 #include "third_party/blink/renderer/platform/blob/blob_data.h"
@@ -31,16 +32,20 @@
 
 PushMessageData* PushMessageData::Create(
     const ArrayBufferOrArrayBufferViewOrUSVString& message_data) {
-  if (message_data.IsArrayBuffer() || message_data.IsArrayBufferView()) {
-    DOMArrayBuffer* buffer = message_data.IsArrayBufferView()
-                                 ? message_data.GetAsArrayBufferView()->buffer()
-                                 : message_data.GetAsArrayBuffer();
-
+  if (message_data.IsArrayBuffer()) {
+    DOMArrayBuffer* buffer = message_data.GetAsArrayBuffer();
     return MakeGarbageCollected<PushMessageData>(
         static_cast<const char*>(buffer->Data()),
         base::checked_cast<wtf_size_t>(buffer->ByteLength()));
   }
 
+  if (message_data.IsArrayBufferView()) {
+    DOMArrayBufferView* buffer_view = message_data.GetAsArrayBufferView().Get();
+    return MakeGarbageCollected<PushMessageData>(
+        static_cast<const char*>(buffer_view->BaseAddress()),
+        base::checked_cast<wtf_size_t>(buffer_view->byteLength()));
+  }
+
   if (message_data.IsUSVString()) {
     std::string encoded_string = UTF8Encoding().Encode(
         message_data.GetAsUSVString(), WTF::kNoUnencodables);
diff --git a/third_party/blink/renderer/platform/fonts/font_baseline.h b/third_party/blink/renderer/platform/fonts/font_baseline.h
index 89b8ca2..5d2778f 100644
--- a/third_party/blink/renderer/platform/fonts/font_baseline.h
+++ b/third_party/blink/renderer/platform/fonts/font_baseline.h
@@ -33,7 +33,7 @@
   kAlphabeticBaseline,
 
   // https://drafts.csswg.org/css-inline/#central-baseline
-  kIdeographicBaseline,
+  kCentralBaseline,
 
   // https://drafts.csswg.org/css-inline/#text-under-baseline
   kTextUnderBaseline,
diff --git a/third_party/blink/renderer/platform/fonts/font_metrics.cc b/third_party/blink/renderer/platform/fonts/font_metrics.cc
index b5e333a..2ef3e48 100644
--- a/third_party/blink/renderer/platform/fonts/font_metrics.cc
+++ b/third_party/blink/renderer/platform/fonts/font_metrics.cc
@@ -155,7 +155,7 @@
     case kAlphabeticBaseline:
       NOTREACHED();
       return float_ascent_;
-    case kIdeographicBaseline:
+    case kCentralBaseline:
       return FloatHeight() / 2;
 
       // The following computations are based on 'dominant-baseline' support in
@@ -188,7 +188,7 @@
     case kAlphabeticBaseline:
       NOTREACHED();
       return int_ascent_;
-    case kIdeographicBaseline:
+    case kCentralBaseline:
       return Height() - Height() / 2;
 
       // The following computations are based on 'dominant-baseline' support in
diff --git a/third_party/blink/renderer/platform/fonts/shaping/glyph_bounds_accumulator.h b/third_party/blink/renderer/platform/fonts/shaping/glyph_bounds_accumulator.h
index 10d4f23..fcfbeb7 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/glyph_bounds_accumulator.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/glyph_bounds_accumulator.h
@@ -86,12 +86,12 @@
     // Convert physical glyph_bounding_box to logical.
     bounds = bounds.TransposedRect();
 
-    // The glyph bounding box of a vertical run uses ideographic baseline.
-    // Adjust the box Y position because the bounding box of a ShapeResult uses
-    // alphabetic baseline.
+    // The glyph bounding box of a vertical run uses ideographic central
+    // baseline. Adjust the box Y position because the bounding box of a
+    // ShapeResult uses alphabetic baseline.
     // See diagrams of base lines at
     // https://drafts.csswg.org/css-writing-modes-3/#intro-baselines
-    int baseline_adjust = font_metrics.Ascent(kIdeographicBaseline) -
+    int baseline_adjust = font_metrics.Ascent(kCentralBaseline) -
                           font_metrics.Ascent(kAlphabeticBaseline);
     bounds.SetY(bounds.Y() + baseline_adjust);
   }
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h
index ce52b5d..8d1e8cb 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_bloberizer.h
@@ -89,12 +89,11 @@
       CommitPendingRun();
       pending_font_data_ = font_data;
       pending_canvas_rotation_ = canvas_rotation;
+      const auto& metrics = font_data->GetFontMetrics();
       pending_vertical_baseline_x_offset_ =
           !IsCanvasRotationInVerticalUpright(canvas_rotation)
               ? 0
-              : font_data->GetFontMetrics().FloatAscent() -
-                    font_data->GetFontMetrics().FloatAscent(
-                        kIdeographicBaseline);
+              : metrics.FloatAscent() - metrics.FloatAscent(kCentralBaseline);
     }
 
     pending_glyphs_.push_back(glyph);
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
index 3a509ba..18c0ef6 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -158,8 +158,9 @@
     const PendingLayer& pending_layer) {
   if (pending_layer.compositing_type != PendingLayer::kPreCompositedLayer) {
     if (const auto* scroll_translation =
-            ScrollTranslationForLayer(pending_layer))
+            ScrollTranslationForScrollHitTestLayer(pending_layer)) {
       return *scroll_translation;
+    }
   }
 
   const auto& transform = pending_layer.property_tree_state.Transform();
@@ -170,26 +171,28 @@
 }
 
 const TransformPaintPropertyNode*
-PaintArtifactCompositor::ScrollTranslationForLayer(
-    const PendingLayer& pending_layer) {
+PaintArtifactCompositor::ScrollTranslationForScrollHitTestLayer(
+    const PendingLayer& pending_layer) const {
+  // Not checking that the compositing type is
+  // PendingLayer::kCompositedScrollHitTestLayer because a scroll hit test
+  // chunk without a direct compositing reasons can still be composited
+  // (e.g. when it can't be merged into any other layer).
   DCHECK_NE(pending_layer.compositing_type, PendingLayer::kPreCompositedLayer);
-  // Not checking PendingLayer::kScrollHitTestLayer because a scroll hit test
-  // chunk without a direct compositing reasons can still be composited (e.g.
-  // when it can't be merged into any other layer).
+
   if (pending_layer.chunks.size() != 1)
     return nullptr;
 
   const auto& paint_chunk = pending_layer.FirstPaintChunk();
   if (!paint_chunk.hit_test_data)
     return nullptr;
-
   return paint_chunk.hit_test_data->scroll_translation;
 }
 
 scoped_refptr<cc::Layer>
 PaintArtifactCompositor::ScrollHitTestLayerForPendingLayer(
     const PendingLayer& pending_layer) {
-  const auto* scroll_translation = ScrollTranslationForLayer(pending_layer);
+  const auto* scroll_translation =
+      ScrollTranslationForScrollHitTestLayer(pending_layer);
   if (!scroll_translation)
     return nullptr;
 
@@ -197,16 +200,14 @@
   DCHECK_EQ(FloatPoint(), pending_layer.offset_of_decomposited_transforms);
 
   const auto& scroll_node = *scroll_translation->ScrollNode();
-  auto scroll_element_id = scroll_node.GetCompositorElementId();
 
-  scoped_refptr<cc::Layer> scroll_layer;
-  for (auto& existing_layer : scroll_hit_test_layers_) {
-    if (existing_layer && existing_layer->element_id() == scroll_element_id)
-      scroll_layer = existing_layer;
-  }
-  if (!scroll_layer) {
+  scoped_refptr<cc::Layer> scroll_layer =
+      ExistingScrollHitTestLayerForPendingLayer(pending_layer);
+  if (scroll_layer) {
+    DCHECK_EQ(scroll_layer->element_id(), scroll_node.GetCompositorElementId());
+  } else {
     scroll_layer = cc::Layer::Create();
-    scroll_layer->SetElementId(scroll_element_id);
+    scroll_layer->SetElementId(scroll_node.GetCompositorElementId());
     scroll_layer->SetHitTestable(true);
   }
 
@@ -230,6 +231,23 @@
   return scroll_layer;
 }
 
+scoped_refptr<cc::Layer>
+PaintArtifactCompositor::ExistingScrollHitTestLayerForPendingLayer(
+    const PendingLayer& pending_layer) const {
+  const auto* scroll_translation =
+      ScrollTranslationForScrollHitTestLayer(pending_layer);
+  if (!scroll_translation)
+    return nullptr;
+
+  const auto& scroll_node = *scroll_translation->ScrollNode();
+  auto scroll_element_id = scroll_node.GetCompositorElementId();
+  for (auto& existing_layer : scroll_hit_test_layers_) {
+    if (existing_layer && existing_layer->element_id() == scroll_element_id)
+      return existing_layer;
+  }
+  return nullptr;
+}
+
 scoped_refptr<cc::ScrollbarLayerBase>
 PaintArtifactCompositor::ScrollbarLayerForPendingLayer(
     const PendingLayer& pending_layer) {
@@ -922,7 +940,7 @@
       // Case A: The next chunk belongs to the current group but no subgroup.
       PendingLayer::CompositingType compositing_type = PendingLayer::kOther;
       if (IsCompositedScrollHitTest(*chunk_cursor)) {
-        compositing_type = PendingLayer::kScrollHitTestLayer;
+        compositing_type = PendingLayer::kCompositedScrollHitTestLayer;
       } else if (chunk_cursor->size()) {
         const auto& first_display_item = *chunk_cursor.DisplayItems().begin();
         if (first_display_item.IsForeignLayer())
@@ -1224,8 +1242,10 @@
       // The scroll translation node of a scroll hit test layer may not be
       // referenced by any pending layer's property tree state. Disallow
       // decomposition of it (and its ancestors).
-      if (const auto* translation = ScrollTranslationForLayer(pending_layer))
+      if (const auto* translation =
+              ScrollTranslationForScrollHitTestLayer(pending_layer)) {
         mark_not_decompositable(translation);
+      }
     }
   }
 
@@ -1468,25 +1488,18 @@
         return;
       layer = &pending_layer.graphics_layer->CcLayer();
       break;
-    case PendingLayer::kScrollHitTestLayer: {
-      // TODO(pdr): Share this code with ScrollHitTestLayerForPendingLayer.
-      const auto* scroll_translation = ScrollTranslationForLayer(pending_layer);
-      DCHECK(scroll_translation);
-      const auto& scroll_node = *scroll_translation->ScrollNode();
-      auto scroll_element_id = scroll_node.GetCompositorElementId();
-      for (auto& existing_layer : scroll_hit_test_layers_) {
-        if (existing_layer->element_id() == scroll_element_id) {
-          layer = existing_layer.get();
-          break;
-        }
-      }
-    } break;
     case PendingLayer::kScrollbarLayer: {
       // TODO(pdr): Share this code with ScrollbarLayerForPendingLayer.
       const auto& item = pending_layer.FirstDisplayItem();
       layer = ScrollbarLayer(To<ScrollbarDisplayItem>(item).ElementId());
     } break;
     default: {
+      if (scoped_refptr<cc::Layer> scroll_layer =
+              ExistingScrollHitTestLayerForPendingLayer(pending_layer)) {
+        layer = scroll_layer.get();
+        break;
+      }
+
       ContentLayerClientImpl* content_layer_client = nullptr;
       const auto& first_chunk = pending_layer.FirstPaintChunk();
       for (auto& client : content_layer_clients_) {
@@ -1866,10 +1879,6 @@
     cc::Layer* layer;
     RasterInvalidationTracking* tracking = nullptr;
     switch (pending_layer.compositing_type) {
-      case PendingLayer::kScrollHitTestLayer:
-        layer = scroll_hit_test_layer_it->get();
-        ++scroll_hit_test_layer_it;
-        break;
       case PendingLayer::kPreCompositedLayer:
         tracking =
             pending_layer.graphics_layer->GetRasterInvalidationTracking();
@@ -1884,6 +1893,14 @@
         ++scrollbar_layer_it;
         break;
       default:
+        if (scoped_refptr<cc::Layer> scroll_layer =
+                ExistingScrollHitTestLayerForPendingLayer(pending_layer)) {
+          DCHECK_EQ(scroll_layer, scroll_hit_test_layer_it->get());
+          layer = scroll_hit_test_layer_it->get();
+          ++scroll_hit_test_layer_it;
+          break;
+        }
+
         tracking =
             (*content_layer_client_it)->GetRasterInvalidator().GetTracking();
         layer = &(*content_layer_client_it)->Layer();
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
index 18a7cef..262a345 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -261,7 +261,9 @@
   // the same cc::Layer.
   struct PLATFORM_EXPORT PendingLayer {
     enum CompositingType {
-      kScrollHitTestLayer,
+      // This type is only for scroll hit test layers that have direct
+      // compositing reasons.
+      kCompositedScrollHitTestLayer,
       kPreCompositedLayer,
       kForeignLayer,
       kScrollbarLayer,
@@ -388,8 +390,8 @@
 
   // If the pending layer has scroll hit test data, return the associated
   // scroll translation node.
-  const TransformPaintPropertyNode* ScrollTranslationForLayer(
-      const PendingLayer&);
+  const TransformPaintPropertyNode* ScrollTranslationForScrollHitTestLayer(
+      const PendingLayer&) const;
 
   // Returns the cc::Layer if the pending layer contains a foreign layer or a
   // wrapper of a GraphicsLayer. If it's the latter and the graphics layer has
@@ -401,6 +403,11 @@
   scoped_refptr<cc::Layer> ScrollHitTestLayerForPendingLayer(
       const PendingLayer&);
 
+  // Finds an existing scroll hit test layer for the pending layer, returning
+  // nullptr if none exists.
+  scoped_refptr<cc::Layer> ExistingScrollHitTestLayerForPendingLayer(
+      const PendingLayer&) const;
+
   // Finds an existing or creates a new scrollbar layer for the pending layer,
   // returning nullptr if the layer is not a scrollbar layer.
   scoped_refptr<cc::ScrollbarLayerBase> ScrollbarLayerForPendingLayer(
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
index 32ad106..19fd878 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor_test.cc
@@ -4993,4 +4993,22 @@
             graphics_layer.CcLayer().scroll_tree_index());
 }
 
+TEST_P(PaintArtifactCompositorTest, RepaintIndirectScrollHitTest) {
+  CompositorElementId scroll_element_id = ScrollElementId(2);
+  auto scroll = CreateScroll(ScrollPaintPropertyNode::Root(), ScrollState1(),
+                             kNotScrollingOnMain, scroll_element_id);
+  auto scroll_translation = CreateScrollTranslation(t0(), 7, 9, *scroll);
+
+  TestPaintArtifact test_artifact;
+  CreateScrollableChunk(test_artifact, *scroll_translation, c0(), e0());
+  auto artifact = test_artifact.Build();
+  Update(artifact);
+  scroll_translation->ClearChangedToRoot();
+
+  Vector<PreCompositedLayerInfo> pre_composited_layers = {
+      {PaintChunkSubset(artifact)}};
+  GetPaintArtifactCompositor().UpdateRepaintedLayers(pre_composited_layers);
+  // This test passes if no CHECK occurs.
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
index a46aa93..5db8e262 100644
--- a/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
+++ b/third_party/blink/renderer/platform/wtf/allocator/partitions.cc
@@ -48,7 +48,7 @@
 const char* const Partitions::kAllocatedObjectPoolName =
     "partition_alloc/allocated_objects";
 
-#if PA_ALLOW_PCSCAN
+#if defined(PA_ALLOW_PCSCAN)
 // Runs PCScan on WTF partitions.
 const base::Feature kPCScanBlinkPartitions{"PCScanBlinkPartitions",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
@@ -113,7 +113,7 @@
   buffer_root_ = buffer_allocator->root();
   layout_root_ = layout_allocator->root();
 
-#if PA_ALLOW_PCSCAN
+#if defined(PA_ALLOW_PCSCAN)
   if (base::FeatureList::IsEnabled(base::features::kPartitionAllocPCScan) ||
       base::FeatureList::IsEnabled(kPCScanBlinkPartitions)) {
     base::internal::PCScan::RegisterNonScannableRoot(array_buffer_root_);
@@ -122,7 +122,7 @@
 #endif
     base::internal::PCScan::RegisterScannableRoot(buffer_root_);
   }
-#endif
+#endif  // defined(PA_ALLOW_PCSCAN)
 
   initialized_ = true;
   return initialized_;
diff --git a/third_party/blink/web_tests/MSANExpectations b/third_party/blink/web_tests/MSANExpectations
index 76bc5196..a62db40 100644
--- a/third_party/blink/web_tests/MSANExpectations
+++ b/third_party/blink/web_tests/MSANExpectations
@@ -185,3 +185,6 @@
 # Sheriff 2021-05-31
 crbug.com/1214884 [ Linux ] external/wpt/html/webappapis/scripting/events/event-handler-attributes-body-window.html [ Pass Timeout ]
 crbug.com/1214884 [ Linux ] external/wpt/html/webappapis/scripting/events/event-handler-all-global-events.html [ Pass Timeout ]
+
+# Sheriff 2021-06-02
+crbug.com/1215390 [ Linux ] external/wpt/pointerevents/pointerevent_pointerId_scope.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/external/wpt/mediasession/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/mediasession/idlharness.window-expected.txt
deleted file mode 100644
index d9833ecf..0000000
--- a/third_party/blink/web_tests/external/wpt/mediasession/idlharness.window-expected.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-This is a testharness.js-based test.
-Found 55 tests; 49 PASS, 6 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS idl_test setup
-PASS idl_test validation
-PASS Partial interface Navigator: original interface defined
-PASS Partial interface Navigator: valid exposure set
-PASS Partial interface Navigator: member names are unique
-PASS Partial interface mixin NavigatorID: member names are unique
-PASS Navigator includes NavigatorID: member names are unique
-PASS Navigator includes NavigatorLanguage: member names are unique
-PASS Navigator includes NavigatorOnLine: member names are unique
-PASS Navigator includes NavigatorContentUtils: member names are unique
-PASS Navigator includes NavigatorCookies: member names are unique
-PASS Navigator includes NavigatorPlugins: member names are unique
-PASS Navigator includes NavigatorConcurrentHardware: member names are unique
-PASS MediaSession interface: existence and properties of interface object
-PASS MediaSession interface object length
-PASS MediaSession interface object name
-PASS MediaSession interface: existence and properties of interface prototype object
-PASS MediaSession interface: existence and properties of interface prototype object's "constructor" property
-PASS MediaSession interface: existence and properties of interface prototype object's @@unscopables property
-PASS MediaSession interface: attribute metadata
-PASS MediaSession interface: attribute playbackState
-PASS MediaSession interface: operation setActionHandler(MediaSessionAction, MediaSessionActionHandler?)
-PASS MediaSession interface: operation setPositionState(optional MediaPositionState)
-FAIL MediaSession interface: operation setMicrophoneActive(boolean) assert_own_property: interface prototype object missing non-static operation expected property "setMicrophoneActive" missing
-FAIL MediaSession interface: operation setCameraActive(boolean) assert_own_property: interface prototype object missing non-static operation expected property "setCameraActive" missing
-PASS MediaSession must be primary interface of navigator.mediaSession
-PASS Stringification of navigator.mediaSession
-PASS MediaSession interface: navigator.mediaSession must inherit property "metadata" with the proper type
-PASS MediaSession interface: navigator.mediaSession must inherit property "playbackState" with the proper type
-PASS MediaSession interface: navigator.mediaSession must inherit property "setActionHandler(MediaSessionAction, MediaSessionActionHandler?)" with the proper type
-PASS MediaSession interface: calling setActionHandler(MediaSessionAction, MediaSessionActionHandler?) on navigator.mediaSession with too few arguments must throw TypeError
-PASS MediaSession interface: navigator.mediaSession must inherit property "setPositionState(optional MediaPositionState)" with the proper type
-PASS MediaSession interface: calling setPositionState(optional MediaPositionState) on navigator.mediaSession with too few arguments must throw TypeError
-FAIL MediaSession interface: navigator.mediaSession must inherit property "setMicrophoneActive(boolean)" with the proper type assert_inherits: property "setMicrophoneActive" not found in prototype chain
-FAIL MediaSession interface: calling setMicrophoneActive(boolean) on navigator.mediaSession with too few arguments must throw TypeError assert_inherits: property "setMicrophoneActive" not found in prototype chain
-FAIL MediaSession interface: navigator.mediaSession must inherit property "setCameraActive(boolean)" with the proper type assert_inherits: property "setCameraActive" not found in prototype chain
-FAIL MediaSession interface: calling setCameraActive(boolean) on navigator.mediaSession with too few arguments must throw TypeError assert_inherits: property "setCameraActive" not found in prototype chain
-PASS MediaMetadata interface: existence and properties of interface object
-PASS MediaMetadata interface object length
-PASS MediaMetadata interface object name
-PASS MediaMetadata interface: existence and properties of interface prototype object
-PASS MediaMetadata interface: existence and properties of interface prototype object's "constructor" property
-PASS MediaMetadata interface: existence and properties of interface prototype object's @@unscopables property
-PASS MediaMetadata interface: attribute title
-PASS MediaMetadata interface: attribute artist
-PASS MediaMetadata interface: attribute album
-PASS MediaMetadata interface: attribute artwork
-PASS MediaMetadata must be primary interface of new MediaMetadata()
-PASS Stringification of new MediaMetadata()
-PASS MediaMetadata interface: new MediaMetadata() must inherit property "title" with the proper type
-PASS MediaMetadata interface: new MediaMetadata() must inherit property "artist" with the proper type
-PASS MediaMetadata interface: new MediaMetadata() must inherit property "album" with the proper type
-PASS MediaMetadata interface: new MediaMetadata() must inherit property "artwork" with the proper type
-PASS Navigator interface: attribute mediaSession
-PASS Navigator interface: navigator must inherit property "mediaSession" with the proper type
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index c2b2a10..371c015 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -4432,6 +4432,8 @@
     getter playbackState
     method constructor
     method setActionHandler
+    method setCameraActive
+    method setMicrophoneActive
     method setPositionState
     setter metadata
     setter playbackState
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 440b635..55a0d7b 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -5341,6 +5341,8 @@
     getter playbackState
     method constructor
     method setActionHandler
+    method setCameraActive
+    method setMicrophoneActive
     method setPositionState
     setter metadata
     setter playbackState
diff --git a/third_party/grpc/BUILD.gn b/third_party/grpc/BUILD.gn
index 1d782b91..0c194f8 100644
--- a/third_party/grpc/BUILD.gn
+++ b/third_party/grpc/BUILD.gn
@@ -41,7 +41,10 @@
     "GRPC_CALLBACK_API_NONEXPERIMENTAL",
   ]
 
-  cflags = [ "-Wno-implicit-fallthrough" ]
+  cflags = [
+    "-Wno-implicit-fallthrough",
+    "-Wno-thread-safety-analysis",
+  ]
 
   if (is_android) {
     libs = [ "log" ]  # For __android_log_write
@@ -190,6 +193,10 @@
     "src/include/grpc/byte_buffer_reader.h",
     "src/include/grpc/census.h",
     "src/include/grpc/compression.h",
+    "src/include/grpc/event_engine/channel_args.h",
+    "src/include/grpc/event_engine/event_engine.h",
+    "src/include/grpc/event_engine/port.h",
+    "src/include/grpc/event_engine/slice_allocator.h",
     "src/include/grpc/fork.h",
     "src/include/grpc/grpc.h",
     "src/include/grpc/grpc_posix.h",
@@ -284,6 +291,7 @@
     "src/include/grpcpp/impl/codegen/message_allocator.h",
     "src/include/grpcpp/impl/codegen/metadata_map.h",
     "src/include/grpcpp/impl/codegen/method_handler.h",
+    "src/include/grpcpp/impl/codegen/method_handler_impl.h",
     "src/include/grpcpp/impl/codegen/proto_buffer_reader.h",
     "src/include/grpcpp/impl/codegen/proto_buffer_writer.h",
     "src/include/grpcpp/impl/codegen/proto_utils.h",
@@ -378,6 +386,8 @@
     "src/src/core/ext/filters/client_channel/resolver_factory.h",
     "src/src/core/ext/filters/client_channel/resolver_registry.h",
     "src/src/core/ext/filters/client_channel/resolver_result_parsing.h",
+    "src/src/core/ext/filters/client_channel/retry_filter.h",
+    "src/src/core/ext/filters/client_channel/retry_service_config.h",
     "src/src/core/ext/filters/client_channel/retry_throttle.h",
     "src/src/core/ext/filters/client_channel/server_address.h",
     "src/src/core/ext/filters/client_channel/service_config.h",
@@ -387,6 +397,8 @@
     "src/src/core/ext/filters/client_channel/subchannel_interface.h",
     "src/src/core/ext/filters/client_channel/subchannel_pool_interface.h",
     "src/src/core/ext/filters/deadline/deadline_filter.h",
+    "src/src/core/ext/filters/fault_injection/fault_injection_filter.h",
+    "src/src/core/ext/filters/fault_injection/service_config_parser.h",
     "src/src/core/ext/filters/http/client/http_client_filter.h",
     "src/src/core/ext/filters/http/client_authority_filter.h",
     "src/src/core/ext/filters/http/message_compress/message_compress_filter.h",
@@ -422,9 +434,11 @@
     "src/src/core/ext/transport/chttp2/transport/stream_map.h",
     "src/src/core/ext/transport/chttp2/transport/varint.h",
     "src/src/core/ext/transport/inproc/inproc_transport.h",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/config_dump.upb.h",
     "src/src/core/ext/upb-generated/envoy/annotations/deprecation.upb.h",
     "src/src/core/ext/upb-generated/envoy/annotations/resource.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/accesslog/v3/accesslog.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/bootstrap/v3/bootstrap.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/circuit_breaker.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/filter.upb.h",
@@ -449,12 +463,17 @@
     "src/src/core/ext/upb-generated/envoy/config/listener/v3/listener.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/listener/v3/listener_components.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/listener/v3/udp_listener_config.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/metrics/v3/stats.upb.h",
+    "src/src/core/ext/upb-generated/envoy/config/overload/v3/overload.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/route/v3/route.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/route/v3/route_components.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/route/v3/scoped_route.upb.h",
     "src/src/core/ext/upb-generated/envoy/config/trace/v3/http_tracer.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/clusters/aggregate/v3/cluster.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/common/fault/v3/fault.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/fault/v3/fault.upb.h",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/router/v3/router.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/cert.upb.h",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.h",
@@ -468,11 +487,14 @@
     "src/src/core/ext/upb-generated/envoy/service/load_stats/v3/lrs.upb.h",
     "src/src/core/ext/upb-generated/envoy/service/route/v3/rds.upb.h",
     "src/src/core/ext/upb-generated/envoy/service/route/v3/srds.upb.h",
+    "src/src/core/ext/upb-generated/envoy/service/status/v3/csds.upb.h",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/metadata.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/node.upb.h",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/number.upb.h",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/path.upb.h",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/regex.upb.h",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/string.upb.h",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/struct.upb.h",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/value.upb.h",
     "src/src/core/ext/upb-generated/envoy/type/metadata/v3/metadata.upb.h",
     "src/src/core/ext/upb-generated/envoy/type/tracing/v3/custom_tag.upb.h",
@@ -502,6 +524,7 @@
     "src/src/core/ext/upb-generated/udpa/annotations/status.upb.h",
     "src/src/core/ext/upb-generated/udpa/annotations/versioning.upb.h",
     "src/src/core/ext/upb-generated/udpa/data/orca/v1/orca_load_report.upb.h",
+    "src/src/core/ext/upb-generated/udpa/type/v1/typed_struct.upb.h",
     "src/src/core/ext/upb-generated/validate/validate.upb.h",
     "src/src/core/ext/upb-generated/xds/core/v3/authority.upb.h",
     "src/src/core/ext/upb-generated/xds/core/v3/collection_entry.upb.h",
@@ -509,9 +532,11 @@
     "src/src/core/ext/upb-generated/xds/core/v3/resource.upb.h",
     "src/src/core/ext/upb-generated/xds/core/v3/resource_locator.upb.h",
     "src/src/core/ext/upb-generated/xds/core/v3/resource_name.upb.h",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/config_dump.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/annotations/deprecation.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/annotations/resource.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/accesslog/v3/accesslog.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/bootstrap/v3/bootstrap.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/circuit_breaker.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/cluster.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/filter.upbdefs.h",
@@ -536,11 +561,16 @@
     "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/listener.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/listener_components.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/udp_listener_config.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/metrics/v3/stats.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/config/overload/v3/overload.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route_components.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/scoped_route.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/http_tracer.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/clusters/aggregate/v3/cluster.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/common/fault/v3/fault.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/fault/v3/fault.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/router/v3/router.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/cert.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/common.upbdefs.h",
@@ -554,11 +584,14 @@
     "src/src/core/ext/upbdefs-generated/envoy/service/load_stats/v3/lrs.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/service/route/v3/rds.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/service/route/v3/srds.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/service/status/v3/csds.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/metadata.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/node.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/number.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/path.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/regex.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/string.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/struct.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/value.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/type/metadata/v3/metadata.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/envoy/type/tracing/v3/custom_tag.upbdefs.h",
@@ -580,6 +613,7 @@
     "src/src/core/ext/upbdefs-generated/udpa/annotations/sensitive.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/status.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/versioning.upbdefs.h",
+    "src/src/core/ext/upbdefs-generated/udpa/type/v1/typed_struct.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/validate/validate.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/authority.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/collection_entry.upbdefs.h",
@@ -587,6 +621,8 @@
     "src/src/core/ext/upbdefs-generated/xds/core/v3/resource.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_locator.upbdefs.h",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_name.upbdefs.h",
+    "src/src/core/lib/address_utils/parse_address.h",
+    "src/src/core/lib/address_utils/sockaddr_utils.h",
     "src/src/core/lib/avl/avl.h",
     "src/src/core/lib/backoff/backoff.h",
     "src/src/core/lib/channel/channel_args.h",
@@ -644,6 +680,8 @@
     "src/src/core/lib/gprpp/ref_counted.h",
     "src/src/core/lib/gprpp/ref_counted_ptr.h",
     "src/src/core/lib/gprpp/stat.h",
+
+    # "src/src/core/lib/gprpp/status_helper.h",
     "src/src/core/lib/gprpp/sync.h",
     "src/src/core/lib/gprpp/thd.h",
     "src/src/core/lib/gprpp/time_util.h",
@@ -683,8 +721,6 @@
     "src/src/core/lib/iomgr/load_file.h",
     "src/src/core/lib/iomgr/lockfree_event.h",
     "src/src/core/lib/iomgr/nameser.h",
-    "src/src/core/lib/iomgr/parse_address.h",
-    "src/src/core/lib/iomgr/poller/eventmanager_libuv.h",
     "src/src/core/lib/iomgr/polling_entity.h",
     "src/src/core/lib/iomgr/pollset.h",
     "src/src/core/lib/iomgr/pollset_custom.h",
@@ -701,7 +737,6 @@
     "src/src/core/lib/iomgr/sockaddr.h",
     "src/src/core/lib/iomgr/sockaddr_custom.h",
     "src/src/core/lib/iomgr/sockaddr_posix.h",
-    "src/src/core/lib/iomgr/sockaddr_utils.h",
     "src/src/core/lib/iomgr/sockaddr_windows.h",
     "src/src/core/lib/iomgr/socket_factory_posix.h",
     "src/src/core/lib/iomgr/socket_mutator.h",
@@ -729,17 +764,9 @@
     "src/src/core/lib/iomgr/work_serializer.h",
     "src/src/core/lib/json/json.h",
     "src/src/core/lib/json/json_util.h",
-    "src/src/core/lib/profiling/timers.h",
-    "src/src/core/lib/security/authorization/authorization_engine.h",
-    "src/src/core/lib/security/authorization/evaluate_args.h",
 
-    # "src/src/core/lib/security/authorization/matchers.h",
-    "src/src/core/lib/security/authorization/mock_cel/activation.h",
-    "src/src/core/lib/security/authorization/mock_cel/cel_expr_builder_factory.h",
-    "src/src/core/lib/security/authorization/mock_cel/cel_expression.h",
-    "src/src/core/lib/security/authorization/mock_cel/cel_value.h",
-    "src/src/core/lib/security/authorization/mock_cel/evaluator_core.h",
-    "src/src/core/lib/security/authorization/mock_cel/flat_expr_builder.h",
+    # "src/src/core/lib/matchers/matchers.h",
+    "src/src/core/lib/profiling/timers.h",
     "src/src/core/lib/security/context/security_context.h",
     "src/src/core/lib/security/credentials/alts/alts_credentials.h",
     "src/src/core/lib/security/credentials/alts/check_gcp_environment.h",
@@ -856,6 +883,7 @@
     "src/src/cpp/server/secure_server_credentials.h",
     "src/src/cpp/server/thread_pool_interface.h",
     "src/src/cpp/thread_manager/thread_manager.h",
+    "src/third_party/xxhash/xxhash.h",
   ]
 
   deps = [
@@ -924,6 +952,8 @@
     "src/src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc",
     "src/src/core/ext/filters/client_channel/resolver_registry.cc",
     "src/src/core/ext/filters/client_channel/resolver_result_parsing.cc",
+    "src/src/core/ext/filters/client_channel/retry_filter.cc",
+    "src/src/core/ext/filters/client_channel/retry_service_config.cc",
     "src/src/core/ext/filters/client_channel/retry_throttle.cc",
     "src/src/core/ext/filters/client_channel/server_address.cc",
     "src/src/core/ext/filters/client_channel/service_config.cc",
@@ -933,6 +963,7 @@
     "src/src/core/ext/filters/client_channel/subchannel_pool_interface.cc",
     "src/src/core/ext/filters/client_idle/client_idle_filter.cc",
     "src/src/core/ext/filters/deadline/deadline_filter.cc",
+    "src/src/core/ext/filters/fault_injection/fault_injection_filter.cc",
     "src/src/core/ext/filters/http/client/http_client_filter.cc",
     "src/src/core/ext/filters/http/client_authority_filter.cc",
     "src/src/core/ext/filters/http/http_filters_plugin.cc",
@@ -978,9 +1009,11 @@
     "src/src/core/ext/transport/chttp2/transport/writing.cc",
     "src/src/core/ext/transport/inproc/inproc_plugin.cc",
     "src/src/core/ext/transport/inproc/inproc_transport.cc",
+    "src/src/core/ext/upb-generated/envoy/admin/v3/config_dump.upb.c",
     "src/src/core/ext/upb-generated/envoy/annotations/deprecation.upb.c",
     "src/src/core/ext/upb-generated/envoy/annotations/resource.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/accesslog/v3/accesslog.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/bootstrap/v3/bootstrap.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/circuit_breaker.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/cluster.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/cluster/v3/filter.upb.c",
@@ -1005,11 +1038,15 @@
     "src/src/core/ext/upb-generated/envoy/config/listener/v3/listener.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/listener/v3/listener_components.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/listener/v3/udp_listener_config.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/metrics/v3/stats.upb.c",
+    "src/src/core/ext/upb-generated/envoy/config/overload/v3/overload.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/rbac/v3/rbac.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/route/v3/route.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/route/v3/route_components.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/route/v3/scoped_route.upb.c",
     "src/src/core/ext/upb-generated/envoy/config/trace/v3/http_tracer.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/common/fault/v3/fault.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/router/v3/router.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/cert.upb.c",
     "src/src/core/ext/upb-generated/envoy/extensions/transport_sockets/tls/v3/common.upb.c",
@@ -1023,20 +1060,22 @@
     "src/src/core/ext/upb-generated/envoy/service/load_stats/v3/lrs.upb.c",
     "src/src/core/ext/upb-generated/envoy/service/route/v3/rds.upb.c",
     "src/src/core/ext/upb-generated/envoy/service/route/v3/srds.upb.c",
+    "src/src/core/ext/upb-generated/envoy/service/status/v3/csds.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/metadata.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/node.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/number.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/path.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/regex.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/string.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/matcher/v3/value.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/tracing/v3/custom_tag.upb.c",
-    "src/src/core/ext/upb-generated/envoy/type/v3/http.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/percent.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/range.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/v3/semantic_version.upb.c",
     "src/src/core/ext/upb-generated/google/api/annotations.upb.c",
     "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/checked.upb.c",
     "src/src/core/ext/upb-generated/google/api/expr/v1alpha1/syntax.upb.c",
+    "src/src/core/ext/upb-generated/google/api/http.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/any.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/duration.upb.c",
     "src/src/core/ext/upb-generated/google/protobuf/empty.upb.c",
@@ -1054,15 +1093,18 @@
     "src/src/core/ext/upb-generated/udpa/annotations/sensitive.upb.c",
     "src/src/core/ext/upb-generated/udpa/annotations/versioning.upb.c",
     "src/src/core/ext/upb-generated/udpa/data/orca/v1/orca_load_report.upb.c",
+    "src/src/core/ext/upb-generated/udpa/type/v1/typed_struct.upb.c",
     "src/src/core/ext/upb-generated/validate/validate.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/authority.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/collection_entry.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/context_params.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/resource_locator.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/resource_name.upb.c",
+    "src/src/core/ext/upbdefs-generated/envoy/admin/v3/config_dump.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/annotations/deprecation.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/annotations/resource.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/accesslog/v3/accesslog.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/bootstrap/v3/bootstrap.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/circuit_breaker.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/cluster.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/cluster/v3/filter.upbdefs.c",
@@ -1087,10 +1129,14 @@
     "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/listener.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/listener_components.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/listener/v3/udp_listener_config.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/metrics/v3/stats.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/config/overload/v3/overload.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/route_components.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/route/v3/scoped_route.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/config/trace/v3/http_tracer.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/common/fault/v3/fault.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/router/v3/router.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/cert.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/transport_sockets/tls/v3/common.upbdefs.c",
@@ -1104,11 +1150,14 @@
     "src/src/core/ext/upbdefs-generated/envoy/service/load_stats/v3/lrs.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/service/route/v3/rds.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/service/route/v3/srds.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/service/status/v3/csds.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/metadata.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/node.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/number.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/path.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/regex.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/string.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/struct.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/matcher/v3/value.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/tracing/v3/custom_tag.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/v3/http.upbdefs.c",
@@ -1119,7 +1168,6 @@
     "src/src/core/ext/upbdefs-generated/google/protobuf/any.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/duration.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/empty.upbdefs.c",
-    "src/src/core/ext/upbdefs-generated/google/protobuf/struct.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/timestamp.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/protobuf/wrappers.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/rpc/status.upbdefs.c",
@@ -1127,12 +1175,15 @@
     "src/src/core/ext/upbdefs-generated/udpa/annotations/security.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/sensitive.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/versioning.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/udpa/type/v1/typed_struct.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/validate/validate.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/authority.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/collection_entry.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/context_params.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_locator.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/resource_name.upbdefs.c",
+    "src/src/core/lib/address_utils/parse_address.cc",
+    "src/src/core/lib/address_utils/sockaddr_utils.cc",
     "src/src/core/lib/avl/avl.cc",
     "src/src/core/lib/backoff/backoff.cc",
     "src/src/core/lib/channel/channel_args.cc",
@@ -1155,6 +1206,8 @@
     "src/src/core/lib/debug/stats.cc",
     "src/src/core/lib/debug/stats_data.cc",
     "src/src/core/lib/debug/trace.cc",
+    "src/src/core/lib/event_engine/slice_allocator.cc",
+    "src/src/core/lib/event_engine/sockaddr.cc",
     "src/src/core/lib/gpr/alloc.cc",
     "src/src/core/lib/gpr/atm.cc",
     "src/src/core/lib/gpr/cpu_iphone.cc",
@@ -1198,6 +1251,8 @@
     "src/src/core/lib/gprpp/mpscq.cc",
     "src/src/core/lib/gprpp/stat_posix.cc",
     "src/src/core/lib/gprpp/stat_windows.cc",
+
+    # "src/src/core/lib/gprpp/status_helper.cc",
     "src/src/core/lib/gprpp/thd_posix.cc",
     "src/src/core/lib/gprpp/thd_windows.cc",
     "src/src/core/lib/gprpp/time_util.cc",
@@ -1246,8 +1301,6 @@
     "src/src/core/lib/iomgr/is_epollexclusive_available.cc",
     "src/src/core/lib/iomgr/load_file.cc",
     "src/src/core/lib/iomgr/lockfree_event.cc",
-    "src/src/core/lib/iomgr/parse_address.cc",
-    "src/src/core/lib/iomgr/poller/eventmanager_libuv.cc",
     "src/src/core/lib/iomgr/polling_entity.cc",
     "src/src/core/lib/iomgr/pollset.cc",
     "src/src/core/lib/iomgr/pollset_custom.cc",
@@ -1261,7 +1314,6 @@
     "src/src/core/lib/iomgr/resolve_address_posix.cc",
     "src/src/core/lib/iomgr/resolve_address_windows.cc",
     "src/src/core/lib/iomgr/resource_quota.cc",
-    "src/src/core/lib/iomgr/sockaddr_utils.cc",
     "src/src/core/lib/iomgr/socket_factory_posix.cc",
     "src/src/core/lib/iomgr/socket_mutator.cc",
     "src/src/core/lib/iomgr/socket_utils_common_posix.cc",
@@ -1304,12 +1356,10 @@
     "src/src/core/lib/json/json_reader.cc",
     "src/src/core/lib/json/json_util.cc",
     "src/src/core/lib/json/json_writer.cc",
+
+    # "src/src/core/lib/matchers/matchers.cc",
     "src/src/core/lib/profiling/basic_timers.cc",
     "src/src/core/lib/profiling/stap_timers.cc",
-    "src/src/core/lib/security/authorization/authorization_engine.cc",
-    "src/src/core/lib/security/authorization/evaluate_args.cc",
-
-    # "src/src/core/lib/security/authorization/matchers.cc",
     "src/src/core/lib/security/context/security_context.cc",
     "src/src/core/lib/security/credentials/alts/alts_credentials.cc",
     "src/src/core/lib/security/credentials/alts/check_gcp_environment.cc",
@@ -1506,14 +1556,19 @@
 
 source_set("grpc++_repeated") {
   sources = [
+    "src/src/core/ext/filters/fault_injection/service_config_parser.cc",
     "src/src/core/ext/upb-generated/envoy/extensions/clusters/aggregate/v3/cluster.upb.c",
+    "src/src/core/ext/upb-generated/envoy/extensions/filters/http/fault/v3/fault.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/matcher/v3/struct.upb.c",
     "src/src/core/ext/upb-generated/envoy/type/metadata/v3/metadata.upb.c",
-    "src/src/core/ext/upb-generated/google/api/http.upb.c",
+    "src/src/core/ext/upb-generated/envoy/type/v3/http.upb.c",
     "src/src/core/ext/upb-generated/udpa/annotations/status.upb.c",
     "src/src/core/ext/upb-generated/xds/core/v3/resource.upb.c",
     "src/src/core/ext/upbdefs-generated/envoy/extensions/clusters/aggregate/v3/cluster.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/envoy/extensions/filters/http/fault/v3/fault.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/envoy/type/metadata/v3/metadata.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/google/api/http.upbdefs.c",
+    "src/src/core/ext/upbdefs-generated/google/protobuf/struct.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/udpa/annotations/status.upbdefs.c",
     "src/src/core/ext/upbdefs-generated/xds/core/v3/resource.upbdefs.c",
     "src/src/core/lib/security/util/json_util.cc",
@@ -1614,10 +1669,6 @@
     "src/third_party/upb/upb/def.hpp",
     "src/third_party/upb/upb/encode.c",
     "src/third_party/upb/upb/encode.h",
-    "src/third_party/upb/upb/json_decode.c",
-    "src/third_party/upb/upb/json_decode.h",
-    "src/third_party/upb/upb/json_encode.c",
-    "src/third_party/upb/upb/json_encode.h",
     "src/third_party/upb/upb/msg.c",
     "src/third_party/upb/upb/msg.h",
     "src/third_party/upb/upb/port_def.inc",
diff --git a/third_party/grpc/README.chromium b/third_party/grpc/README.chromium
index 2180f32..8ac7146 100644
--- a/third_party/grpc/README.chromium
+++ b/third_party/grpc/README.chromium
@@ -1,11 +1,11 @@
 Name: grpc
 URL: https://github.com/grpc/grpc
 License: Apache 2.0
-Version: v1.36.1+
-Revision: 3ca079faadfcc1f111b6c9a3f3fb10f4b5c794ea
+Version: v1.38.0+
+Revision: 54dc182082db941aa67c7c3f93ad858c99a16d7d
 Security Critical: yes
 
-Steps to upgrade to a new version of GRPC:
+Steps to upgrade to a new version of GRPC, all relative to //third_party/grpc:
 1. Update ../../DEPS to pull origin/grpc/master.
    NOTE: Tagged "official releases" do not work on the buildbots. The master
    branch must be used.
@@ -16,11 +16,12 @@
 5. Rebuild BUILD.gn by running from the src/ directory:
    tools/buildgen/generate_projects.sh
     (make sure mako_templates python module is installed in your system using
-     command apt-get install python3-mako)
+     command "apt-get install python3-mako")
    This will use the template in templates/BUILD.chromium.gn.template to
    generate src/BUILD.chromium.gn file.
 6. Run: mv third_party/grpc/src/BUILD.chromium.gn third_party/grpc/BUILD.gn
 7. If the new gRPC version adds new dependencies, this may result in linker
    errors when building due to some of the gRPC Plugins that have been manually
-   disabled. Manually remove these from the BUILD.gn file.
+   disabled. Manually comment out - not remove - these from the BUILD.gn file,
+   so they may be referenced in future updates.
 8. Run: gn format --in-place BUILD.gn
diff --git a/third_party/grpc/template/BUILD.chromium.gn.template b/third_party/grpc/template/BUILD.chromium.gn.template
index 584b078..f268add1 100644
--- a/third_party/grpc/template/BUILD.chromium.gn.template
+++ b/third_party/grpc/template/BUILD.chromium.gn.template
@@ -171,8 +171,11 @@
     elif name in ("grpc", "grpc_unsecure"):
       deps.add("//third_party/zlib")
     add_absl = False
+    add_boring_ssl = False
     for d in target_dict.get("deps", []):
-      if d.startswith('absl'):
+      if d.startswith('libssl'):
+        add_boring_ssl = True
+      elif d.startswith('absl'):
         add_absl = True
       elif d.startswith(("//", ":")):
         deps.add(d)
@@ -180,6 +183,8 @@
         deps.add(":%s" % d)
     if add_absl:
       deps.add("//third_party/abseil-cpp:absl")
+    if add_boring_ssl:
+      deps.add("//third_party/boringssl",)
     return list(deps)
 
   # Get dependencies for a list of sources.
@@ -237,7 +242,7 @@
     return ["src/" + f for f in sources]
 
   def get_sources(target):
-    sources = ((target.public_headers or []) +
+    sources = (([] if not hasattr(target, "public_headers") else target.public_headers or []) +
             (target.headers or []) +
             (target.src or []))
     return adjust_srcs(sources)
diff --git a/third_party/nearby/BUILD.gn b/third_party/nearby/BUILD.gn
index cf400ab..ebd6042 100644
--- a/third_party/nearby/BUILD.gn
+++ b/third_party/nearby/BUILD.gn
@@ -171,6 +171,7 @@
   public_deps = [
     ":platform_api_platform",
     ":platform_api_types",
+    "//base:base",
   ]
   configs -= [ "//build/config/compiler:chromium_code" ]
   configs += [ "//build/config/compiler:no_chromium_code" ]
diff --git a/third_party/nearby/README.chromium b/third_party/nearby/README.chromium
index d850a32..3090f94 100644
--- a/third_party/nearby/README.chromium
+++ b/third_party/nearby/README.chromium
@@ -1,7 +1,7 @@
 Name: Nearby Connections Library
 Short Name: Nearby
 URL: https://github.com/google/nearby-connections
-Version: 93eec2c07b588985da41af77a4589dd0de37c677
+Version: 258403118d3bd98ec6c52eeb6db0d626af05249d
 License: Apache 2.0
 License File: LICENSE
 Security Critical: yes
diff --git a/third_party/zlib/google/zip_writer.cc b/third_party/zlib/google/zip_writer.cc
index b6f3365..69949cde 100644
--- a/third_party/zlib/google/zip_writer.cc
+++ b/third_party/zlib/google/zip_writer.cc
@@ -230,10 +230,8 @@
     DCHECK_EQ(relative_paths.size(), n);
 
     files.clear();
-    if (!file_accessor_->Open(relative_paths, &files) || files.size() != n) {
-      LOG(ERROR) << "Cannot open " << n << " files";
+    if (!file_accessor_->Open(relative_paths, &files) || files.size() != n)
       return false;
-    }
 
     for (size_t i = 0; i < n; i++) {
       const base::FilePath& relative_path = relative_paths[i];
diff --git a/tools/grit/PRESUBMIT.py b/tools/grit/PRESUBMIT.py
index cd3cc585..d82b5b3 100644
--- a/tools/grit/PRESUBMIT.py
+++ b/tools/grit/PRESUBMIT.py
@@ -8,7 +8,6 @@
 details on the presubmit API built into gcl.
 """
 
-USE_PYTHON3 = True
 
 def RunUnittests(input_api, output_api):
   presubmit_path = input_api.PresubmitLocalPath()
@@ -19,7 +18,7 @@
           input_api.os_path.join(input_api.PresubmitLocalPath(),
                                  'preprocess_if_expr_test.py')
       ],
-      run_on_python2=False)
+      run_on_python3=False)  # See https://crbug.com/1145395.
 
 
 def CheckChangeOnUpload(input_api, output_api):
diff --git a/tools/grit/grit.py b/tools/grit/grit.py
index 700d474..abd1ab64 100755
--- a/tools/grit/grit.py
+++ b/tools/grit/grit.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/clique_unittest.py b/tools/grit/grit/clique_unittest.py
index 8f6a2d4..7d2d7318 100755
--- a/tools/grit/grit/clique_unittest.py
+++ b/tools/grit/grit/clique_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/android_xml_unittest.py b/tools/grit/grit/format/android_xml_unittest.py
index 034ce992..d9f476f 100755
--- a/tools/grit/grit/format/android_xml_unittest.py
+++ b/tools/grit/grit/format/android_xml_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/c_format_unittest.py b/tools/grit/grit/format/c_format_unittest.py
index 8ecd0ef..380120c 100755
--- a/tools/grit/grit/format/c_format_unittest.py
+++ b/tools/grit/grit/format/c_format_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/chrome_messages_json_unittest.py b/tools/grit/grit/format/chrome_messages_json_unittest.py
index 6df70b29..a54e6bd 100755
--- a/tools/grit/grit/format/chrome_messages_json_unittest.py
+++ b/tools/grit/grit/format/chrome_messages_json_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/data_pack.py b/tools/grit/grit/format/data_pack.py
index b180bf6..cfb0156 100755
--- a/tools/grit/grit/format/data_pack.py
+++ b/tools/grit/grit/format/data_pack.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/data_pack_unittest.py b/tools/grit/grit/format/data_pack_unittest.py
index 751a17a0..b61155f 100755
--- a/tools/grit/grit/format/data_pack_unittest.py
+++ b/tools/grit/grit/format/data_pack_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/gen_predetermined_ids.py b/tools/grit/grit/format/gen_predetermined_ids.py
index bec2a8a4..3b4109d 100755
--- a/tools/grit/grit/format/gen_predetermined_ids.py
+++ b/tools/grit/grit/format/gen_predetermined_ids.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/gen_predetermined_ids_unittest.py b/tools/grit/grit/format/gen_predetermined_ids_unittest.py
index 63f353a..8c75be8b 100755
--- a/tools/grit/grit/format/gen_predetermined_ids_unittest.py
+++ b/tools/grit/grit/format/gen_predetermined_ids_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/gzip_string_unittest.py b/tools/grit/grit/format/gzip_string_unittest.py
index a62f570..c0cfbe18 100755
--- a/tools/grit/grit/format/gzip_string_unittest.py
+++ b/tools/grit/grit/format/gzip_string_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2016 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.
diff --git a/tools/grit/grit/format/html_inline.py b/tools/grit/grit/format/html_inline.py
index fa0e5568..cd1fe1f3 100755
--- a/tools/grit/grit/format/html_inline.py
+++ b/tools/grit/grit/format/html_inline.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/html_inline_unittest.py b/tools/grit/grit/format/html_inline_unittest.py
index 28ee23a6..1b11e9e4 100755
--- a/tools/grit/grit/format/html_inline_unittest.py
+++ b/tools/grit/grit/format/html_inline_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/policy_templates_json_unittest.py b/tools/grit/grit/format/policy_templates_json_unittest.py
index e3267bb7..e252c94e 100755
--- a/tools/grit/grit/format/policy_templates_json_unittest.py
+++ b/tools/grit/grit/format/policy_templates_json_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # coding: utf-8
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
diff --git a/tools/grit/grit/format/rc_header_unittest.py b/tools/grit/grit/format/rc_header_unittest.py
index 7a3ca5a4..9e47912 100755
--- a/tools/grit/grit/format/rc_header_unittest.py
+++ b/tools/grit/grit/format/rc_header_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/format/rc_unittest.py b/tools/grit/grit/format/rc_unittest.py
index 699e092..d23f063 100755
--- a/tools/grit/grit/format/rc_unittest.py
+++ b/tools/grit/grit/format/rc_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 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.
diff --git a/tools/grit/grit/format/resource_map_unittest.py b/tools/grit/grit/format/resource_map_unittest.py
index 440e91e..4b2c2d2 100755
--- a/tools/grit/grit/format/resource_map_unittest.py
+++ b/tools/grit/grit/format/resource_map_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/gather/admin_template_unittest.py b/tools/grit/grit/gather/admin_template_unittest.py
index f7be5b7b..c637af3 100755
--- a/tools/grit/grit/gather/admin_template_unittest.py
+++ b/tools/grit/grit/gather/admin_template_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/gather/chrome_html_unittest.py b/tools/grit/grit/gather/chrome_html_unittest.py
index 75dc3a6..8c75ee5 100755
--- a/tools/grit/grit/gather/chrome_html_unittest.py
+++ b/tools/grit/grit/gather/chrome_html_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/gather/chrome_scaled_image_unittest.py b/tools/grit/grit/gather/chrome_scaled_image_unittest.py
index 144f943..1cebfc6 100755
--- a/tools/grit/grit/gather/chrome_scaled_image_unittest.py
+++ b/tools/grit/grit/gather/chrome_scaled_image_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/gather/policy_json_unittest.py b/tools/grit/grit/gather/policy_json_unittest.py
index 05db0ed..214cd27 100755
--- a/tools/grit/grit/gather/policy_json_unittest.py
+++ b/tools/grit/grit/gather/policy_json_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 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.
diff --git a/tools/grit/grit/gather/rc_unittest.py b/tools/grit/grit/gather/rc_unittest.py
index 4f53b12..3c26a43 100755
--- a/tools/grit/grit/gather/rc_unittest.py
+++ b/tools/grit/grit/gather/rc_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/gather/tr_html_unittest.py b/tools/grit/grit/gather/tr_html_unittest.py
index 77dbe4e..1194853 100755
--- a/tools/grit/grit/gather/tr_html_unittest.py
+++ b/tools/grit/grit/gather/tr_html_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/gather/txt_unittest.py b/tools/grit/grit/gather/txt_unittest.py
index 1ad751f70..abb9ed9 100755
--- a/tools/grit/grit/gather/txt_unittest.py
+++ b/tools/grit/grit/gather/txt_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/grd_reader.py b/tools/grit/grit/grd_reader.py
index 3ffa4dc9..b7bb7829 100755
--- a/tools/grit/grit/grd_reader.py
+++ b/tools/grit/grit/grd_reader.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/grd_reader_unittest.py b/tools/grit/grit/grd_reader_unittest.py
index 3540c0e..920a92f 100755
--- a/tools/grit/grit/grd_reader_unittest.py
+++ b/tools/grit/grit/grd_reader_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/grit_runner.py b/tools/grit/grit/grit_runner.py
index 49be7a0..26aa0d5 100755
--- a/tools/grit/grit/grit_runner.py
+++ b/tools/grit/grit/grit_runner.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/grit_runner_unittest.py b/tools/grit/grit/grit_runner_unittest.py
index 0a2b7e0..1487001 100755
--- a/tools/grit/grit/grit_runner_unittest.py
+++ b/tools/grit/grit/grit_runner_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/lazy_re_unittest.py b/tools/grit/grit/lazy_re_unittest.py
index 056471c6..8488b454 100755
--- a/tools/grit/grit/lazy_re_unittest.py
+++ b/tools/grit/grit/lazy_re_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/node/base_unittest.py b/tools/grit/grit/node/base_unittest.py
index 285ba4c..32a5a0ca 100755
--- a/tools/grit/grit/node/base_unittest.py
+++ b/tools/grit/grit/node/base_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/node/custom/filename_unittest.py b/tools/grit/grit/node/custom/filename_unittest.py
index b10ba6c..8e2a6dd 100755
--- a/tools/grit/grit/node/custom/filename_unittest.py
+++ b/tools/grit/grit/node/custom/filename_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/node/include_unittest.py b/tools/grit/grit/node/include_unittest.py
index 71c38bdcc..cc68177 100755
--- a/tools/grit/grit/node/include_unittest.py
+++ b/tools/grit/grit/node/include_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/node/message_unittest.py b/tools/grit/grit/node/message_unittest.py
index 534673f..7a4cbbe 100755
--- a/tools/grit/grit/node/message_unittest.py
+++ b/tools/grit/grit/node/message_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/node/misc_unittest.py b/tools/grit/grit/node/misc_unittest.py
index 3b8c320..684d3ac 100755
--- a/tools/grit/grit/node/misc_unittest.py
+++ b/tools/grit/grit/node/misc_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/node/mock_brotli.py b/tools/grit/grit/node/mock_brotli.py
index d67b7358..14237aa 100755
--- a/tools/grit/grit/node/mock_brotli.py
+++ b/tools/grit/grit/node/mock_brotli.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # 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.
diff --git a/tools/grit/grit/node/node_io_unittest.py b/tools/grit/grit/node/node_io_unittest.py
index 6f8ffae1..1f45e51 100755
--- a/tools/grit/grit/node/node_io_unittest.py
+++ b/tools/grit/grit/node/node_io_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/node/structure_unittest.py b/tools/grit/grit/node/structure_unittest.py
index 9631f46d..0e66dce 100755
--- a/tools/grit/grit/node/structure_unittest.py
+++ b/tools/grit/grit/node/structure_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/pseudo_unittest.py b/tools/grit/grit/pseudo_unittest.py
index cebeef1..b1d53ff 100755
--- a/tools/grit/grit/pseudo_unittest.py
+++ b/tools/grit/grit/pseudo_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/pseudolocales_unittest.py b/tools/grit/grit/pseudolocales_unittest.py
index cfd4b74e..bc449af 100755
--- a/tools/grit/grit/pseudolocales_unittest.py
+++ b/tools/grit/grit/pseudolocales_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2021 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.
diff --git a/tools/grit/grit/tclib_unittest.py b/tools/grit/grit/tclib_unittest.py
index d02edd39..7a08654 100755
--- a/tools/grit/grit/tclib_unittest.py
+++ b/tools/grit/grit/tclib_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/tool/android2grd_unittest.py b/tools/grit/grit/tool/android2grd_unittest.py
index 9e421fb..a6934a7 100755
--- a/tools/grit/grit/tool/android2grd_unittest.py
+++ b/tools/grit/grit/tool/android2grd_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/tool/build_unittest.py b/tools/grit/grit/tool/build_unittest.py
index a2f57a6..969f571 100755
--- a/tools/grit/grit/tool/build_unittest.py
+++ b/tools/grit/grit/tool/build_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/tool/buildinfo_unittest.py b/tools/grit/grit/tool/buildinfo_unittest.py
index 736d6da6..24e9ddf 100755
--- a/tools/grit/grit/tool/buildinfo_unittest.py
+++ b/tools/grit/grit/tool/buildinfo_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/tool/diff_structures_unittest.py b/tools/grit/grit/tool/diff_structures_unittest.py
index d75d796..a6d7585 100755
--- a/tools/grit/grit/tool/diff_structures_unittest.py
+++ b/tools/grit/grit/tool/diff_structures_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # 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.
diff --git a/tools/grit/grit/tool/newgrd_unittest.py b/tools/grit/grit/tool/newgrd_unittest.py
index f65f257..f7c8831 100755
--- a/tools/grit/grit/tool/newgrd_unittest.py
+++ b/tools/grit/grit/tool/newgrd_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # 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.
diff --git a/tools/grit/grit/tool/postprocess_unittest.py b/tools/grit/grit/tool/postprocess_unittest.py
index 7cbe307..77fe228b 100755
--- a/tools/grit/grit/tool/postprocess_unittest.py
+++ b/tools/grit/grit/tool/postprocess_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/tool/preprocess_unittest.py b/tools/grit/grit/tool/preprocess_unittest.py
index a5ed8a8..40b95cd 100755
--- a/tools/grit/grit/tool/preprocess_unittest.py
+++ b/tools/grit/grit/tool/preprocess_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/tool/rc2grd_unittest.py b/tools/grit/grit/tool/rc2grd_unittest.py
index c599032..6d53794 100755
--- a/tools/grit/grit/tool/rc2grd_unittest.py
+++ b/tools/grit/grit/tool/rc2grd_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/tool/transl2tc_unittest.py b/tools/grit/grit/tool/transl2tc_unittest.py
index 95837c89..22e937f 100755
--- a/tools/grit/grit/tool/transl2tc_unittest.py
+++ b/tools/grit/grit/tool/transl2tc_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py b/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
index 1c152283..164d8207 100755
--- a/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
+++ b/tools/grit/grit/tool/update_resource_ids/assigner_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # 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.
diff --git a/tools/grit/grit/tool/xmb_unittest.py b/tools/grit/grit/tool/xmb_unittest.py
index c756a8a..3c7e92ce 100755
--- a/tools/grit/grit/tool/xmb_unittest.py
+++ b/tools/grit/grit/tool/xmb_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/util_unittest.py b/tools/grit/grit/util_unittest.py
index dd61275..7d6efaf 100755
--- a/tools/grit/grit/util_unittest.py
+++ b/tools/grit/grit/util_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit/xtb_reader_unittest.py b/tools/grit/grit/xtb_reader_unittest.py
index bd04cc29..79c0ac9 100755
--- a/tools/grit/grit/xtb_reader_unittest.py
+++ b/tools/grit/grit/xtb_reader_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/grit_info.py b/tools/grit/grit_info.py
index 7072f5d..6752e8673 100755
--- a/tools/grit/grit_info.py
+++ b/tools/grit/grit_info.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/grit/minify_with_uglify.py b/tools/grit/minify_with_uglify.py
index 3f66245e..1c12140 100755
--- a/tools/grit/minify_with_uglify.py
+++ b/tools/grit/minify_with_uglify.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # 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.
@@ -21,7 +21,7 @@
   # write its output to the other.
   with tempfile.NamedTemporaryFile(suffix='.js') as infile, \
        tempfile.NamedTemporaryFile(suffix='.js') as outfile:
-    infile.write(source.encode())
+    infile.write(source)
     infile.flush();
     node.RunNode(
         [node_modules.PathToTerser(), infile.name, '--output', outfile.name])
diff --git a/tools/grit/minimize_css.py b/tools/grit/minimize_css.py
index 5e2d6c2d..2c3b8ae 100755
--- a/tools/grit/minimize_css.py
+++ b/tools/grit/minimize_css.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright 2016 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.
diff --git a/tools/grit/minimize_css_unittest.py b/tools/grit/minimize_css_unittest.py
index b7ed3e3b..cddc3130 100755
--- a/tools/grit/minimize_css_unittest.py
+++ b/tools/grit/minimize_css_unittest.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright 2016 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.
diff --git a/tools/grit/pak_util.py b/tools/grit/pak_util.py
index bab07ea8..62931f9 100755
--- a/tools/grit/pak_util.py
+++ b/tools/grit/pak_util.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright 2017 The Chromium Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 70b495f..a79adc5 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -17911,6 +17911,7 @@
      please refer to content/common/debug_utils.h. -->
 
   <int value="1" label="kDebugSameDocNavigationDocIdMismatch"/>
+  <int value="2" label="kDebugNonMainFrameWithOldPageInfo"/>
 </enum>
 
 <enum name="DeclarativeAPIFunctionType">
@@ -50212,6 +50213,14 @@
   <int value="1" label="kChangeEuidToMlServiceDBusFailed"/>
   <int value="2" label="kChangeEuidBackToRootFailed"/>
   <int value="3" label="kGetWorkerProcessMemoryUsageFailed"/>
+  <int value="4" label="kReapWorkerProcessMaxNumOfRetrialsExceeded"/>
+</enum>
+
+<enum name="MachineLearningServiceReapWorkerProcessErrno">
+  <int value="0" label="kUnknown"/>
+  <int value="1" label="kECHILD"/>
+  <int value="2" label="kEINTR"/>
+  <int value="3" label="kEINVAL"/>
 </enum>
 
 <enum name="MachineLevelUserCloudPolicyEnrollmentResult">
@@ -59141,6 +59150,11 @@
   <int value="6" label="No supported hints found">
     Hints processed but no supported hints found.
   </int>
+  <int value="7" label="Failed to read previously-attempted version">
+    Previous version for which processing did not complete could not be read so
+    clearing the state and skipping for the session. Will try again next
+    session.
+  </int>
 </enum>
 
 <enum name="OptimizationGuideRaceNavigationFetchAttemptStatus">
@@ -59304,9 +59318,9 @@
   <int value="307" label="Restrict Sign In"/>
   <int value="308" label="Add To User Allowlist"/>
   <int value="309" label="Remove From User Allowlist"/>
-  <int value="310" label="Add Kerberos Ticket"/>
-  <int value="311" label="Remove Kerberos Ticket"/>
-  <int value="312" label="Set Active Kerberos Ticket"/>
+  <int value="310" label="Add Kerberos Ticket (Deprecated)"/>
+  <int value="311" label="Remove Kerberos Ticket (Deprecated)"/>
+  <int value="312" label="Set Active Kerberos Ticket (Deprecated)"/>
   <int value="313" label="Add Fingerprint"/>
   <int value="314" label="Remove Fingerprint"/>
   <int value="315" label="Set Up Parental Controls"/>
@@ -59448,7 +59462,7 @@
   <int value="1705" label="Report an Issue"/>
   <int value="1706" label="View Terms of Service"/>
   <int value="1707" label="Open Diagnostics App"/>
-  <int value="1800" label="View Add Kerberos Ticket V2"/>
+  <int value="1800" label="Add Kerberos Ticket V2"/>
   <int value="1801" label="Remove Kerberos Ticket V2"/>
   <int value="1802" label="Set Active Kerberos Ticket V2"/>
 </enum>
@@ -59531,7 +59545,7 @@
   <int value="304" label="Security And Sign In"/>
   <int value="305" label="Fingerprint"/>
   <int value="306" label="Manage Other People"/>
-  <int value="307" label="Kerberos Accounts"/>
+  <int value="307" label="Kerberos Accounts (Deprecated)"/>
   <int value="400" label="Pointers"/>
   <int value="401" label="Keyboard"/>
   <int value="402" label="Stylus"/>
@@ -64829,6 +64843,9 @@
 </enum>
 
 <enum name="PrivateSetMembershipStatus">
+  <obsolete>
+    Deprecated since June 2021 and replaced with PsmResult.
+  </obsolete>
   <int value="0" label="Attempt"/>
   <int value="1" label="Successful Determination"/>
   <int value="2" label="Error"/>
@@ -65814,6 +65831,19 @@
   <int value="1" label="PSM true and Hash dance false"/>
 </enum>
 
+<enum name="PsmResult">
+  <int value="0" label="Successful Determination"/>
+  <int value="1" label="Create RLWE client library Error"/>
+  <int value="2" label="Create RLWE OPRF request library error"/>
+  <int value="3" label="Create RLWE query request library error"/>
+  <int value="4" label="Processing query response library error"/>
+  <int value="5" label="Empty RLWE OPRF response error"/>
+  <int value="6" label="Empty RLWE query response Error"/>
+  <int value="7" label="Connection Error"/>
+  <int value="8" label="Server Error"/>
+  <int value="9" label="Timeout"/>
+</enum>
+
 <enum name="PublicKeyPinFailedDomain">
   <int value="0" label="DOMAIN_NOT_PINNED"/>
   <int value="1" label="DOMAIN_GOOGLE_COM"/>
diff --git a/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
index 9073827a..d28864b 100644
--- a/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
@@ -40,6 +40,7 @@
 schenney@chromium.org
 sebmarchand@chromium.org
 sebsg@chromium.org
+shend@chromium.org
 sophiechang@chromium.org
 tbansal@chromium.org
 tby@chromium.org
diff --git a/tools/metrics/histograms/histograms_xml/android/histograms.xml b/tools/metrics/histograms/histograms_xml/android/histograms.xml
index dc6c472..b2692cd 100644
--- a/tools/metrics/histograms/histograms_xml/android/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/android/histograms.xml
@@ -37,6 +37,26 @@
   <variant name=".UnknownIntent"/>
 </variants>
 
+<variants name="JankMetricsScenarios">
+  <variant name="">
+    <obsolete>
+      Renamed to .Total
+    </obsolete>
+  </variant>
+  <variant name=".NewTabPage" summary="while NTP is open"/>
+  <variant name=".Omnibox" summary="while Omnibox is open"/>
+  <variant name=".OpenLinkInNewTab"
+      summary="while opening a link in a new tab using the long press menu"/>
+  <variant name=".Startup" summary="while Chrome is starting"/>
+  <variant name=".TabSwitcher" summary="while Tab switcher is open"/>
+  <variant name=".Total"
+      summary="while ChromeTabbedActivity is resumed. However, there's an
+               unusual implementation detail: Individual frame durations are
+               measured in Java, and passed over to C++ for recording once
+               every 30 seconds. Thus, frames that occur near program
+               termination might be dropped from the data"/>
+</variants>
+
 <histogram name="Android.AdaptiveToolbarButton.Clicked"
     enum="AdaptiveToolbarButtonVariant" expires_after="2022-02-15">
   <owner>bttk@chromium.org</owner>
@@ -1409,37 +1429,36 @@
   </summary>
 </histogram>
 
-<histogram name="Android.Jank.FrameDuration" units="ms"
+<histogram name="Android.Jank.FrameDuration{Scenario}" units="ms"
     expires_after="2021-09-30">
   <owner>salg@google.com</owner>
   <owner>ssid@chromium.org</owner>
   <summary>
     Amount of time it takes to draw Android UI frames on ChromeTabbedActivity as
-    measured by Android's FrameMetrics API. Each frame's duration is recorded
-    individually while ChromeTabbedActivity is resumed. However, there's an
-    unusual implementation detail: Individual frame durations are measured in
-    Java, and passed over to C++ for recording once every 30 seconds. Thus,
-    frames that occur near program termination might be dropped from the data.
+    measured by Android's FrameMetrics API. Each frame's draw duration is stored
+    individually {Scenario}.
 
     The samples for this metric (Along with Android.Jank.JankBursts and
-    Android.Jank.MissedFrames) are manually passed from Java to C++ every 30
-    seconds to be recorded. This is done instead of recording in real time from
-    Java due to the large number of generated samples (potentially ~60 per
-    second). Recording them directly from Java would result in a JNI call for
-    each sample which could cause performance issues.
+    Android.Jank.MissedFrames) are manually passed from Java to C++ to be
+    recorded. This is done instead of recording in real time from Java due to
+    the large number of generated samples (potentially ~60 per second).
+    Recording them directly from Java would result in a JNI call for each sample
+    which could cause performance issues.
 
     FrameMetrics only reports frames with Android UI updates. An idle omnibox
     would report only 2 frames per second due to the cursor blink animation.
     Scrolling through a web page may record no frames if no Android UI (like the
     address bar) updates.
   </summary>
+  <token key="Scenario" variants="JankMetricsScenarios"/>
 </histogram>
 
-<histogram name="Android.Jank.JankBursts" units="ms" expires_after="2021-09-30">
+<histogram name="Android.Jank.JankBursts{Scenario}" units="ms"
+    expires_after="2021-09-30">
   <owner>salg@google.com</owner>
   <owner>ssid@chromium.org</owner>
   <summary>
-    Recorded every 30 seconds alongside Android.Jank.FrameDuration. Each sample
+    Recorded alongside Android.Jank.FrameDuration {Scenario}. Each sample
     represents a jank burst: the total frame duration of one or many janky
     frames drawn in quick succession. For example, the following sequence of
     frame durations: [2ms, 100ms, 100ms, 100ms, 2ms, 2ms, 2ms, 100ms, 2ms] would
@@ -1457,18 +1476,27 @@
     it indicates its jankyness is caused by a different event or animation. (see
     go/clank-zero-browser-jank for more details).
   </summary>
+  <token key="Scenario" variants="JankMetricsScenarios"/>
 </histogram>
 
-<histogram name="Android.Jank.MissedFrames" units="frames"
+<histogram name="Android.Jank.MissedFrames{Scenario}" units="frames"
     expires_after="2021-09-30">
   <owner>salg@google.com</owner>
   <owner>ssid@chromium.org</owner>
   <summary>
-    Number of frames that Android's FrameMetrics API failed to report on because
-    of high main thread activity. Android.Jank.FrameDuration and
-    Android.Jank.JankBursts are not recorded for these missed frames. This
-    metric is reported every 30 seconds along with Android.Jank.FrameDuration.
+    Number of frames that Android's FrameMetrics API skipped reporting on
+    because of high main thread activity. This metric is recorded at the same
+    time as Android.Jank.* histograms {Scenario}. Each sample is the number of
+    frames that we didn't receive timings for, so their draw duration is not
+    recorded in the respective Android.Jank.FrameDuration.* histogram, and (even
+    if its duration was greater than 16ms) it is not counted in any
+    Android.Jank.JankyBursts.* samples.
+
+    A high number of missed frames would mean that we are taking too long to
+    record frame metrics and that the other Android.Jank.* histograms may not be
+    accurate.
   </summary>
+  <token key="Scenario" variants="JankMetricsScenarios"/>
 </histogram>
 
 <histogram name="Android.KernelVersion" enum="AndroidKernelVersion"
diff --git a/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml b/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml
index 14dab6c7..9d9d9dac 100644
--- a/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/enterprise/histograms.xml
@@ -121,6 +121,10 @@
 
 <histogram name="Enterprise.AutoEnrollmentPrivateSetMembershipRequestStatus"
     enum="PrivateSetMembershipStatus" expires_after="2021-10-31">
+  <obsolete>
+    Deprecated since June 2021 and replaced with
+    Enterprise.AutoEnrollmentPsmResult.
+  </obsolete>
   <owner>amraboelkher@google.com</owner>
   <owner>mpolzer@google.com</owner>
   <summary>
@@ -148,6 +152,17 @@
   <summary>Total duration time of the auto-enrollment protocol.</summary>
 </histogram>
 
+<histogram name="Enterprise.AutoEnrollmentPsmDmServerRequestStatus"
+    enum="EnterpriseDeviceManagementStatus" expires_after="2021-10-31">
+<!-- Name completed by histogram_suffixes name="EnterpriseAutoEnrollmentType". -->
+
+  <owner>amraboelkher@google.com</owner>
+  <owner>mpolzer@google.com</owner>
+  <summary>
+    Device management server request status for auto-enrollment PSM requests.
+  </summary>
+</histogram>
+
 <histogram
     name="Enterprise.AutoEnrollmentPsmHashDanceDifferentResultsComparison"
     enum="PsmHashDanceDifferentResultsComparison" expires_after="2021-09-01">
@@ -160,6 +175,30 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.AutoEnrollmentPsmRequestNetworkErrorCode"
+    enum="NetErrorCodes" expires_after="2021-10-31">
+<!-- Name completed by histogram_suffixes name="EnterpriseAutoEnrollmentType". -->
+
+  <owner>amraboelkher@google.com</owner>
+  <owner>mpolzer@google.com</owner>
+  <summary>
+    Network error code (if applicable) for auto-enrollment PSM requests.
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.AutoEnrollmentPsmResult" enum="PsmResult"
+    expires_after="2021-10-31">
+<!-- Name completed by histogram_suffixes name="EnterpriseAutoEnrollmentType". -->
+
+  <owner>amraboelkher@google.com</owner>
+  <owner>mpolzer@google.com</owner>
+  <summary>
+    All possible PSM protocol results after it has executed succcessfully or
+    terminated due to an error or timeout. This request is used to determine the
+    initial enrollment state of the device.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Enterprise.AutoEnrollmentRequestNetworkErrorCode"
     enum="NetErrorCodes" expires_after="2021-10-17">
 <!-- Name completed by histogram_suffixes name="EnterpriseAutoEnrollmentType". -->
@@ -1429,7 +1468,7 @@
 </histogram>
 
 <histogram name="Enterprise.SystemFeaturesDisableList" enum="SystemFeature"
-    expires_after="2021-06-01">
+    expires_after="2023-06-01">
   <owner>ayaelattar@chromium.org</owner>
   <owner>poromov@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
index fd1bab4..a4b76385 100644
--- a/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
+++ b/tools/metrics/histograms/histograms_xml/histogram_suffixes_list.xml
@@ -6050,6 +6050,10 @@
   <affected-histogram name="Enterprise.AutoEnrollmentExtraTime"/>
   <affected-histogram name="Enterprise.AutoEnrollmentHashDanceSuccessTime"/>
   <affected-histogram name="Enterprise.AutoEnrollmentProtocolTime"/>
+  <affected-histogram name="Enterprise.AutoEnrollmentPsmDmServerRequestStatus"/>
+  <affected-histogram
+      name="Enterprise.AutoEnrollmentPsmRequestNetworkErrorCode"/>
+  <affected-histogram name="Enterprise.AutoEnrollmentPsmResult"/>
   <affected-histogram name="Enterprise.AutoEnrollmentRequestNetworkErrorCode"/>
   <affected-histogram name="Enterprise.AutoEnrollmentRequestStatus"/>
 </histogram_suffixes>
diff --git a/tools/metrics/histograms/histograms_xml/input/OWNERS b/tools/metrics/histograms/histograms_xml/input/OWNERS
new file mode 100644
index 0000000..d9a31ab
--- /dev/null
+++ b/tools/metrics/histograms/histograms_xml/input/OWNERS
@@ -0,0 +1,5 @@
+per-file OWNERS=file://tools/metrics/histograms/histograms_xml/METRIC_REVIEWER_OWNERS
+
+# Prefer sending CLs to the owners listed below.
+# Use chromium-metrics-reviews@google.com as a backup.
+shend@chromium.org
diff --git a/tools/metrics/histograms/histograms_xml/input/histograms.xml b/tools/metrics/histograms/histograms_xml/input/histograms.xml
index f6fd4e1..30c8cd5 100644
--- a/tools/metrics/histograms/histograms_xml/input/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/input/histograms.xml
@@ -812,7 +812,7 @@
 </histogram>
 
 <histogram name="InputMethod.VirtualKeyboard.Handwriting.CommitType"
-    enum="VirtualKeyboardHandwritingCommitType" expires_after="2021-07-01">
+    enum="VirtualKeyboardHandwritingCommitType" expires_after="2022-02-01">
   <owner>curtismcmullan@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
@@ -920,7 +920,7 @@
 </histogram>
 
 <histogram name="InputMethod.VirtualKeyboard.ResizableWindowInitWidth"
-    units="px" expires_after="2021-07-06">
+    units="px" expires_after="2022-02-01">
   <owner>curtismcmullan@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/net/histograms.xml b/tools/metrics/histograms/histograms_xml/net/histograms.xml
index f6a5ff6..a49aacb 100644
--- a/tools/metrics/histograms/histograms_xml/net/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/net/histograms.xml
@@ -3834,7 +3834,7 @@
 </histogram>
 
 <histogram name="Net.QuicSession.PathValidationSuccess" enum="BooleanSuccess"
-    expires_after="2021-07-11">
+    expires_after="2022-07-11">
   <owner>renjietang@chromium.org</owner>
   <owner>src/net/quic/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/histograms_xml/optimization/histograms.xml b/tools/metrics/histograms/histograms_xml/optimization/histograms.xml
index 5778113..2369d28 100644
--- a/tools/metrics/histograms/histograms_xml/optimization/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/optimization/histograms.xml
@@ -746,6 +746,16 @@
   </summary>
 </histogram>
 
+<histogram name="OptimizationGuide.ProcessingComponentAtShutdown"
+    enum="BooleanYesNo" expires_after="M94">
+  <owner>mcrouse@chromium.org</owner>
+  <owner>sophiechang@chromium.org</owner>
+  <summary>
+    Whether the Optimization Hints component was being processed when the hints
+    manager is told to shutdown.
+  </summary>
+</histogram>
+
 <histogram name="OptimizationGuide.PushNotifications.CachedNotificationCount"
     units="count" expires_after="M94">
   <owner>robertogden@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml
index 64b24aa..594487b870 100644
--- a/tools/metrics/histograms/histograms_xml/others/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -9361,6 +9361,23 @@
   </summary>
 </histogram>
 
+<histogram name="MachineLearningService.ReapWorkerProcessErrno"
+    enum="MachineLearningServiceReapWorkerProcessErrno"
+    expires_after="2021-11-07">
+  <owner>amoylan@chromium.org</owner>
+  <owner>alanlxl@chromium.org</owner>
+  <owner>honglinyu@chromium.org</owner>
+  <summary>
+    The &quot;errno&quot; of waitpid calls used by ml-service's control process
+    to reap the worker processes. This is only recorded when waitpid meets error
+    and should be rare. This is a Chrome OS only metric. And we only record the
+    corresponding enum values of &quot;ECHILD&quot;, &quot;EINTR&quot; and
+    &quot;EINVAL&quot; because according to the man page of waitpid, they are
+    the only possible errno. All the other &quot;errno&quot; are treated as
+    &quot;unknown&quot; error.
+  </summary>
+</histogram>
+
 <histogram name="MachineLearningService.TextSuggester.Suggest.Event"
     enum="BooleanError" expires_after="2022-05-31">
   <owner>curtismcmullan@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/stability/histograms.xml b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
index 6e5a2aa..ec5372dd 100644
--- a/tools/metrics/histograms/histograms_xml/stability/histograms.xml
+++ b/tools/metrics/histograms/histograms_xml/stability/histograms.xml
@@ -157,8 +157,9 @@
 </histogram>
 
 <histogram name="Stability.BadMessageTerminated.Extensions"
-    enum="BadMessageReasonExtensions" expires_after="M82">
+    enum="BadMessageReasonExtensions" expires_after="2021-11-14">
   <owner>jamescook@chromium.org</owner>
+  <owner>karandeepb@chromium.org</owner>
   <summary>
     Count of extension processes killed because they sent an IPC that couldn't
     be properly handled. Categories are the reasons (code locations) for the
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 57d5f22b..1face3c8 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -9,8 +9,8 @@
             "remote_path": "perfetto_binaries/trace_processor_shell/mac/bae8193de6c017394901163b7817157342914679/trace_processor_shell"
         },
         "linux": {
-            "hash": "d8409cee39bfb2ba7210faad852f07ad4485f92a",
-            "remote_path": "perfetto_binaries/trace_processor_shell/linux/f1f79d741319ec182b72bd473078c3ba5e8bf777/trace_processor_shell"
+            "hash": "5b5e6db44c3cd71a42adcad20800c9a4e35b3fd8",
+            "remote_path": "perfetto_binaries/trace_processor_shell/linux/31cfc47480a7a226c3ff06143620509cbd2aa29c/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/vim/PRESUBMIT.py b/tools/vim/PRESUBMIT.py
index 1cd25f9..06b5bd35 100644
--- a/tools/vim/PRESUBMIT.py
+++ b/tools/vim/PRESUBMIT.py
@@ -6,6 +6,8 @@
 Runs Python unit tests in /tools/vim/tests on upload.
 """
 
+USE_PYTHON3 = True
+
 
 def CheckChangeOnUpload(input_api, output_api):
   results = []
diff --git a/tools/vim/tests/chromium.ycm_extra_conf_unittest.py b/tools/vim/tests/chromium.ycm_extra_conf_unittest.py
index 3662fee..6c00d2b 100755
--- a/tools/vim/tests/chromium.ycm_extra_conf_unittest.py
+++ b/tools/vim/tests/chromium.ycm_extra_conf_unittest.py
@@ -140,33 +140,31 @@
   def testFindChromeSrc(self):
     chrome_source = self.ycm_extra_conf.FindChromeSrcFromFilename(
         os.path.join(self.chrome_root, 'chrome', 'one.cpp'))
-    self.assertEquals(chrome_source, self.chrome_root)
+    self.assertEqual(chrome_source, self.chrome_root)
 
     chrome_source = self.ycm_extra_conf.FindChromeSrcFromFilename(
         os.path.join(self.chrome_root, 'one.cpp'))
-    self.assertEquals(chrome_source, self.chrome_root)
+    self.assertEqual(chrome_source, self.chrome_root)
 
   def testCommandLineForKnownCppFile(self):
     command_line = self.ycm_extra_conf.GetClangCommandLineFromNinjaForSource(
         self.out_dir, os.path.join(self.chrome_root, 'one.cpp'))
-    self.assertEquals(command_line,
-                      ('../../fake-clang++ -Ia -Itag-one ../../one.cpp '
-                       '-o obj/one.o'))
+    self.assertEqual(command_line,
+                     ('../../fake-clang++ -Ia -Itag-one ../../one.cpp '
+                      '-o obj/one.o'))
 
   def testCommandLineForUnknownCppFile(self):
     command_line = self.ycm_extra_conf.GetClangCommandLineFromNinjaForSource(
         self.out_dir, os.path.join(self.chrome_root, 'unknown.cpp'))
-    self.assertEquals(command_line, None)
+    self.assertEqual(command_line, None)
 
   def testGetClangOptionsForKnownCppFile(self):
     clang_options = \
         self.ycm_extra_conf.GetClangOptionsFromNinjaForFilename(
             self.chrome_root, os.path.join(self.chrome_root, 'one.cpp'))
-    self.assertEquals(
-        self.NormalizeStringsInList(clang_options), [
-            '-I[SRC]', '-Wno-unknown-warning-option', '-I[OUT]/a',
-            '-I[OUT]/tag-one'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(clang_options), [
+        '-I[SRC]', '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-one'
+    ])
 
   def testOutDirNames(self):
     out_root = os.path.join(self.chrome_root, 'out_with_underscore')
@@ -188,11 +186,10 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
-            '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-one'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-one'
+    ])
 
   def testGetFlagsForFileForUnknownCppFile(self):
     result = self.ycm_extra_conf.FlagsForFile(
@@ -201,11 +198,10 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
-            '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default'
+    ])
 
   def testGetFlagsForFileForUnknownCppNotTestFile(self):
     result = self.ycm_extra_conf.FlagsForFile(
@@ -214,11 +210,10 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
-            '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default'
+    ])
 
   testGetFlagsForFileForKnownObjcFile = TestLanguage('eight.m', 'objective-c')
   testGetFlagsForFileForKnownObjcHeaderFile = TestLanguage(
@@ -239,11 +234,10 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
-            '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default'
+    ])
 
   def testGetFlagsForFileForUnknownUnittestFile(self):
     result = self.ycm_extra_conf.FlagsForFile(
@@ -252,12 +246,10 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
-            '-Wno-unknown-warning-option', '-I[OUT]/a',
-            '-I[OUT]/tag-default-test'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default-test'
+    ])
 
   def testGetFlagsForFileForUnknownBrowsertestFile2(self):
     result = self.ycm_extra_conf.FlagsForFile(
@@ -266,12 +258,10 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
-            '-Wno-unknown-warning-option', '-I[OUT]/a',
-            '-I[OUT]/tag-default-test'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default-test'
+    ])
 
   def testGetFlagsForFileForKnownHeaderFileWithAssociatedCppFile(self):
     result = self.ycm_extra_conf.FlagsForFile(
@@ -280,11 +270,10 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
-            '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-three'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-three'
+    ])
 
   def testSourceFileWithNonClangOutputs(self):
     # Verify assumption that four.cc has non-compiler-output listed as the first
@@ -296,10 +285,11 @@
         universal_newlines=True)
     stdout, _ = p.communicate()
     self.assertFalse(p.returncode)
-    self.assertEquals(stdout, '../../four.cc:\n'
-                      '  outputs:\n'
-                      '    obj/linker-output.o\n'
-                      '    obj/four.o\n')
+    self.assertEqual(
+        stdout, '../../four.cc:\n'
+        '  outputs:\n'
+        '    obj/linker-output.o\n'
+        '    obj/four.o\n')
 
     result = self.ycm_extra_conf.FlagsForFile(
         os.path.join(self.chrome_root, 'four.cc'))
@@ -307,11 +297,10 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
-            '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-four'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-four'
+    ])
 
   def testSourceFileWithOnlyNonClangOutputs(self):
     result = self.ycm_extra_conf.FlagsForFile(
@@ -320,11 +309,10 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
-            '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/a', '-I[OUT]/tag-default'
+    ])
 
   def testGetFlagsForSysrootAbsPath(self):
     result = self.ycm_extra_conf.FlagsForFile(
@@ -333,19 +321,18 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER',
-            '-std=c++14',
-            '-x',
-            'c++',
-            '-I[SRC]',
-            '-Wno-unknown-warning-option',
-            '-I[OUT]/a',
-            '--sysroot=/usr/lib/sysroot-image',
-            '-isysroot',
-            '/mac.sdk',
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER',
+        '-std=c++14',
+        '-x',
+        'c++',
+        '-I[SRC]',
+        '-Wno-unknown-warning-option',
+        '-I[OUT]/a',
+        '--sysroot=/usr/lib/sysroot-image',
+        '-isysroot',
+        '/mac.sdk',
+    ])
 
   def testGetFlagsForSysrootRelPath(self):
     result = self.ycm_extra_conf.FlagsForFile(
@@ -354,55 +341,39 @@
     self.assertTrue('do_cache' in result)
     self.assertTrue(result['do_cache'])
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER',
-            '-std=c++14',
-            '-x',
-            'c++',
-            '-I[SRC]',
-            '-Wno-unknown-warning-option',
-            '-I[OUT]/a',
-            '--sysroot=[SRC]/build/sysroot-image',
-            '-isysroot',
-            '[SRC]/build/mac.sdk',
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER',
+        '-std=c++14',
+        '-x',
+        'c++',
+        '-I[SRC]',
+        '-Wno-unknown-warning-option',
+        '-I[OUT]/a',
+        '--sysroot=[SRC]/build/sysroot-image',
+        '-isysroot',
+        '[SRC]/build/mac.sdk',
+    ])
 
   def testGetFlagsForIsystem(self):
     result = self.ycm_extra_conf.FlagsForFile(
         os.path.join(self.chrome_root, 'ten.cc'))
     self.assertTrue(result)
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER',
-            '-std=c++14',
-            '-x',
-            'c++',
-            '-I[SRC]',
-            '-Wno-unknown-warning-option',
-            '-I[OUT]/b',
-            '-isystem[OUT]/a',
-            '-isystem', '[SRC]/build/c',
-            '-isystem', '/usr/lib/include'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I[OUT]/b', '-isystem[OUT]/a',
+        '-isystem', '[SRC]/build/c', '-isystem', '/usr/lib/include'
+    ])
 
   def testGetFlagsTwoPartI(self):
     result = self.ycm_extra_conf.FlagsForFile(
         os.path.join(self.chrome_root, 'eleven.cc'))
     self.assertTrue(result)
     self.assertTrue('flags' in result)
-    self.assertEquals(
-        self.NormalizeStringsInList(result['flags']), [
-            '-DUSE_CLANG_COMPLETER',
-            '-std=c++14',
-            '-x',
-            'c++',
-            '-I[SRC]',
-            '-Wno-unknown-warning-option',
-            '-I', '[OUT]/a',
-            '-I', '[OUT]/tag-eleven'
-        ])
+    self.assertEqual(self.NormalizeStringsInList(result['flags']), [
+        '-DUSE_CLANG_COMPLETER', '-std=c++14', '-x', 'c++', '-I[SRC]',
+        '-Wno-unknown-warning-option', '-I', '[OUT]/a', '-I', '[OUT]/tag-eleven'
+    ])
 
 
 if __name__ == '__main__':
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
index f5da8fed..f904b79 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.h
@@ -223,6 +223,10 @@
     // AVSampleBufferDisplayLayer, then |ca_layer| will point to |av_layer|.
     base::scoped_nsobject<AVSampleBufferDisplayLayer> av_layer_;
 
+    // Layer used to colorize content when it updates, if borders are
+    // enabled.
+    base::scoped_nsobject<CALayer> update_indicator_layer_;
+
    private:
     DISALLOW_COPY_AND_ASSIGN(ContentLayer);
   };
diff --git a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
index c959d1f..fc05082 100644
--- a/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
+++ b/ui/accelerated_widget_mac/ca_renderer_layer_tree.mm
@@ -559,13 +559,16 @@
       video_type_can_downgrade_(layer.video_type_can_downgrade_),
       protected_video_type_(layer.protected_video_type_),
       ca_layer_(std::move(layer.ca_layer_)),
-      av_layer_(std::move(layer.av_layer_)) {
+      av_layer_(std::move(layer.av_layer_)),
+      update_indicator_layer_(std::move(layer.update_indicator_layer_)) {
   DCHECK(!layer.ca_layer_);
   DCHECK(!layer.av_layer_);
+  DCHECK(!update_indicator_layer_);
 }
 
 CARendererLayerTree::ContentLayer::~ContentLayer() {
   [ca_layer_ removeFromSuperlayer];
+  [update_indicator_layer_ removeFromSuperlayer];
 }
 
 bool CARendererLayerTree::RootLayer::AddContentLayer(
@@ -959,27 +962,81 @@
 
   static bool show_borders = base::CommandLine::ForCurrentProcess()->HasSwitch(
       switches::kShowMacOverlayBorders);
-  if (show_borders) {
-    base::ScopedCFTypeRef<CGColorRef> color;
-    float alpha = update_anything ? 1.f : 0.2f;
-    if (type_ == CALayerType::kHDRCopier) {
-      // Blue represents an HDR layer.
-      color.reset(CGColorCreateGenericRGB(0, 0, 1, alpha));
-    } else if (type_ == CALayerType::kVideo) {
-      // Yellow represents an AV layer.
-      color.reset(CGColorCreateGenericRGB(1, 1, 0, alpha));
-    } else if (io_surface_) {
-      // Magenta represents a CALayer.
-      color.reset(CGColorCreateGenericRGB(1, 0, 1, alpha));
-    } else if (solid_color_contents_) {
-      // Cyan represents a solid color.
-      color.reset(CGColorCreateGenericRGB(0, 1, 1, alpha));
-    } else {
-      // Grey represents a CALayer that has not changed.
-      color.reset(CGColorCreateGenericRGB(0.5, 0.5, 0.5, 1));
+  static bool fill_layers = false;
+  if (show_borders || fill_layers) {
+    uint32_t pixel_format =
+        io_surface_ ? IOSurfaceGetPixelFormat(io_surface_) : 0;
+    float red = 0;
+    float green = 0;
+    float blue = 0;
+    switch (type_) {
+      case CALayerType::kHDRCopier:
+        // Blue represents a copied HDR layer.
+        blue = 1.0;
+        break;
+      case CALayerType::kVideo:
+        switch (pixel_format) {
+          case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
+            // Yellow is NV12 AVSampleBufferDisplayLayer
+            red = green = 1;
+            break;
+          case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
+            // Cyan is P010 AVSampleBufferDisplayLayer
+            green = blue = 1;
+            break;
+          default:
+            NOTREACHED();
+            break;
+        }
+        break;
+      case CALayerType::kDefault:
+        switch (pixel_format) {
+          case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange:
+            // Green is NV12 AVSampleBufferDisplayLayer
+            green = 1;
+            break;
+          case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange:
+            // Red is P010 AVSampleBufferDisplayLayer
+            red = 1;
+            break;
+          case 0:
+            // Grey is no IOSurface (a solid color layer).
+            red = green = blue = 0.5;
+            break;
+          default:
+            // Magenta is a non-video IOSurface.
+            red = blue = 1;
+            break;
+        }
+        break;
     }
-    [ca_layer_ setBorderWidth:1];
+
+    // If content did not change this frame, then use 0.5 opacity and a 1 pixel
+    // border. If it did change, then use full opacity and a 2 pixel border.
+    float alpha = update_anything ? 1.f : 0.5f;
+    [ca_layer_ setBorderWidth:update_anything ? 2 : 1];
+
+    // Set the layer color based on usage.
+    base::ScopedCFTypeRef<CGColorRef> color(
+        CGColorCreateGenericRGB(red, green, blue, alpha));
     [ca_layer_ setBorderColor:color];
+
+    // Flash indication of updates.
+    if (fill_layers) {
+      color.reset(CGColorCreateGenericRGB(red, green, blue, 1.0));
+      if (!update_indicator_layer_)
+        update_indicator_layer_.reset([[CALayer alloc] init]);
+      if (update_anything) {
+        [update_indicator_layer_ setBackgroundColor:color];
+        [update_indicator_layer_ setOpacity:0.25];
+        [ca_layer_ addSublayer:update_indicator_layer_];
+        [update_indicator_layer_
+            setFrame:CGRectMake(0, 0, CGRectGetWidth([ca_layer_ bounds]),
+                                CGRectGetHeight([ca_layer_ bounds]))];
+      } else {
+        [update_indicator_layer_ setOpacity:0.1];
+      }
+    }
   }
 }
 
diff --git a/ui/ozone/platform/scenic/scenic_window.cc b/ui/ozone/platform/scenic/scenic_window.cc
index 191db843..fb2806f 100644
--- a/ui/ozone/platform/scenic/scenic_window.cc
+++ b/ui/ozone/platform/scenic/scenic_window.cc
@@ -114,7 +114,9 @@
 
   visible_ = true;
 
-  view_.AddChild(node_);
+  if (!previous_view_is_zero_sized_) {
+    view_.AddChild(node_);
+  }
 
   // Call Present2() to ensure that the scenic session commands are processed,
   // which is necessary to receive metrics event from Scenic.
@@ -126,7 +128,9 @@
     return;
 
   visible_ = false;
-  node_.Detach();
+  if (!previous_view_is_zero_sized_) {
+    node_.Detach();
+  }
 }
 
 void ScenicWindow::Close() {
@@ -239,6 +243,26 @@
   if (screen)
     screen->OnWindowBoundsChanged(window_id_, bounds_);
 
+  // If the width or height of the window is zero, then we shouldn't render
+  // the node. Instead, we should detach it from its parent.
+  if (width == 0.f || height == 0.f) {
+    if (!previous_view_is_zero_sized_) {
+      if (visible_) {
+        node_.Detach();
+      }
+      previous_view_is_zero_sized_ = true;
+    }
+    return;
+  }
+
+  // Otherwise we add them back to the View.
+  if (previous_view_is_zero_sized_) {
+    if (visible_) {
+      view_.AddChild(node_);
+    }
+    previous_view_is_zero_sized_ = false;
+  }
+
   // Translate the node by half of the view dimensions to put it in the center
   // of the view.
   node_.SetTranslation(width / 2.0, height / 2.0, 0.f);
diff --git a/ui/ozone/platform/scenic/scenic_window.h b/ui/ozone/platform/scenic/scenic_window.h
index 77b7f3e0..f5538eee 100644
--- a/ui/ozone/platform/scenic/scenic_window.h
+++ b/ui/ozone/platform/scenic/scenic_window.h
@@ -161,6 +161,11 @@
 
   bool visible_ = false;
 
+  // Tracks if the View was previously hidden due to having a size of zero.
+  // If the View was previously zero sized, then we need to re-attach it to
+  // its parent before we change its size to non-zero; and vice versa.
+  bool previous_view_is_zero_sized_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(ScenicWindow);
 };
 
diff --git a/ui/webui/resources/cr_components/BUILD.gn b/ui/webui/resources/cr_components/BUILD.gn
index 5cb6265..c90c3d4 100644
--- a/ui/webui/resources/cr_components/BUILD.gn
+++ b/ui/webui/resources/cr_components/BUILD.gn
@@ -93,7 +93,11 @@
   in_folder = "./"
   out_folder = "$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_src_manifest"
-  in_files = [ "customize_themes/browser_proxy.js" ]
+  in_files = [
+    "customize_themes/browser_proxy.js",
+    "most_visited/browser_proxy.js",
+    "most_visited/window_proxy.js",
+  ]
 
   if (use_nss_certs) {
     in_files += [
@@ -105,12 +109,17 @@
 }
 
 preprocess_if_expr("preprocess_mojom") {
-  deps =
-      [ "//ui/webui/resources/cr_components/customize_themes:mojom_webui_js" ]
+  deps = [
+    "//ui/webui/resources/cr_components/customize_themes:mojom_webui_js",
+    "//ui/webui/resources/cr_components/most_visited:mojom_webui_js",
+  ]
   in_folder = "$root_gen_dir/mojom-webui/ui/webui/resources/cr_components"
   out_folder = "$preprocess_folder"
   out_manifest = "$target_gen_dir/$preprocess_mojom_manifest"
-  in_files = [ "customize_themes/customize_themes.mojom-webui.js" ]
+  in_files = [
+    "customize_themes/customize_themes.mojom-webui.js",
+    "most_visited/most_visited.mojom-webui.js",
+  ]
 }
 
 preprocess_if_expr("preprocess_generated") {
@@ -120,6 +129,8 @@
   out_manifest = "$target_gen_dir/$preprocess_gen_manifest"
   in_files = [
     "customize_themes/customize_themes.js",
+    "most_visited/most_visited.mojom-lite.js",
+    "most_visited/most_visited.js",
     "customize_themes/theme_icon.js",
     "managed_dialog/managed_dialog.js",
     "managed_footnote/managed_footnote.js",
@@ -399,6 +410,7 @@
     "customize_themes:closure_compile",
     "managed_dialog:closure_compile",
     "managed_footnote:closure_compile",
+    "most_visited:closure_compile",
     "omnibox:closure_compile",
   ]
 
@@ -413,6 +425,7 @@
     "customize_themes:web_components",
     "managed_dialog:web_components",
     "managed_footnote:web_components",
+    "most_visited:web_components",
     "omnibox:web_components",
   ]
   if (is_chromeos_ash) {
diff --git a/ui/webui/resources/cr_components/most_visited/BUILD.gn b/ui/webui/resources/cr_components/most_visited/BUILD.gn
new file mode 100644
index 0000000..815c149
--- /dev/null
+++ b/ui/webui/resources/cr_components/most_visited/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2021 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("//mojo/public/tools/bindings/mojom.gni")
+import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/html_to_js.gni")
+
+js_type_check("closure_compile") {
+  is_polymer3 = true
+  closure_flags = default_closure_args + mojom_js_args
+  deps = [
+    ":browser_proxy",
+    ":most_visited",
+    ":window_proxy",
+  ]
+}
+
+mojom("mojom") {
+  sources = [ "most_visited.mojom" ]
+  public_deps = [
+    "//mojo/public/mojom/base",
+    "//skia/public/mojom",
+    "//url/mojom:url_mojom_gurl",
+  ]
+  webui_module_path = "chrome://resources/cr_components/most_visited/"
+}
+
+js_library("browser_proxy") {
+  deps = [
+    ":mojom_webui_js",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+  ]
+}
+
+js_library("most_visited") {
+  deps = [
+    ":browser_proxy",
+    ":window_proxy",
+    "//skia/public/mojom:mojom_webui_js",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu.m",
+    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
+    "//ui/webui/resources/cr_elements/cr_toast:cr_toast.m",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:i18n_behavior.m",
+    "//ui/webui/resources/js:load_time_data.m",
+    "//ui/webui/resources/js/cr/ui:focus_outline_manager.m",
+  ]
+}
+
+js_library("window_proxy") {
+}
+
+html_to_js("web_components_local") {
+  js_files = [ "most_visited.js" ]
+}
+
+group("web_components") {
+  public_deps = [
+    ":mojom_webui_js",
+    ":web_components_local",
+  ]
+}
diff --git a/ui/webui/resources/cr_components/most_visited/OWNERS b/ui/webui/resources/cr_components/most_visited/OWNERS
new file mode 100644
index 0000000..1788a275
--- /dev/null
+++ b/ui/webui/resources/cr_components/most_visited/OWNERS
@@ -0,0 +1,4 @@
+file://chrome/browser/resources/new_tab_page/OWNERS
+
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/ui/webui/resources/cr_components/most_visited/browser_proxy.js b/ui/webui/resources/cr_components/most_visited/browser_proxy.js
new file mode 100644
index 0000000..ec65d1f
--- /dev/null
+++ b/ui/webui/resources/cr_components/most_visited/browser_proxy.js
@@ -0,0 +1,44 @@
+// Copyright 2021 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 {MostVisitedPageCallbackRouter, MostVisitedPageHandlerFactory, MostVisitedPageHandlerRemote} from './most_visited.mojom-webui.js';
+
+export class MostVisitedBrowserProxy {
+  /**
+   * @param {!MostVisitedPageHandlerRemote} handler
+   * @param {!MostVisitedPageCallbackRouter} callbackRouter
+   */
+  constructor(handler, callbackRouter) {
+    /** @type {!MostVisitedPageHandlerRemote} */
+    this.handler = handler;
+
+    /** @type {!MostVisitedPageCallbackRouter} */
+    this.callbackRouter = callbackRouter;
+  }
+
+  /** @return {!MostVisitedBrowserProxy} */
+  static getInstance() {
+    if (instance) {
+      return instance;
+    }
+    const callbackRouter = new MostVisitedPageCallbackRouter();
+    const handler = new MostVisitedPageHandlerRemote();
+    const factory = MostVisitedPageHandlerFactory.getRemote();
+    factory.createPageHandler(
+        callbackRouter.$.bindNewPipeAndPassRemote(),
+        handler.$.bindNewPipeAndPassReceiver());
+    instance = new MostVisitedBrowserProxy(handler, callbackRouter);
+    return instance;
+  }
+
+  /**
+   * @param {!MostVisitedBrowserProxy} obj
+   */
+  static setInstance(obj) {
+    instance = obj;
+  }
+}
+
+/** @type {?MostVisitedBrowserProxy} */
+let instance = null;
diff --git a/chrome/browser/resources/new_tab_page/most_visited.html b/ui/webui/resources/cr_components/most_visited/most_visited.html
similarity index 89%
rename from chrome/browser/resources/new_tab_page/most_visited.html
rename to ui/webui/resources/cr_components/most_visited/most_visited.html
index a6cd63b..0c5674a 100644
--- a/chrome/browser/resources/new_tab_page/most_visited.html
+++ b/ui/webui/resources/cr_components/most_visited/most_visited.html
@@ -3,11 +3,24 @@
     --icon-button-color-active: var(--google-grey-refresh-700);
     --icon-button-color: var(--google-grey-600);
     --icon-size: 48px;
+    --tile-background-color: rgb(229, 231, 232);
     --tile-hover-color: rgba(var(--google-grey-900-rgb), .1);
     --tile-size: 112px;
     --title-height: 32px;
   }
 
+  @media (prefers-color-scheme: dark) {
+    :host {
+      --tile-background-color: var(--google-grey-refresh-100);
+    }
+  }
+
+  :host([is-dark_]) {
+    --icon-button-color-active: var(--google-grey-refresh-300);
+    --icon-button-color: white;
+    --tile-hover-color: rgba(255, 255, 255, .1);
+  }
+
   #container {
     --content-width: calc(var(--column-count) * var(--tile-size)
       /* We add an extra pixel because rounding errors on different zooms can
@@ -44,11 +57,11 @@
     background-color: var(--google-grey-700);
   }
 
-  :host([use-white-add-icon]) #addShortcutIcon {
+  :host([use-white-tile-icon_]) #addShortcutIcon {
     background-color: white;
   }
 
-  :host([use-white-add-icon]) .query-tile-icon {
+  :host([use-white-tile-icon_]) .query-tile-icon {
     background-color: var(--google-grey-400);
   }
 
@@ -79,7 +92,7 @@
 
   :host-context(.focus-outline-visible) .tile:focus,
   :host-context(.focus-outline-visible) #addShortcut:focus {
-    box-shadow: var(--ntp-focus-shadow);
+    box-shadow: var(--most-visited-focus-shadow);
   }
 
   #addShortcut {
@@ -98,7 +111,7 @@
 
   .tile-icon {
     align-items: center;
-    background-color: var(--ntp-theme-shortcut-background-color);
+    background-color: var(--tile-background-color);
     border-radius: 50%;
     display: flex;
     flex-shrink: 0;
@@ -116,7 +129,7 @@
   .tile-title {
     align-items: center;
     border-radius: calc(var(--title-height) / 2 + 2px);
-    color: var(--ntp-theme-text-color);
+    color: var(--most-visited-text-color);
     display: flex;
     height: var(--title-height);
     line-height: calc(var(--title-height) / 2);
@@ -125,7 +138,7 @@
     width: 88px;
   }
 
-  :host([use-title-pill]) .tile-title {
+  :host([use-title-pill_]) .tile-title {
     background-color: white;
     color: var(--google-grey-800);
   }
@@ -135,7 +148,7 @@
     overflow: hidden;
     text-align: center;
     text-overflow: ellipsis;
-    text-shadow: var(--ntp-theme-text-shadow);
+    text-shadow: var(--most-visited-text-shadow);
     white-space: nowrap;
     width: 100%;
   }
@@ -147,7 +160,7 @@
     white-space: initial;
   }
 
-  :host([use-title-pill]) .tile-title span {
+  :host([use-title-pill_]) .tile-title span {
     text-shadow: none;
   }
 
@@ -198,7 +211,8 @@
   }
 </style>
 <div id="container" hidden$="[[!visible_]]"
-    style="--column-count: [[columnCount_]]; --row-count: [[rowCount_]];">
+    style="--tile-background-color: [[rgbaOrInherit_(theme.backgroundColor)]];
+           --column-count: [[columnCount_]]; --row-count: [[rowCount_]];">
   <dom-repeat id="tiles" items="[[tiles_]]" on-dom-change="onTilesRendered_">
     <template>
       <a class="tile" href$="[[item.url.url]]" title$="[[item.title]]"
diff --git a/chrome/browser/resources/new_tab_page/most_visited.js b/ui/webui/resources/cr_components/most_visited/most_visited.js
similarity index 89%
rename from chrome/browser/resources/new_tab_page/most_visited.js
rename to ui/webui/resources/cr_components/most_visited/most_visited.js
index 0360945..8d105913 100644
--- a/chrome/browser/resources/new_tab_page/most_visited.js
+++ b/ui/webui/resources/cr_components/most_visited/most_visited.js
@@ -10,19 +10,23 @@
 import 'chrome://resources/cr_elements/cr_input/cr_input.m.js';
 import 'chrome://resources/cr_elements/cr_toast/cr_toast.m.js';
 import 'chrome://resources/cr_elements/hidden_style_css.m.js';
-import 'chrome://resources/mojo/mojo/public/js/mojo_bindings_lite.js';
-import 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-lite.js';
 
 import {assert} from 'chrome://resources/js/assert.m.js';
+import {skColorToRgba} from 'chrome://resources/js/color_utils.js';
 import {isMac} from 'chrome://resources/js/cr.m.js';
 import {FocusOutlineManager} from 'chrome://resources/js/cr/ui/focus_outline_manager.m.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.m.js';
+import {I18nBehavior} from 'chrome://resources/js/i18n_behavior.m.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {hasKeyModifiers} from 'chrome://resources/js/util.m.js';
+import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
+import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {I18nBehavior, loadTimeData} from './i18n_setup.js';
-import {NewTabPageProxy} from './new_tab_page_proxy.js';
-import {WindowProxy} from './window_proxy.js';
+import {MostVisitedBrowserProxy} from './browser_proxy.js';
+import {MostVisitedPageCallbackRouter, MostVisitedPageHandlerRemote, MostVisitedTheme, MostVisitedTile} from './most_visited.mojom-webui.js';
+import {MostVisitedWindowProxy} from './window_proxy.js';
 
 /**
  * @enum {number}
@@ -90,10 +94,10 @@
  * @polymer
  * @extends {PolymerElement}
  */
-class MostVisitedElement extends mixinBehaviors
+export class MostVisitedElement extends mixinBehaviors
 ([I18nBehavior], PolymerElement) {
   static get is() {
-    return 'ntp-most-visited';
+    return 'cr-most-visited';
   }
 
   static get template() {
@@ -102,20 +106,29 @@
 
   static get properties() {
     return {
+      /** @type {?MostVisitedTheme} */
+      theme: Object,
+
       /**
-       * When the tile icon background is dark, the add icon color is white for
+       * When the tile icon background is dark, the icon color is white for
        * contrast. This can be used to determine the color of the tile hover as
        * well.
+       * @private
        */
-      useWhiteAddIcon: {
+      useWhiteTileIcon_: {
         type: Boolean,
         reflectToAttribute: true,
+        computed: `computeUseWhiteTileIcon_(theme)`,
       },
 
-      /* If true wraps the tile titles in white pills. */
-      useTitlePill: {
+      /**
+       * If true wraps the tile titles in white pills.
+       * @private
+       */
+      useTitlePill_: {
         type: Boolean,
         reflectToAttribute: true,
+        computed: `computeUseTitlePill_(theme)`,
       },
 
       /** @private */
@@ -131,7 +144,10 @@
       },
 
       /** @private */
-      customLinksEnabled_: Boolean,
+      customLinksEnabled_: {
+        type: Boolean,
+        reflectToAttribute: true,
+      },
 
       /** @private */
       dialogTileTitle_: String,
@@ -171,6 +187,13 @@
             dialogShortcutAlreadyExists_)`,
       },
 
+      /** @private */
+      isDark_: {
+        type: Boolean,
+        reflectToAttribute: true,
+        computed: `computeIsDark_(theme)`,
+      },
+
       /**
        * Used to hide hover style and cr-icon-button of tiles while the tiles
        * are being reordered.
@@ -208,7 +231,7 @@
       /** @private {!ScreenWidth} */
       screenWidth_: Number,
 
-      /** @private {!Array<!newTabPage.mojom.MostVisitedTile>} */
+      /** @private {!Array<!MostVisitedTile>} */
       tiles_: Array,
 
       /** @private */
@@ -231,15 +254,22 @@
   constructor() {
     performance.mark('most-visited-creation-start');
     super();
+
     /** @private {boolean} */
     this.adding_ = false;
-    const {callbackRouter, handler} = NewTabPageProxy.getInstance();
-    /** @private {!newTabPage.mojom.PageCallbackRouter} */
-    this.callbackRouter_ = callbackRouter;
-    /** @private {newTabPage.mojom.PageHandlerRemote} */
-    this.pageHandler_ = handler;
+
+    /** @private {!MostVisitedPageCallbackRouter} */
+    this.callbackRouter_ = MostVisitedBrowserProxy.getInstance().callbackRouter;
+
+    /** @private {!MostVisitedPageHandlerRemote} */
+    this.pageHandler_ = MostVisitedBrowserProxy.getInstance().handler;
+
+    /** @private {!MostVisitedWindowProxy} */
+    this.windowProxy_ = MostVisitedWindowProxy.getInstance();
+
     /** @private {?number} */
     this.setMostVisitedInfoListenerId_ = null;
+
     /** @private {number} */
     this.actionMenuTargetIndex_ = -1;
 
@@ -249,6 +279,7 @@
      * @private {(!{x: number, y: number}|null)}
      */
     this.dragOffset_ = null;
+
     /** @private {!Array<!DOMRect>} */
     this.tileRects_ = [];
   }
@@ -283,8 +314,6 @@
   /** @override */
   disconnectedCallback() {
     super.disconnectedCallback();
-    this.callbackRouter_.removeListener(
-        assert(this.setMostVisitedInfoListenerId_));
     this.mediaListenerWideWidth_.removeListener(
         assert(this.boundOnWidthChange_));
     this.mediaListenerMediumWidth_.removeListener(
@@ -302,11 +331,11 @@
     this.boundOnWidthChange_ = this.updateScreenWidth_.bind(this);
     /** @private {!MediaQueryList} */
     this.mediaListenerWideWidth_ =
-        WindowProxy.getInstance().matchMedia('(min-width: 672px)');
+        this.windowProxy_.matchMedia('(min-width: 672px)');
     this.mediaListenerWideWidth_.addListener(this.boundOnWidthChange_);
     /** @private {!MediaQueryList} */
     this.mediaListenerMediumWidth_ =
-        WindowProxy.getInstance().matchMedia('(min-width: 560px)');
+        this.windowProxy_.matchMedia('(min-width: 560px)');
     this.mediaListenerMediumWidth_.addListener(this.boundOnWidthChange_);
     this.updateScreenWidth_();
     /** @private {!function(Event)} */
@@ -318,6 +347,15 @@
     performance.measure('most-visited-creation', 'most-visited-creation-start');
   }
 
+  /**
+   * @param {?SkColor} skColor
+   * @return {string}
+   * @private
+   */
+  rgbaOrInherit_(skColor) {
+    return skColor ? skColorToRgba(skColor) : 'inherit';
+  }
+
   /** @private */
   clearForceHover_() {
     const forceHover = this.shadowRoot.querySelector('.force-hover');
@@ -425,6 +463,30 @@
   }
 
   /**
+   * @return {boolean}
+   * @private
+   */
+  computeIsDark_() {
+    return this.theme ? this.theme.isDark : false;
+  }
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  computeUseWhiteTileIcon_() {
+    return this.theme ? this.theme.useWhiteTileIcon : false;
+  }
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  computeUseTitlePill_() {
+    return this.theme ? this.theme.useTitlePill : false;
+  }
+
+  /**
    * If a pointer is over a tile rect that is different from the one being
    * dragged, the dragging tile is moved to the new position. The reordering
    * is done in the DOM and the by the |reorderMostVisitedTile()| call. This is
@@ -556,7 +618,7 @@
   }
 
   /**
-   * @param {!url.mojom.Url} url
+   * @param {!Url} url
    * @return {string}
    * @private
    */
@@ -580,14 +642,13 @@
   }
 
   /**
-   * @param {!newTabPage.mojom.MostVisitedTile} tile
+   * @param {!MostVisitedTile} tile
    * @return {string}
    * @private
    */
   getTileTitleDirectionClass_(tile) {
-    return tile.titleDirection === mojoBase.mojom.TextDirection.RIGHT_TO_LEFT ?
-        'title-rtl' :
-        'title-ltr';
+    return tile.titleDirection === TextDirection.RIGHT_TO_LEFT ? 'title-rtl' :
+                                                                 'title-ltr';
   }
 
   /**
@@ -886,7 +947,6 @@
   /**
    * @param {string} msgId
    * @param {boolean} showButtons
-   * @private
    */
   toast_(msgId, showButtons) {
     this.toastContent_ = loadTimeData.getString(msgId);
@@ -896,10 +956,9 @@
 
   /**
    * @param {number} index
-   * @return {!Promise}
    * @private
    */
-  async tileRemove_(index) {
+  tileRemove_(index) {
     const {url, isQueryTile} = this.tiles_[index];
     this.pageHandler_.deleteMostVisitedTile(url);
     // Do not show the toast buttons when a query tile is removed unless it is a
@@ -926,7 +985,7 @@
     performance.measure('most-visited-rendered');
     this.pageHandler_.onMostVisitedTilesRendered(
         this.tiles_.slice(0, assert(this.maxVisibleTiles_)),
-        WindowProxy.getInstance().now());
+        this.windowProxy_.now());
   }
 }
 
diff --git a/ui/webui/resources/cr_components/most_visited/most_visited.mojom b/ui/webui/resources/cr_components/most_visited/most_visited.mojom
new file mode 100644
index 0000000..b10f146
--- /dev/null
+++ b/ui/webui/resources/cr_components/most_visited/most_visited.mojom
@@ -0,0 +1,95 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module most_visited.mojom;
+
+import "mojo/public/mojom/base/text_direction.mojom";
+import "mojo/public/mojom/base/time.mojom";
+import "skia/public/mojom/skcolor.mojom";
+import "url/mojom/url.mojom";
+
+struct MostVisitedTile {
+  string title;
+  mojo_base.mojom.TextDirection title_direction;
+  url.mojom.Url url;
+  bool is_query_tile;
+
+  // ======= METRICS =======
+  // Identifier of most visited entry source (e.g. top sites).
+  int32 source;
+  // Identifier of most visited entry title source (e.g. page's title tag).
+  int32 title_source;
+  // Time the most visited entry was generated (e.g. received by a suggestion
+  // server).
+  mojo_base.mojom.Time data_generation_time;
+};
+
+// Theme settings for the NTP MV tiles.
+struct MostVisitedTheme {
+  skia.mojom.SkColor background_color;
+  // True if |background_color| is dark.
+  bool use_white_tile_icon;
+  // True if the shortcuts titles should be wrapped in a pill.
+  bool use_title_pill;
+  // True if the theme is dark (e.g. NTP background color is dark).
+  bool is_dark;
+};
+
+struct MostVisitedInfo {
+  bool custom_links_enabled;
+  bool visible;
+  array<MostVisitedTile> tiles;
+};
+
+// Used to bootstrap the bidirectional communication.
+interface MostVisitedPageHandlerFactory {
+  // The component calls this method when it is first initialized.
+  CreatePageHandler(
+           pending_remote<MostVisitedPage> page,
+           pending_receiver<MostVisitedPageHandler> handler);
+};
+
+// Browser-side handler for requests from WebUI page.
+interface MostVisitedPageHandler {
+  // Adds tile.
+  AddMostVisitedTile(url.mojom.Url url, string title) => (bool success);
+  // Deletes tile by |url|.
+  DeleteMostVisitedTile(url.mojom.Url url);
+  // Moves tile identified by url to a new position at index |new_pos|.
+  ReorderMostVisitedTile(url.mojom.Url url, uint8 new_pos);
+  // Replaces the custom and most-visited tiles with the default tile set.
+  RestoreMostVisitedDefaults();
+  // Undoes the last action done to the tiles (add, delete, reorder, restore or
+  // update). Note that only the last action can be undone.
+  UndoMostVisitedTileAction();
+  // Called to get updates on visibility of most visited tiles, whether custom
+  // links are enabled, and the tiles themselves.
+  UpdateMostVisitedInfo();
+  // Updates a tile by url.
+  UpdateMostVisitedTile(url.mojom.Url url,
+                        url.mojom.Url new_url,
+                        string new_title) => (bool success);
+  // ======= METRICS =======
+  // Logs that |tiles| were displayed / updated at |time|. The first instance of
+  // this event is used as a proxy for when the NTP has finished loading.
+  OnMostVisitedTilesRendered(array<MostVisitedTile> tiles, double time);
+  // Logs that |tile| at position |index| was triggered to navigate to that
+  // most visited entry.
+  // |mouse_button| indicates which mouse button was pressed on the entry. See
+  // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+  OnMostVisitedTileNavigation(MostVisitedTile tile,
+                              uint32 index,
+                              uint8 mouse_button,
+                              bool alt_key,
+                              bool ctrl_key,
+                              bool meta_key,
+                              bool shift_key);
+};
+
+// WebUI-side handler for requests from the browser.
+interface MostVisitedPage {
+  // Updates the page with the visibility of most visited tiles, whether custom
+  // links are enabled, and the tiles themselves.
+  SetMostVisitedInfo(MostVisitedInfo info);
+};
diff --git a/ui/webui/resources/cr_components/most_visited/window_proxy.js b/ui/webui/resources/cr_components/most_visited/window_proxy.js
new file mode 100644
index 0000000..cfd5dbf
--- /dev/null
+++ b/ui/webui/resources/cr_components/most_visited/window_proxy.js
@@ -0,0 +1,34 @@
+// Copyright 2021 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.
+
+/**
+ * Abstracts built-in JS functions in order to mock in tests.
+ */
+export class MostVisitedWindowProxy {
+  /**
+   * @param {string} query
+   * @return {!MediaQueryList}
+   */
+  matchMedia(query) {
+    return window.matchMedia(query);
+  }
+
+  /** @return {number} */
+  now() {
+    return Date.now();
+  }
+
+  /** @return {!MostVisitedWindowProxy} */
+  static getInstance() {
+    return instance || (instance = new MostVisitedWindowProxy());
+  }
+
+  /** @param {!MostVisitedWindowProxy} obj */
+  static setInstance(obj) {
+    instance = obj;
+  }
+}
+
+/** @type {?MostVisitedWindowProxy} */
+let instance = null;
diff --git a/weblayer/browser/browser_controls_navigation_state_handler.cc b/weblayer/browser/browser_controls_navigation_state_handler.cc
index fe45c01b..c27dab4 100644
--- a/weblayer/browser/browser_controls_navigation_state_handler.cc
+++ b/weblayer/browser/browser_controls_navigation_state_handler.cc
@@ -88,7 +88,7 @@
       render_frame_host->GetMainFrame() == render_frame_host;
   if (is_main_frame)
     ScheduleStopDelayedForceShow();
-  if (render_frame_host->IsCurrent() &&
+  if (render_frame_host->IsActive() &&
       (render_frame_host == web_contents()->GetMainFrame())) {
     UpdateState();
   }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py
index 300806a9..ca2f0c1 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py
+++ b/weblayer/browser/java/org/chromium/weblayer_private/interfaces/PRESUBMIT.py
@@ -15,6 +15,8 @@
 import sys
 import tempfile
 
+USE_PYTHON3 = True
+
 _INCOMPATIBLE_API_ERROR_STRING = """You have made an incompatible API change.
 Generally this means one of the following:
   A function has been removed.