diff --git a/DEPS b/DEPS
index 518473a4..2042322 100644
--- a/DEPS
+++ b/DEPS
@@ -245,15 +245,15 @@
   # 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': '3518875c21ea43cb1d7c35f3d023b172a6426aa2',
+  'skia_revision': '1f2435f46b48a19b3818df20085af685119cb8e0',
   # 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': 'ddf3c0a3f5b701ff55ea61be84bed5a9a6a63e35',
+  'v8_revision': '50e6d38885688ec38fcc5ccd214ac16eb85ee389',
   # 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': 'b756b2c2744aeff7a8207a44fe8772b1c5c97c9d',
+  'angle_revision': '464396c7b9edd05a329ed33135e53a844dca699a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -320,7 +320,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': '6abfe068bb1e3e203462fdf4d25825ee1287c84c',
+  'devtools_frontend_revision': '4d1f2028c3076194a05decd1a94574ff48f2c22b',
   # 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.
@@ -360,7 +360,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': '012840b75229c32924ed1dfcd643be4104fae155',
+  'dawn_revision': '7c5000abeaec4b9ecb7a8368c3519d7b4d3ba9ec',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -851,7 +851,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': '-qxJHfqWawBjacl9Uo4Q7C8p8kibjIFWMiQMm4jg9swC',
+          'version': 'nfjVLoN-XGTXpgb9_rGZXUPYfs_5j9hzi26MwMKtcsYC',
       },
     ],
     'condition': 'checkout_android',
@@ -1655,7 +1655,7 @@
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '62d7d0c928c9a040dce96aa2f16c00e7e67d59cb',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@5b6cf1b845ca80985d0806e1622c03690c02ea51',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@3a6f074dbd38e318d7b36551276c0dca5870808b',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'ebe84bec02c041d28f902da0214bf442743fc907',
@@ -1694,7 +1694,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'ed042183cddff326234a692cad0de93c1eb8e81f',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'e4836121649400b78407a5237bb2a1f8458ee9c6',
+    Var('webrtc_git') + '/src.git' + '@' + '63c3b136a87db32d3d5760466c681bdc125d4630',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1764,7 +1764,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@81bfd691d57c4c8e6fd302799bc5e6a03570a0d0',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@02cbb4e7969f8898a0d480347a2a5cba52373a6f',
     'condition': 'checkout_src_internal',
   },
 
@@ -1816,7 +1816,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': '_-MeoCCkeQeoTZdmNg_os9nIKbT1IVHKmu3qyyuUensC',
+        'version': 'uYHeCyvV2v2hhOPtVpE12ZFyS8tmPfzsa-CvXvP7J4MC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 7688583..8e49273 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -167,6 +167,8 @@
     "ambient/model/ambient_slideshow_photo_config.cc",
     "ambient/model/ambient_slideshow_photo_config.h",
     "ambient/resources/ambient_animation_static_resources.h",
+    "ambient/ui/ambient_animation_resizer.cc",
+    "ambient/ui/ambient_animation_resizer.h",
     "ambient/ui/ambient_animation_view.cc",
     "ambient/ui/ambient_animation_view.h",
     "ambient/ui/ambient_background_image_view.cc",
@@ -2298,6 +2300,7 @@
     "ambient/model/ambient_animation_photo_config_unittest.cc",
     "ambient/model/ambient_animation_photo_provider_unittest.cc",
     "ambient/model/ambient_backend_model_unittest.cc",
+    "ambient/ui/ambient_animation_resizer_unittest.cc",
     "ambient/ui/ambient_animation_view_unittest.cc",
     "ambient/ui/ambient_container_view_unittest.cc",
     "ambient/ui/media_string_view_unittest.cc",
diff --git a/ash/ambient/ui/ambient_animation_resizer.cc b/ash/ambient/ui/ambient_animation_resizer.cc
new file mode 100644
index 0000000..6d76262
--- /dev/null
+++ b/ash/ambient/ui/ambient_animation_resizer.cc
@@ -0,0 +1,59 @@
+// Copyright 2022 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 "ash/ambient/ui/ambient_animation_resizer.h"
+
+#include "base/check.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/lottie/animation.h"
+#include "ui/views/controls/animated_image_view.h"
+
+namespace ash {
+
+// static
+void AmbientAnimationResizer::Resize(
+    views::AnimatedImageView& animated_image_view) {
+  DCHECK(animated_image_view.animated_image());
+  gfx::Size animation_size =
+      animated_image_view.animated_image()->GetOriginalSize();
+  DCHECK(!animation_size.IsEmpty());
+  gfx::Rect destination_bounds = animated_image_view.GetContentsBounds();
+  DCHECK(!destination_bounds.IsEmpty());
+  gfx::Size animation_resized;
+  if (destination_bounds.width() >= destination_bounds.height()) {
+    // Landscape: Scale the width and crop the height.
+    float width_scale_factor =
+        static_cast<float>(destination_bounds.width()) / animation_size.width();
+    animation_resized.set_width(destination_bounds.width());
+    // TODO(esum): Add metrics for the number of times the new scaled height
+    // is less than the destination height. UX did not intend for this to
+    // happen, so it's worth recording.
+    animation_resized.set_height(
+        base::ClampRound(animation_size.height() * width_scale_factor));
+    animated_image_view.SetVerticalAlignment(
+        views::ImageViewBase::Alignment::kCenter);
+  } else {
+    // Portrait: Scale the height and crop the width.
+    float height_scale_factor =
+        static_cast<float>(destination_bounds.height()) /
+        animation_size.height();
+    animation_resized.set_height(destination_bounds.height());
+    animation_resized.set_width(
+        base::ClampRound(animation_size.width() * height_scale_factor));
+    animated_image_view.SetHorizontalAlignment(
+        views::ImageViewBase::Alignment::kCenter);
+  }
+  // The animation's new scaled size has been computed above.
+  // AnimatedImageView::SetImageSize() takes care of both a) applying the
+  // scaled size and b) cropping by translating the canvas before painting such
+  // that the rescaled animation's origin resides outside the boundaries of the
+  // view. The portions of the rescaled animation that reside outside of the
+  // view's boundaries ultimately get cropped.
+  animated_image_view.SetImageSize(animation_resized);
+}
+
+}  // namespace ash
diff --git a/ash/ambient/ui/ambient_animation_resizer.h b/ash/ambient/ui/ambient_animation_resizer.h
new file mode 100644
index 0000000..d41deb4
--- /dev/null
+++ b/ash/ambient/ui/ambient_animation_resizer.h
@@ -0,0 +1,61 @@
+// Copyright 2022 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 ASH_AMBIENT_UI_AMBIENT_ANIMATION_RESIZER_H_
+#define ASH_AMBIENT_UI_AMBIENT_ANIMATION_RESIZER_H_
+
+#include "ash/ash_export.h"
+
+namespace views {
+class AnimatedImageView;
+}  // namespace views
+
+namespace ash {
+
+// Each Skottie animation has fixed dimensions baked into its corresponding
+// Lottie file that were picked by UX when the animation was designed. On the
+// other hand, the UI view rendering the animation can have arbitrary
+// dimensions. AmbientAnimationResizer modifies an AnimatedImageView such that
+// its underlying Skottie animation fills its bounds according to the following
+// UX requirements:
+//
+// Let's say the view is landscape with dimensions 1000x600 and the
+// corresponding Lottie animation has dimensions 2000x1500. The animation
+// must first be scaled down to match the UI's width. Then, the animation's
+// height must be center-aligned and cropped:
+// Width scale factor = 1000 / 2000 = 0.5
+// New Animation width = 2000 * 0.5 = 1000
+// New Animation height uncropped = 1500 * 0.5 = 750
+// At this point, 750 > 600, so 150 pixels must be cropped from the height.
+// Since the animation should be center-aligned vertically, 75 pixels will be
+// cropped from the top and 75 pixels will be cropped from the bottom.
+//
+// UX has intentionally designed the ambient animations such that the above
+// case is the most common. That is to say: the animation's dimensions are
+// already larger than most views' expected dimensions, and the animation's
+// width:height aspect ratio is intentionally smaller than most if not all of
+// the expected UI aspect ratios (meaning the height gets cropped).
+//
+// Corner cases:
+// 1) If the animation's new uncropped height is *less* than the the view's
+//    height, keep the new height as is and vertically center-align the
+//    animation within the view's bounds.
+// 2) If the UI's width is greater than the animation's width, run the exact
+//    same logic (scale the width, then crop the height). The only difference is
+//    the animation will be scaled "up" instead of "down".
+//
+// For portrait mode, the same logic is applied except with the dimensions
+// reversed (scale the height, then crop the width).
+class ASH_EXPORT AmbientAnimationResizer {
+ public:
+  // Resizes the |animated_image_view| according to the UX requirements
+  // described above. The |animated_image_view| must:
+  // * Have initialized non-empty bounds.
+  // * Have been initialized already with a lottie::Animation.
+  static void Resize(views::AnimatedImageView& animated_image_view);
+};
+
+}  // namespace ash
+
+#endif  // ASH_AMBIENT_UI_AMBIENT_ANIMATION_RESIZER_H_
diff --git a/ash/ambient/ui/ambient_animation_resizer_unittest.cc b/ash/ambient/ui/ambient_animation_resizer_unittest.cc
new file mode 100644
index 0000000..53aaaaa7
--- /dev/null
+++ b/ash/ambient/ui/ambient_animation_resizer_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2022 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 "ash/ambient/ui/ambient_animation_resizer.h"
+
+#include <memory>
+
+#include "base/logging.h"
+#include "cc/test/skia_common.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/vector2d.h"
+#include "ui/lottie/animation.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/animated_image_view.h"
+
+namespace ash {
+namespace {
+
+using ::testing::Eq;
+
+std::unique_ptr<views::AnimatedImageView> CreateAnimatedImageView(
+    const gfx::Size& animation_size,
+    const gfx::Rect& view_bounds) {
+  auto view = std::make_unique<views::AnimatedImageView>();
+  view->SetAnimatedImage(std::make_unique<lottie::Animation>(
+      cc::CreateSkottie(animation_size,
+                        /*duration_secs=*/1)));
+  view->SetBoundsRect(view_bounds);
+  return view;
+}
+
+}  // namespace
+
+TEST(AmbientAnimationResizerTest, LandscapeScalesDownWidthAndCropsHeight) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(2000, 1500), gfx::Rect(1000, 600));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_THAT(view->GetImageBounds(), Eq(gfx::Rect(0, -75, 1000, 750)));
+}
+
+TEST(AmbientAnimationResizerTest,
+     LandscapeScalesDownWidthAndCropsHeightWithInsets) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(2000, 1500), gfx::Rect(1000, 600));
+  // Content bounds are 500 x 300.
+  view->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(/*vertical=*/150, /*horizontal*/ 250)));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_TRUE(view->GetImageBounds().ApproximatelyEqual(
+      gfx::Rect(250, 150 - (75 / 2), 500, 375), /*tolerance=*/1));
+}
+
+TEST(AmbientAnimationResizerTest, LandscapeScalesDownWidthAndHeight) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(2000, 1500), gfx::Rect(1000, 750));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_THAT(view->GetImageBounds(), Eq(gfx::Rect(0, 0, 1000, 750)));
+}
+
+TEST(AmbientAnimationResizerTest,
+     LandscapeScalesDownWidthAndDoesNotCropHeight) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(2000, 1500), gfx::Rect(1000, 800));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_THAT(view->GetImageBounds(), Eq(gfx::Rect(0, 25, 1000, 750)));
+}
+
+TEST(AmbientAnimationResizerTest, LandscapeScalesUpByWidth) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(2000, 1500), gfx::Rect(2500, 1500));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_TRUE(view->GetImageBounds().ApproximatelyEqual(
+      gfx::Rect(0, -(375 / 2), 2500, 1875), /*tolerance=*/1));
+}
+
+TEST(AmbientAnimationResizerTest, PortraitScalesDownWidthAndCropsHeight) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(1500, 2000), gfx::Rect(600, 1000));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_THAT(view->GetImageBounds(), Eq(gfx::Rect(-75, 0, 750, 1000)));
+}
+
+TEST(AmbientAnimationResizerTest,
+     PortraitScalesDownWidthAndCropsHeightWithInsets) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(1500, 2000), gfx::Rect(600, 1000));
+  // Content bounds are 300 x 500.
+  view->SetBorder(views::CreateEmptyBorder(
+      gfx::Insets(/*vertical=*/250, /*horizontal*/ 150)));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_TRUE(view->GetImageBounds().ApproximatelyEqual(
+      gfx::Rect(150 - (75 / 2), 250, 375, 500), /*tolerance=*/1));
+}
+
+TEST(AmbientAnimationResizerTest, PortraitScalesDownWidthAndHeight) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(1500, 2000), gfx::Rect(750, 1000));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_THAT(view->GetImageBounds(), Eq(gfx::Rect(0, 0, 750, 1000)));
+}
+
+TEST(AmbientAnimationResizerTest, PortraitScalesDownWidthAndDoesNotCropHeight) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(1500, 2000), gfx::Rect(800, 1000));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_THAT(view->GetImageBounds(), Eq(gfx::Rect(25, 0, 750, 1000)));
+}
+
+TEST(AmbientAnimationResizerTest, PortraitScalesUpByWidth) {
+  auto view =
+      CreateAnimatedImageView(gfx::Size(1500, 2000), gfx::Rect(1500, 2500));
+  AmbientAnimationResizer::Resize(*view);
+  EXPECT_TRUE(view->GetImageBounds().ApproximatelyEqual(
+      gfx::Rect(-(375 / 2), 0, 1875, 2500), /*tolerance=*/1));
+}
+
+}  // namespace ash
diff --git a/ash/ambient/ui/ambient_animation_view.cc b/ash/ambient/ui/ambient_animation_view.cc
index d04438a..de41fed6 100644
--- a/ash/ambient/ui/ambient_animation_view.cc
+++ b/ash/ambient/ui/ambient_animation_view.cc
@@ -11,6 +11,7 @@
 #include "ash/ambient/model/ambient_backend_model.h"
 #include "ash/ambient/model/ambient_photo_config.h"
 #include "ash/ambient/resources/ambient_animation_static_resources.h"
+#include "ash/ambient/ui/ambient_animation_resizer.h"
 #include "ash/ambient/ui/ambient_view_delegate.h"
 #include "ash/ambient/ui/ambient_view_ids.h"
 #include "base/check.h"
@@ -53,6 +54,7 @@
       cc::SkottieColorMap(), &animation_photo_provider_);
   animation->SetAnimationObserver(this);
   animated_image_view_->SetAnimatedImage(std::move(animation));
+  animated_image_view_observer_.Observe(animated_image_view_);
 }
 
 void AmbientAnimationView::AnimationWillStartPlaying(
@@ -65,10 +67,11 @@
   event_handler_->OnMarkerHit(AmbientPhotoConfig::Marker::kUiCycleEnded);
 }
 
-void AmbientAnimationView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
-  DVLOG(4) << __func__ << " from " << previous_bounds.ToString() << " to "
-           << bounds().ToString();
-  if (size().IsEmpty())
+void AmbientAnimationView::OnViewBoundsChanged(View* observed_view) {
+  DCHECK_EQ(observed_view, static_cast<View*>(animated_image_view_));
+  DVLOG(4) << __func__ << " to "
+           << animated_image_view_->GetContentsBounds().ToString();
+  if (animated_image_view_->GetContentsBounds().IsEmpty())
     return;
 
   // By default, the |animated_image_view_| will render the animation with the
@@ -76,8 +79,13 @@
   // at the view's full bounds, wait for the view's initial layout to happen
   // so that its proper bounds become available (they are 0x0 initially) before
   // starting the animation playback.
-  DVLOG(4) << "View bounds available. Beginning rendering...";
-  animated_image_view_->SetImageSize(size());
+  gfx::Rect previous_animation_bounds = animated_image_view_->GetImageBounds();
+  AmbientAnimationResizer::Resize(*animated_image_view_);
+  DVLOG(4)
+      << "View bounds available. Resized animation with native size "
+      << animated_image_view_->animated_image()->GetOriginalSize().ToString()
+      << " from " << previous_animation_bounds.ToString() << " to "
+      << animated_image_view_->GetImageBounds().ToString();
   animated_image_view_->Play();
 }
 
diff --git a/ash/ambient/ui/ambient_animation_view.h b/ash/ambient/ui/ambient_animation_view.h
index f390852..b53070de 100644
--- a/ash/ambient/ui/ambient_animation_view.h
+++ b/ash/ambient/ui/ambient_animation_view.h
@@ -9,8 +9,10 @@
 
 #include "ash/ambient/model/ambient_animation_photo_provider.h"
 #include "ash/ash_export.h"
+#include "base/scoped_observation.h"
 #include "ui/lottie/animation_observer.h"
 #include "ui/views/view.h"
+#include "ui/views/view_observer.h"
 
 namespace views {
 class AnimatedImageView;
@@ -23,7 +25,8 @@
 class AmbientViewEventHandler;
 
 class ASH_EXPORT AmbientAnimationView : public views::View,
-                                        public lottie::AnimationObserver {
+                                        public lottie::AnimationObserver,
+                                        public views::ViewObserver {
  public:
   METADATA_HEADER(AmbientAnimationView);
 
@@ -41,7 +44,7 @@
   void AnimationWillStartPlaying(const lottie::Animation* animation) override;
   void AnimationCycleEnded(const lottie::Animation* animation) override;
 
-  void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
+  void OnViewBoundsChanged(View* observed_view) override;
 
   AmbientViewEventHandler* const event_handler_;
 
@@ -50,6 +53,8 @@
   AmbientAnimationPhotoProvider animation_photo_provider_;
 
   views::AnimatedImageView* animated_image_view_ = nullptr;
+  base::ScopedObservation<View, ViewObserver> animated_image_view_observer_{
+      this};
 };
 
 }  // namespace ash
diff --git a/ash/quick_pair/repository/fast_pair/saved_device_registry.cc b/ash/quick_pair/repository/fast_pair/saved_device_registry.cc
index b659e2e6..c432af8 100644
--- a/ash/quick_pair/repository/fast_pair/saved_device_registry.cc
+++ b/ash/quick_pair/repository/fast_pair/saved_device_registry.cc
@@ -56,10 +56,8 @@
     return absl::nullopt;
   }
 
-  std::string encoded;
   std::string decoded;
-  result->GetAsString(&encoded);
-  if (!base::Base64Decode(encoded, &decoded)) {
+  if (!base::Base64Decode(result->GetString(), &decoded)) {
     QP_LOG(WARNING) << __func__
                     << ": Failed to decode the account key from Base64.";
     return absl::nullopt;
diff --git a/ash/webui/camera_app_ui/camera_app_ui.cc b/ash/webui/camera_app_ui/camera_app_ui.cc
index 623f4678..4a39291e 100644
--- a/ash/webui/camera_app_ui/camera_app_ui.cc
+++ b/ash/webui/camera_app_ui/camera_app_ui.cc
@@ -4,11 +4,11 @@
 
 #include "ash/webui/camera_app_ui/camera_app_ui.h"
 
+#include "ash/grit/ash_camera_app_resources_map.h"
 #include "ash/public/cpp/window_properties.h"
 #include "ash/webui/camera_app_ui/camera_app_helper_impl.h"
 #include "ash/webui/camera_app_ui/resources.h"
 #include "ash/webui/camera_app_ui/url_constants.h"
-#include "ash/webui/grit/ash_camera_app_resources_map.h"
 #include "base/bind.h"
 #include "base/strings/string_util.h"
 #include "components/arc/intent_helper/arc_intent_helper_bridge.h"
diff --git a/ash/webui/color_internals/DEPS b/ash/webui/color_internals/DEPS
index 4244eece..92c2044 100644
--- a/ash/webui/color_internals/DEPS
+++ b/ash/webui/color_internals/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   # Do not add chrome here. Use a delegate instead.
+  "+ash/grit/ash_color_internals_resources.h",
   "+ui/webui",
 ]
diff --git a/ash/webui/color_internals/color_internals_ui.cc b/ash/webui/color_internals/color_internals_ui.cc
index ae37002..fbf21703 100644
--- a/ash/webui/color_internals/color_internals_ui.cc
+++ b/ash/webui/color_internals/color_internals_ui.cc
@@ -4,9 +4,9 @@
 
 #include "ash/webui/color_internals/color_internals_ui.h"
 
+#include "ash/grit/ash_color_internals_resources.h"
+#include "ash/grit/ash_color_internals_resources_map.h"
 #include "ash/webui/color_internals/url_constants.h"
-#include "ash/webui/grit/ash_color_internals_resources.h"
-#include "ash/webui/grit/ash_color_internals_resources_map.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
diff --git a/ash/webui/demo_mode_app_ui/demo_mode_app_ui.cc b/ash/webui/demo_mode_app_ui/demo_mode_app_ui.cc
index 0cce03c..afa8a8c 100644
--- a/ash/webui/demo_mode_app_ui/demo_mode_app_ui.cc
+++ b/ash/webui/demo_mode_app_ui/demo_mode_app_ui.cc
@@ -4,10 +4,10 @@
 
 #include "ash/webui/demo_mode_app_ui/demo_mode_app_ui.h"
 
+#include "ash/grit/ash_demo_mode_app_resources.h"
+#include "ash/grit/ash_demo_mode_app_resources_map.h"
 #include "ash/webui/demo_mode_app_ui/demo_mode_page_handler.h"
 #include "ash/webui/demo_mode_app_ui/url_constants.h"
-#include "ash/webui/grit/ash_demo_mode_app_resources.h"
-#include "ash/webui/grit/ash_demo_mode_app_resources_map.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui_data_source.h"
 #include "services/network/public/mojom/content_security_policy.mojom.h"
diff --git a/ash/webui/diagnostics_ui/DEPS b/ash/webui/diagnostics_ui/DEPS
index 3f6f56d..c325318 100644
--- a/ash/webui/diagnostics_ui/DEPS
+++ b/ash/webui/diagnostics_ui/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/grit/ash_diagnostics_app_resources.h",
   "+chromeos/login/login_state",
   "+chromeos/strings/grit/chromeos_strings.h",
   "+components",
diff --git a/ash/webui/diagnostics_ui/diagnostics_ui.cc b/ash/webui/diagnostics_ui/diagnostics_ui.cc
index 7e80725..b65b6362 100644
--- a/ash/webui/diagnostics_ui/diagnostics_ui.cc
+++ b/ash/webui/diagnostics_ui/diagnostics_ui.cc
@@ -8,6 +8,8 @@
 #include <string>
 
 #include "ash/constants/ash_features.h"
+#include "ash/grit/ash_diagnostics_app_resources.h"
+#include "ash/grit/ash_diagnostics_app_resources_map.h"
 #include "ash/webui/common/backend/plural_string_handler.h"
 #include "ash/webui/diagnostics_ui/backend/diagnostics_manager.h"
 #include "ash/webui/diagnostics_ui/backend/histogram_util.h"
@@ -21,8 +23,6 @@
 #include "ash/webui/diagnostics_ui/mojom/network_health_provider.mojom.h"
 #include "ash/webui/diagnostics_ui/mojom/system_data_provider.mojom.h"
 #include "ash/webui/diagnostics_ui/url_constants.h"
-#include "ash/webui/grit/ash_diagnostics_app_resources.h"
-#include "ash/webui/grit/ash_diagnostics_app_resources_map.h"
 #include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
diff --git a/ash/webui/eche_app_ui/DEPS b/ash/webui/eche_app_ui/DEPS
index c85b845..e81a168e 100644
--- a/ash/webui/eche_app_ui/DEPS
+++ b/ash/webui/eche_app_ui/DEPS
@@ -1,5 +1,7 @@
 include_rules = [
   # Do not add chrome here (use a delegate instead).
+  "+ash/grit/ash_eche_bundle_resources.h",
+  "+ash/grit/ash_eche_bundle_resources_map.h",
   "+chromeos/services/device_sync/public/cpp",
   "+components/keyed_service/core/keyed_service.h",
   "+crypto/random.h",
diff --git a/ash/webui/eche_app_ui/eche_app_ui.cc b/ash/webui/eche_app_ui/eche_app_ui.cc
index db853dce..0c113f6 100644
--- a/ash/webui/eche_app_ui/eche_app_ui.cc
+++ b/ash/webui/eche_app_ui/eche_app_ui.cc
@@ -4,12 +4,12 @@
 
 #include <memory>
 
+#include "ash/grit/ash_eche_app_resources.h"
+#include "ash/grit/ash_eche_bundle_resources.h"
 #include "ash/webui/eche_app_ui/eche_app_manager.h"
 #include "ash/webui/eche_app_ui/eche_app_ui.h"
 #include "ash/webui/eche_app_ui/mojom/eche_app.mojom.h"
 #include "ash/webui/eche_app_ui/url_constants.h"
-#include "ash/webui/grit/ash_eche_app_resources.h"
-#include "ash/webui/grit/ash_eche_bundle_resources.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
diff --git a/ash/webui/eche_app_ui/untrusted_eche_app_ui.cc b/ash/webui/eche_app_ui/untrusted_eche_app_ui.cc
index 6b2df83b..8856501 100644
--- a/ash/webui/eche_app_ui/untrusted_eche_app_ui.cc
+++ b/ash/webui/eche_app_ui/untrusted_eche_app_ui.cc
@@ -4,10 +4,10 @@
 
 #include "ash/webui/eche_app_ui/untrusted_eche_app_ui.h"
 
+#include "ash/grit/ash_eche_app_resources.h"
+#include "ash/grit/ash_eche_bundle_resources.h"
+#include "ash/grit/ash_eche_bundle_resources_map.h"
 #include "ash/webui/eche_app_ui/url_constants.h"
-#include "ash/webui/grit/ash_eche_app_resources.h"
-#include "ash/webui/grit/ash_eche_bundle_resources.h"
-#include "ash/webui/grit/ash_eche_bundle_resources_map.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
 #include "content/public/browser/web_ui_data_source.h"
diff --git a/ash/webui/file_manager/DEPS b/ash/webui/file_manager/DEPS
index 2483c32..96c8cf6a 100644
--- a/ash/webui/file_manager/DEPS
+++ b/ash/webui/file_manager/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   # Do not add chrome here (use a delegate instead).
+  "+ash/grit/ash_file_manager_resources.h",
   "+ui/file_manager/grit",
   "+ui/webui",
 ]
diff --git a/ash/webui/firmware_update_ui/DEPS b/ash/webui/firmware_update_ui/DEPS
index 5ea09cfd..0db0cc9 100644
--- a/ash/webui/firmware_update_ui/DEPS
+++ b/ash/webui/firmware_update_ui/DEPS
@@ -1,3 +1,5 @@
 include_rules = [
+  "+ash/grit/ash_firmware_update_app_resources.h",
+  "+ash/grit/ash_firmware_update_app_resources_map.h",
   "+ui/resources",
 ]
diff --git a/ash/webui/firmware_update_ui/firmware_update_app_ui.cc b/ash/webui/firmware_update_ui/firmware_update_app_ui.cc
index f16f07b..97f9e81 100644
--- a/ash/webui/firmware_update_ui/firmware_update_app_ui.cc
+++ b/ash/webui/firmware_update_ui/firmware_update_app_ui.cc
@@ -8,10 +8,10 @@
 #include <utility>
 
 #include "ash/components/fwupd/firmware_update_manager.h"
+#include "ash/grit/ash_firmware_update_app_resources.h"
+#include "ash/grit/ash_firmware_update_app_resources_map.h"
 #include "ash/webui/firmware_update_ui/mojom/firmware_update.mojom.h"
 #include "ash/webui/firmware_update_ui/url_constants.h"
-#include "ash/webui/grit/ash_firmware_update_app_resources.h"
-#include "ash/webui/grit/ash_firmware_update_app_resources_map.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
diff --git a/ash/webui/help_app_ui/help_app_ui.cc b/ash/webui/help_app_ui/help_app_ui.cc
index 7d188c0..5ac1003 100644
--- a/ash/webui/help_app_ui/help_app_ui.cc
+++ b/ash/webui/help_app_ui/help_app_ui.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "ash/constants/ash_features.h"
-#include "ash/webui/grit/ash_help_app_resources.h"
+#include "ash/grit/ash_help_app_resources.h"
 #include "ash/webui/help_app_ui/help_app_manager.h"
 #include "ash/webui/help_app_ui/help_app_manager_factory.h"
 #include "ash/webui/help_app_ui/help_app_page_handler.h"
diff --git a/ash/webui/help_app_ui/help_app_untrusted_ui.cc b/ash/webui/help_app_ui/help_app_untrusted_ui.cc
index ee58d6d..cdcedfd2 100644
--- a/ash/webui/help_app_ui/help_app_untrusted_ui.cc
+++ b/ash/webui/help_app_ui/help_app_untrusted_ui.cc
@@ -4,7 +4,7 @@
 
 #include "ash/webui/help_app_ui/help_app_untrusted_ui.h"
 
-#include "ash/webui/grit/ash_help_app_resources.h"
+#include "ash/grit/ash_help_app_resources.h"
 #include "ash/webui/help_app_ui/url_constants.h"
 #include "ash/webui/web_applications/webui_test_prod_util.h"
 #include "chromeos/grit/chromeos_help_app_bundle_resources.h"
diff --git a/ash/webui/media_app_ui/media_app_guest_ui.cc b/ash/webui/media_app_ui/media_app_guest_ui.cc
index 383ce8a..fd954db 100644
--- a/ash/webui/media_app_ui/media_app_guest_ui.cc
+++ b/ash/webui/media_app_ui/media_app_guest_ui.cc
@@ -4,7 +4,7 @@
 
 #include "ash/webui/media_app_ui/media_app_guest_ui.h"
 
-#include "ash/webui/grit/ash_media_app_resources.h"
+#include "ash/grit/ash_media_app_resources.h"
 #include "ash/webui/media_app_ui/url_constants.h"
 #include "ash/webui/web_applications/webui_test_prod_util.h"
 #include "chromeos/grit/chromeos_media_app_bundle_resources.h"
diff --git a/ash/webui/media_app_ui/media_app_ui.cc b/ash/webui/media_app_ui/media_app_ui.cc
index 42685eb..ac26db8 100644
--- a/ash/webui/media_app_ui/media_app_ui.cc
+++ b/ash/webui/media_app_ui/media_app_ui.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "ash/webui/grit/ash_media_app_resources.h"
+#include "ash/grit/ash_media_app_resources.h"
 #include "ash/webui/media_app_ui/media_app_page_handler.h"
 #include "ash/webui/media_app_ui/url_constants.h"
 #include "ash/webui/web_applications/webui_test_prod_util.h"
diff --git a/ash/webui/multidevice_debug/proximity_auth_ui.cc b/ash/webui/multidevice_debug/proximity_auth_ui.cc
index e10699c..d761916 100644
--- a/ash/webui/multidevice_debug/proximity_auth_ui.cc
+++ b/ash/webui/multidevice_debug/proximity_auth_ui.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/webui/grit/ash_multidevice_debug_resources.h"
+#include "ash/grit/ash_multidevice_debug_resources.h"
 #include "ash/webui/multidevice_debug/proximity_auth_webui_handler.h"
 #include "ash/webui/multidevice_debug/url_constants.h"
 #include "base/bind.h"
diff --git a/ash/webui/os_feedback_ui/os_feedback_ui.cc b/ash/webui/os_feedback_ui/os_feedback_ui.cc
index f85b4e1..a5a5f6a 100644
--- a/ash/webui/os_feedback_ui/os_feedback_ui.cc
+++ b/ash/webui/os_feedback_ui/os_feedback_ui.cc
@@ -4,8 +4,8 @@
 
 #include "ash/webui/os_feedback_ui/os_feedback_ui.h"
 
-#include "ash/webui/grit/ash_os_feedback_resources.h"
-#include "ash/webui/grit/ash_os_feedback_resources_map.h"
+#include "ash/grit/ash_os_feedback_resources.h"
+#include "ash/grit/ash_os_feedback_resources_map.h"
 #include "ash/webui/os_feedback_ui/url_constants.h"
 #include "base/memory/ptr_util.h"
 #include "content/public/browser/web_contents.h"
diff --git a/ash/webui/personalization_app/personalization_app_ui.cc b/ash/webui/personalization_app/personalization_app_ui.cc
index e152a13..2c09250 100644
--- a/ash/webui/personalization_app/personalization_app_ui.cc
+++ b/ash/webui/personalization_app/personalization_app_ui.cc
@@ -5,8 +5,8 @@
 #include "ash/webui/personalization_app/personalization_app_ui.h"
 
 #include "ash/constants/ash_features.h"
-#include "ash/webui/grit/ash_personalization_app_resources.h"
-#include "ash/webui/grit/ash_personalization_app_resources_map.h"
+#include "ash/grit/ash_personalization_app_resources.h"
+#include "ash/grit/ash_personalization_app_resources_map.h"
 #include "ash/webui/personalization_app/personalization_app_ambient_provider.h"
 #include "ash/webui/personalization_app/personalization_app_theme_provider.h"
 #include "ash/webui/personalization_app/personalization_app_url_constants.h"
diff --git a/ash/webui/personalization_app/untrusted_personalization_app_ui_config.cc b/ash/webui/personalization_app/untrusted_personalization_app_ui_config.cc
index 8485a5c..d6877e2 100644
--- a/ash/webui/personalization_app/untrusted_personalization_app_ui_config.cc
+++ b/ash/webui/personalization_app/untrusted_personalization_app_ui_config.cc
@@ -5,8 +5,8 @@
 #include "ash/webui/personalization_app/untrusted_personalization_app_ui_config.h"
 
 #include "ash/constants/ash_features.h"
-#include "ash/webui/grit/ash_personalization_app_resources.h"
-#include "ash/webui/grit/ash_personalization_app_resources_map.h"
+#include "ash/grit/ash_personalization_app_resources.h"
+#include "ash/grit/ash_personalization_app_resources_map.h"
 #include "ash/webui/personalization_app/personalization_app_url_constants.h"
 #include "base/strings/string_util.h"
 #include "chromeos/strings/grit/chromeos_strings.h"
diff --git a/ash/webui/print_management/print_management_ui.cc b/ash/webui/print_management/print_management_ui.cc
index 597d7d4e..0047466 100644
--- a/ash/webui/print_management/print_management_ui.cc
+++ b/ash/webui/print_management/print_management_ui.cc
@@ -4,8 +4,8 @@
 
 #include "ash/webui/print_management/print_management_ui.h"
 
-#include "ash/webui/grit/ash_print_management_resources.h"
-#include "ash/webui/grit/ash_print_management_resources_map.h"
+#include "ash/grit/ash_print_management_resources.h"
+#include "ash/grit/ash_print_management_resources_map.h"
 #include "ash/webui/print_management/mojom/printing_manager.mojom.h"
 #include "ash/webui/print_management/url_constants.h"
 #include "base/memory/ptr_util.h"
diff --git a/ash/webui/projector_app/trusted_projector_annotator_ui.cc b/ash/webui/projector_app/trusted_projector_annotator_ui.cc
index 0acd45b..9087937 100644
--- a/ash/webui/projector_app/trusted_projector_annotator_ui.cc
+++ b/ash/webui/projector_app/trusted_projector_annotator_ui.cc
@@ -4,9 +4,9 @@
 
 #include "ash/webui/projector_app/trusted_projector_annotator_ui.h"
 
+#include "ash/grit/ash_projector_app_trusted_resources.h"
+#include "ash/grit/ash_projector_app_trusted_resources_map.h"
 #include "ash/public/cpp/projector/projector_annotator_controller.h"
-#include "ash/webui/grit/ash_projector_app_trusted_resources.h"
-#include "ash/webui/grit/ash_projector_app_trusted_resources_map.h"
 #include "ash/webui/projector_app/annotator_message_handler.h"
 #include "ash/webui/projector_app/projector_message_handler.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
diff --git a/ash/webui/projector_app/trusted_projector_ui.cc b/ash/webui/projector_app/trusted_projector_ui.cc
index db000f9..65caf0e 100644
--- a/ash/webui/projector_app/trusted_projector_ui.cc
+++ b/ash/webui/projector_app/trusted_projector_ui.cc
@@ -4,9 +4,9 @@
 
 #include "ash/webui/projector_app/trusted_projector_ui.h"
 
+#include "ash/grit/ash_projector_app_trusted_resources.h"
+#include "ash/grit/ash_projector_app_trusted_resources_map.h"
 #include "ash/public/cpp/projector/projector_annotator_controller.h"
-#include "ash/webui/grit/ash_projector_app_trusted_resources.h"
-#include "ash/webui/grit/ash_projector_app_trusted_resources_map.h"
 #include "ash/webui/projector_app/annotator_message_handler.h"
 #include "ash/webui/projector_app/projector_message_handler.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
diff --git a/ash/webui/projector_app/untrusted_projector_annotator_ui.cc b/ash/webui/projector_app/untrusted_projector_annotator_ui.cc
index bdfc967..e085976 100644
--- a/ash/webui/projector_app/untrusted_projector_annotator_ui.cc
+++ b/ash/webui/projector_app/untrusted_projector_annotator_ui.cc
@@ -4,8 +4,8 @@
 
 #include "ash/webui/projector_app/untrusted_projector_annotator_ui.h"
 
-#include "ash/webui/grit/ash_projector_app_untrusted_resources.h"
-#include "ash/webui/grit/ash_projector_app_untrusted_resources_map.h"
+#include "ash/grit/ash_projector_app_untrusted_resources.h"
+#include "ash/grit/ash_projector_app_untrusted_resources_map.h"
 #include "ash/webui/media_app_ui/buildflags.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "chromeos/grit/chromeos_projector_app_bundle_resources.h"
diff --git a/ash/webui/projector_app/untrusted_projector_ui.cc b/ash/webui/projector_app/untrusted_projector_ui.cc
index bd2f0ff..383bd99d 100644
--- a/ash/webui/projector_app/untrusted_projector_ui.cc
+++ b/ash/webui/projector_app/untrusted_projector_ui.cc
@@ -4,8 +4,8 @@
 
 #include "ash/webui/projector_app/untrusted_projector_ui.h"
 
-#include "ash/webui/grit/ash_projector_app_untrusted_resources.h"
-#include "ash/webui/grit/ash_projector_app_untrusted_resources_map.h"
+#include "ash/grit/ash_projector_app_untrusted_resources.h"
+#include "ash/grit/ash_projector_app_untrusted_resources_map.h"
 #include "ash/webui/media_app_ui/buildflags.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "chromeos/grit/chromeos_projector_app_bundle_resources.h"
diff --git a/ash/webui/resources/BUILD.gn b/ash/webui/resources/BUILD.gn
index 6e3ad667..493de55 100644
--- a/ash/webui/resources/BUILD.gn
+++ b/ash/webui/resources/BUILD.gn
@@ -10,8 +10,6 @@
 
 assert(is_chromeos_ash, "Non-ChromeOS builds cannot depend on //ash")
 
-ash_webui_grit_output_dir = "$root_gen_dir/ash/webui"
-
 # Convenience template for WebUIs using generate_grd.
 # TODO(calamity): Further deduplicate with non-generated grit targets.
 template("ash_generated_grit") {
@@ -24,14 +22,13 @@
 
     # These arguments are needed since the grd is generated at build time.
     enable_input_discovery_for_gn_analyze = false
-
     outputs = [
-      "ash_${target_name}.pak",
-      "grit/ash_${target_name}.h",
+      "ash_$target_name.pak",
+      "grit/ash_$target_name.h",
       "grit/ash_${target_name}_map.cc",
       "grit/ash_${target_name}_map.h",
     ]
-    output_dir = "$ash_webui_grit_output_dir"
+    output_dir = "$root_gen_dir/ash"
   }
 }
 
@@ -136,7 +133,7 @@
     "grit/ash_scanning_app_resources_map.h",
   ]
 
-  output_dir = "$ash_webui_grit_output_dir"
+  output_dir = "$root_gen_dir/ash"
 }
 
 grit("diagnostics_app_resources") {
@@ -154,7 +151,7 @@
     "grit/ash_diagnostics_app_resources_map.h",
   ]
 
-  output_dir = "$ash_webui_grit_output_dir"
+  output_dir = "$root_gen_dir/ash"
 }
 
 # Resources used by chrome://help-app, and parts of the sandboxed app it hosts
@@ -168,7 +165,7 @@
     "grit/ash_help_app_resources_map.h",
     "ash_help_app_resources.pak",
   ]
-  output_dir = "$ash_webui_grit_output_dir"
+  output_dir = "$root_gen_dir/ash"
 
   deps = [
     "//ash/webui/help_app_ui:mojo_bindings_js",
@@ -231,7 +228,7 @@
     "grit/ash_media_app_resources.h",
     "ash_media_app_resources.pak",
   ]
-  output_dir = "$ash_webui_grit_output_dir"
+  output_dir = "$root_gen_dir/ash"
 
   use_brotli = true
 
@@ -282,7 +279,7 @@
     "ash_print_management_resources.pak",
   ]
 
-  output_dir = "$ash_webui_grit_output_dir"
+  output_dir = "$root_gen_dir/ash"
 }
 
 # Resources used by chrome-untrusted://projector SWA.
@@ -344,7 +341,7 @@
     "grit/ash_eche_bundle_resources_map.h",
     "ash_eche_bundle_resources.pak",
   ]
-  output_dir = "$ash_webui_grit_output_dir"
+  output_dir = "$root_gen_dir/ash"
 }
 
 # Resources used by chrome://connectivity-diagnostics
@@ -364,7 +361,7 @@
     "grit/connectivity_diagnostics_resources_map.h",
   ]
 
-  output_dir = "$ash_webui_grit_output_dir"
+  output_dir = "$root_gen_dir/ash"
 }
 
 grit("multidevice_debug_resources") {
@@ -378,5 +375,5 @@
     "ash_multidevice_debug_resources.pak",
   ]
 
-  output_dir = "$ash_webui_grit_output_dir"
+  output_dir = "$root_gen_dir/ash"
 }
diff --git a/ash/webui/sample_system_web_app_ui/DEPS b/ash/webui/sample_system_web_app_ui/DEPS
index 52336fd4..1da0510b 100644
--- a/ash/webui/sample_system_web_app_ui/DEPS
+++ b/ash/webui/sample_system_web_app_ui/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   # Do not add chrome here (use a delegate instead).
+  "+ash/grit/ash_sample_system_web_app_resources.h",
   "+ui/webui",
 ]
diff --git a/ash/webui/sample_system_web_app_ui/sample_system_web_app_ui.cc b/ash/webui/sample_system_web_app_ui/sample_system_web_app_ui.cc
index 64724f25..57bbd8f6 100644
--- a/ash/webui/sample_system_web_app_ui/sample_system_web_app_ui.cc
+++ b/ash/webui/sample_system_web_app_ui/sample_system_web_app_ui.cc
@@ -6,8 +6,8 @@
 
 #include <utility>
 
-#include "ash/webui/grit/ash_sample_system_web_app_resources.h"
-#include "ash/webui/grit/ash_sample_system_web_app_resources_map.h"
+#include "ash/grit/ash_sample_system_web_app_resources.h"
+#include "ash/grit/ash_sample_system_web_app_resources_map.h"
 #include "ash/webui/sample_system_web_app_ui/sample_page_handler.h"
 #include "ash/webui/sample_system_web_app_ui/url_constants.h"
 #include "base/memory/ptr_util.h"
diff --git a/ash/webui/sample_system_web_app_ui/untrusted_sample_system_web_app_ui.cc b/ash/webui/sample_system_web_app_ui/untrusted_sample_system_web_app_ui.cc
index 54c913e..6463c35 100644
--- a/ash/webui/sample_system_web_app_ui/untrusted_sample_system_web_app_ui.cc
+++ b/ash/webui/sample_system_web_app_ui/untrusted_sample_system_web_app_ui.cc
@@ -4,7 +4,7 @@
 
 #include "ash/webui/sample_system_web_app_ui/untrusted_sample_system_web_app_ui.h"
 
-#include "ash/webui/grit/ash_sample_system_web_app_untrusted_resources_map.h"
+#include "ash/grit/ash_sample_system_web_app_untrusted_resources_map.h"
 #include "ash/webui/sample_system_web_app_ui/sample_system_web_app_ui.h"
 #include "ash/webui/sample_system_web_app_ui/url_constants.h"
 #include "content/public/browser/web_contents.h"
diff --git a/ash/webui/scanning/DEPS b/ash/webui/scanning/DEPS
index fd995e3..f911907 100644
--- a/ash/webui/scanning/DEPS
+++ b/ash/webui/scanning/DEPS
@@ -1,4 +1,6 @@
 include_rules = [
+  "+ash/grit/ash_scanning_app_resources.h",
+  "+ash/grit/ash_scanning_app_resources_map.h",
   "+chromeos/strings/grit/chromeos_strings.h",
   "+ui/base",
   "+ui/gfx",
diff --git a/ash/webui/scanning/scanning_ui.cc b/ash/webui/scanning/scanning_ui.cc
index 486cc33..db759663 100644
--- a/ash/webui/scanning/scanning_ui.cc
+++ b/ash/webui/scanning/scanning_ui.cc
@@ -9,10 +9,10 @@
 #include <utility>
 
 #include "ash/constants/ash_features.h"
+#include "ash/grit/ash_scanning_app_resources.h"
+#include "ash/grit/ash_scanning_app_resources_map.h"
 #include "ash/webui/common/backend/accessibility_features.h"
 #include "ash/webui/common/mojom/accessibility_features.mojom.h"
-#include "ash/webui/grit/ash_scanning_app_resources.h"
-#include "ash/webui/grit/ash_scanning_app_resources_map.h"
 #include "ash/webui/scanning/mojom/scanning.mojom.h"
 #include "ash/webui/scanning/scanning_app_delegate.h"
 #include "ash/webui/scanning/scanning_metrics_handler.h"
diff --git a/ash/webui/shimless_rma/DEPS b/ash/webui/shimless_rma/DEPS
index c72c12ae..cdebd9908 100644
--- a/ash/webui/shimless_rma/DEPS
+++ b/ash/webui/shimless_rma/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
+  "+ash/grit/ash_shimless_rma_resources.h",
   "+chromeos/dbus",
   "+chromeos/dbus/power",
   "+chromeos/dbus/rmad",
diff --git a/ash/webui/shimless_rma/shimless_rma.cc b/ash/webui/shimless_rma/shimless_rma.cc
index e61cfa48..39e6e763 100644
--- a/ash/webui/shimless_rma/shimless_rma.cc
+++ b/ash/webui/shimless_rma/shimless_rma.cc
@@ -8,9 +8,9 @@
 #include <string>
 #include <utility>
 
+#include "ash/grit/ash_shimless_rma_resources.h"
+#include "ash/grit/ash_shimless_rma_resources_map.h"
 #include "ash/public/cpp/network_config_service.h"
-#include "ash/webui/grit/ash_shimless_rma_resources.h"
-#include "ash/webui/grit/ash_shimless_rma_resources_map.h"
 #include "ash/webui/shimless_rma/backend/shimless_rma_delegate.h"
 #include "ash/webui/shimless_rma/url_constants.h"
 #include "base/containers/span.h"
diff --git a/ash/webui/shortcut_customization_ui/DEPS b/ash/webui/shortcut_customization_ui/DEPS
index 93138d4..4ff4187 100644
--- a/ash/webui/shortcut_customization_ui/DEPS
+++ b/ash/webui/shortcut_customization_ui/DEPS
@@ -1,4 +1,6 @@
 include_rules = [
+  "+ash/grit/ash_shortcut_customization_app_resources.h",
+  "+ash/grit/ash_shortcut_customization_app_resources_map.h",
   "+ui/resources",
   "+ui/webui",
 ]
diff --git a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc
index 66c04a2..b38294b 100644
--- a/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc
+++ b/ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.cc
@@ -7,8 +7,8 @@
 #include <memory>
 #include <utility>
 
-#include "ash/webui/grit/ash_shortcut_customization_app_resources.h"
-#include "ash/webui/grit/ash_shortcut_customization_app_resources_map.h"
+#include "ash/grit/ash_shortcut_customization_app_resources.h"
+#include "ash/grit/ash_shortcut_customization_app_resources_map.h"
 #include "ash/webui/shortcut_customization_ui/backend/accelerator_configuration_provider.h"
 #include "ash/webui/shortcut_customization_ui/mojom/shortcut_customization.mojom.h"
 #include "ash/webui/shortcut_customization_ui/url_constants.h"
diff --git a/ash/webui/system_extensions_internals_ui/DEPS b/ash/webui/system_extensions_internals_ui/DEPS
index 4244eece..e1a02ef 100644
--- a/ash/webui/system_extensions_internals_ui/DEPS
+++ b/ash/webui/system_extensions_internals_ui/DEPS
@@ -1,4 +1,5 @@
 include_rules = [
   # Do not add chrome here. Use a delegate instead.
+  "+ash/grit/ash_system_extensions_internals_resources.h",
   "+ui/webui",
 ]
diff --git a/ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.cc b/ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.cc
index 0b6a2a1..69458872 100644
--- a/ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.cc
+++ b/ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.cc
@@ -4,8 +4,8 @@
 
 #include "ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h"
 
-#include "ash/webui/grit/ash_system_extensions_internals_resources.h"
-#include "ash/webui/grit/ash_system_extensions_internals_resources_map.h"
+#include "ash/grit/ash_system_extensions_internals_resources.h"
+#include "ash/grit/ash_system_extensions_internals_resources_map.h"
 #include "ash/webui/system_extensions_internals_ui/url_constants.h"
 #include "base/memory/ptr_util.h"
 #include "content/public/browser/web_contents.h"
diff --git a/ash/webui/telemetry_extension_ui/DEPS b/ash/webui/telemetry_extension_ui/DEPS
index f396279..e16f58c 100644
--- a/ash/webui/telemetry_extension_ui/DEPS
+++ b/ash/webui/telemetry_extension_ui/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   # Do not add chrome here (use a delegate instead).
+  "+ash/grit/ash_telemetry_extension_resources.h",
   "+chrome/browser/ash/wilco_dtc_supportd/mojo_utils.h",
 
   # TODO(https://crbug.com/1164001): Remove when these moves to ash.
diff --git a/base/allocator/partition_allocator/address_pool_manager_unittest.cc b/base/allocator/partition_allocator/address_pool_manager_unittest.cc
index c743acb..120602e1 100644
--- a/base/allocator/partition_allocator/address_pool_manager_unittest.cc
+++ b/base/allocator/partition_allocator/address_pool_manager_unittest.cc
@@ -196,7 +196,7 @@
   ASSERT_TRUE(address);
   RecommitSystemPages(address, kSuperPageSize,
                       PageAccessibilityConfiguration::kReadWrite,
-                      PageAccessibilityDisposition::kUpdatePermissions);
+                      PageAccessibilityDisposition::kRequireUpdate);
 
   memset(reinterpret_cast<void*>(address), 42, kSuperPageSize);
   GetAddressPoolManager()->UnreserveAndDecommit(pool_, address, kSuperPageSize);
@@ -206,7 +206,7 @@
   ASSERT_EQ(address, address2);
   RecommitSystemPages(address2, kSuperPageSize,
                       PageAccessibilityConfiguration::kReadWrite,
-                      PageAccessibilityDisposition::kUpdatePermissions);
+                      PageAccessibilityDisposition::kRequireUpdate);
 
   uint32_t sum = 0;
   for (size_t i = 0; i < kSuperPageSize; i++) {
diff --git a/base/allocator/partition_allocator/page_allocator.h b/base/allocator/partition_allocator/page_allocator.h
index d34d386..2f0e076 100644
--- a/base/allocator/partition_allocator/page_allocator.h
+++ b/base/allocator/partition_allocator/page_allocator.h
@@ -37,10 +37,10 @@
   // PageAccessibilityConfiguration::kInaccessible;
   // Recommit will set to whatever was requested, other than
   // PageAccessibilityConfiguration::kInaccessible).
-  kUpdatePermissions,
+  kRequireUpdate,
   // Will not update permissions, if the platform supports that (POSIX & Fuchsia
   // only).
-  kKeepPermissionsIfPossible,
+  kAllowKeepForPerf,
 };
 
 // macOS supports tagged memory regions, to help in debugging. On Android,
@@ -58,7 +58,7 @@
                                             uintptr_t alignment,
                                             uintptr_t requested_offset);
 
-// Allocate one or more pages.
+// Allocates one or more pages.
 //
 // The requested |address| is just a hint; the actual address returned may
 // differ. The returned address will be aligned to |align_offset| modulo |align|
@@ -100,7 +100,7 @@
                           PageAccessibilityConfiguration page_accessibility,
                           PageTag page_tag);
 
-// Free one or more pages starting at |address| and continuing for |length|
+// Frees one or more pages starting at |address| and continuing for |length|
 // bytes.
 //
 // |address| and |length| must match a previous call to |AllocPages|. Therefore,
@@ -109,7 +109,7 @@
 BASE_EXPORT void FreePages(uintptr_t address, size_t length);
 BASE_EXPORT void FreePages(void* address, size_t length);
 
-// Mark one or more system pages, starting at |address| with the given
+// Marks one or more system pages, starting at |address| with the given
 // |page_accessibility|. |length| must be a multiple of |SystemPageSize()|
 // bytes.
 //
@@ -124,7 +124,7 @@
     size_t length,
     PageAccessibilityConfiguration page_accessibility);
 
-// Mark one or more system pages, starting at |address| with the given
+// Marks one or more system pages, starting at |address| with the given
 // |page_accessibility|. |length| must be a multiple of |SystemPageSize()|
 // bytes.
 //
@@ -138,40 +138,38 @@
     size_t length,
     PageAccessibilityConfiguration page_accessibility);
 
-// Decommit one or more system pages starting at |address| and continuing for
+// Decommits one or more system pages starting at |address| and continuing for
 // |length| bytes. |address| and |length| must be aligned to a system page
 // boundary.
 //
-// |accessibility_disposition| allows to specify whether the pages should be
-// made inaccessible (PageAccessibilityDisposition::kUpdatePermissions), or left
-// as is (PageAccessibilityDisposition::kKeepPermissionsIfPossible, POSIX &
-// Fuchsia only). The latter should only be used as an optimization if you
-// really know what you're doing.
-// TODO(bartekn): Ideally, all callers should use
-// PageAccessibilityDisposition::kUpdatePermissions, for better security, but
-// that may lead to a perf regression. Tracked at http://crbug.com/766882.
+// This API will crash if the operation cannot be performed!
 //
-// Decommitted means that physical resources (RAM or swap) backing the allocated
-// virtual address range may be released back to the system, but the address
-// space is still allocated to the process (possibly using up page table entries
-// or other accounting resources). There is no guarantee that the pages are
-// zeroed, see |DecommittedMemoryIsAlwaysZeroed()| for such a guarantee. Unless
-// PageAccessibilityDisposition::kKeepPermissionsIfPossible disposition is used,
-// any access to a decommitted region of memory is an error and will generate a
-// fault.
+// If disposition is PageAccessibilityDisposition::kRequireUpdate (recommended),
+// the decommitted pages will be made inaccessible before the call returns.
+// While it is always a programming error to access decommitted pages without
+// first recommitting them, callers may use
+// PageAccessibilityDisposition::kAllowKeepForPerf to allow the implementation
+// to skip changing permissions (use with care), for performance reasons (see
+// crrev.com/c/2567282 and crrev.com/c/2563038 for perf regressions encountered
+// in the past). Implementations may choose to always modify permissions, hence
+// accessing those pages may or may not trigger a fault.
 //
-// This operation is not atomic on all platforms.
+// Decommitting means that physical resources (RAM or swap/pagefile) backing the
+// allocated virtual address range may be released back to the system, but the
+// address space is still allocated to the process (possibly using up page table
+// entries or other accounting resources). There is no guarantee that the pages
+// are zeroed, unless |DecommittedMemoryIsAlwaysZeroed()| is true.
+//
+// This operation may not be atomic on some platforms.
 //
 // Note: "Committed memory" is a Windows Memory Subsystem concept that ensures
 // processes will not fault when touching a committed memory region. There is
 // no analogue in the POSIX & Fuchsia memory API where virtual memory pages are
 // best-effort allocated resources on the first touch. If
-// PageAccessibilityDisposition::kUpdatePermissions disposition is used, this
-// API behaves in a platform-agnostic way by simulating the Windows "decommit"
-// state by both discarding the region (allowing the OS to avoid swap
-// operations) *and* changing the page protections so accesses fault.
-//
-// This API will crash if the operation cannot be performed.
+// PageAccessibilityDisposition::kRequireUpdate disposition is used, this API
+// behaves in a platform-agnostic way by simulating the Windows "decommit" state
+// by both discarding the region (allowing the OS to avoid swap operations)
+// *and* changing the page protections so accesses fault.
 BASE_EXPORT void DecommitSystemPages(
     uintptr_t address,
     size_t length,
@@ -181,7 +179,7 @@
     size_t length,
     PageAccessibilityDisposition accessibility_disposition);
 
-// Decommit one or more system pages starting at |address| and continuing for
+// Decommits one or more system pages starting at |address| and continuing for
 // |length| bytes. |address| and |length| must be aligned to a system page
 // boundary.
 //
@@ -203,26 +201,26 @@
 #endif
 }
 
-// Recommit one or more system pages, starting at |address| and continuing for
-// |length| bytes with the given |page_accessibility| (must not be
-// PageAccessibilityConfiguration::kInaccessible). |address| and |length| must
-// be aligned to a system page boundary.
+// (Re)Commits one or more system pages, starting at |address| and continuing
+// for |length| bytes with the given |page_accessibility| (must not be
+// PageAccessibilityConfiguration::kInaccessible). |address| and |length|
+// must be aligned to a system page boundary.
 //
-// |accessibility_disposition| allows to specify whether the page permissions
-// should be set to |page_accessibility|
-// (PageAccessibilityDisposition::kUpdatePermissions), or left as is
-// (PageAccessibilityDisposition::kKeepPermissionsIfPossible, POSIX & Fuchsia
-// only). The latter can only be used if the pages were previously accessible
-// and decommitted with
-// PageAccessibilityDisposition::kKeepPermissionsIfPossible. It is ok, however,
-// to recommit with PageAccessibilityDisposition::kUpdatePermissions even if
-// pages were decommitted with
-// PageAccessibilityDisposition::kKeepPermissionsIfPossible (merely losing an
-// optimization).
+// This API will crash if the operation cannot be performed!
 //
-// This operation is not atomic on all platforms.
+// If disposition is PageAccessibilityConfiguration::kRequireUpdate, the calls
+// updates the pages to |page_accessibility|. This can be used regardless of
+// what disposition was used to decommit the pages.
+// PageAccessibilityConfiguration::kAllowKeepForPerf allows the implementation
+// to leave the page permissions, if that improves performance. This option can
+// only be used if the pages were previously accessible and decommitted with
+// that same option.
 //
-// This API will crash if the operation cannot be performed.
+// The memory will be zeroed when it is committed for the first time. However,
+// there is no such guarantee when memory is recommitted, unless
+// |DecommittedMemoryIsAlwaysZeroed()| is true.
+//
+// This operation may not be atomic on some platforms.
 BASE_EXPORT void RecommitSystemPages(
     uintptr_t address,
     size_t length,
diff --git a/base/allocator/partition_allocator/page_allocator_internals_fuchsia.h b/base/allocator/partition_allocator/page_allocator_internals_fuchsia.h
index 4fd1e38..a0fca92f6 100644
--- a/base/allocator/partition_allocator/page_allocator_internals_fuchsia.h
+++ b/base/allocator/partition_allocator/page_allocator_internals_fuchsia.h
@@ -183,7 +183,7 @@
     size_t length,
     PageAccessibilityDisposition accessibility_disposition) {
   if (accessibility_disposition ==
-      PageAccessibilityDisposition::kUpdatePermissions) {
+      PageAccessibilityDisposition::kRequireUpdate) {
     SetSystemPagesAccess(address, length,
                          PageAccessibilityConfiguration::kInaccessible);
   }
@@ -213,7 +213,7 @@
   // it. However, if decommit changed the permissions, recommit has to change
   // them back.
   if (accessibility_disposition ==
-      PageAccessibilityDisposition::kUpdatePermissions) {
+      PageAccessibilityDisposition::kRequireUpdate) {
     SetSystemPagesAccess(address, length, accessibility);
   }
 }
@@ -227,7 +227,7 @@
   // it. However, if decommit changed the permissions, recommit has to change
   // them back.
   if (accessibility_disposition ==
-      PageAccessibilityDisposition::kUpdatePermissions) {
+      PageAccessibilityDisposition::kRequireUpdate) {
     return TrySetSystemPagesAccess(address, length, accessibility);
   }
   return true;
diff --git a/base/allocator/partition_allocator/page_allocator_internals_posix.h b/base/allocator/partition_allocator/page_allocator_internals_posix.h
index 41138c14..364b46e 100644
--- a/base/allocator/partition_allocator/page_allocator_internals_posix.h
+++ b/base/allocator/partition_allocator/page_allocator_internals_posix.h
@@ -260,8 +260,8 @@
   // pages in the region.
   DiscardSystemPages(address, length);
 
-  bool change_permissions = accessibility_disposition ==
-                            PageAccessibilityDisposition::kUpdatePermissions;
+  bool change_permissions =
+      accessibility_disposition == PageAccessibilityDisposition::kRequireUpdate;
 #if DCHECK_IS_ON()
   // This is not guaranteed, show that we're serious.
   //
@@ -315,7 +315,7 @@
   // it. However, if decommit changed the permissions, recommit has to change
   // them back.
   if (accessibility_disposition ==
-      PageAccessibilityDisposition::kUpdatePermissions) {
+      PageAccessibilityDisposition::kRequireUpdate) {
     SetSystemPagesAccess(address, length, accessibility);
   }
 
@@ -335,7 +335,7 @@
   // it. However, if decommit changed the permissions, recommit has to change
   // them back.
   if (accessibility_disposition ==
-      PageAccessibilityDisposition::kUpdatePermissions) {
+      PageAccessibilityDisposition::kRequireUpdate) {
     bool ok = TrySetSystemPagesAccess(address, length, accessibility);
     if (!ok)
       return false;
diff --git a/base/allocator/partition_allocator/page_allocator_unittest.cc b/base/allocator/partition_allocator/page_allocator_unittest.cc
index 28579e8..6bf48b41 100644
--- a/base/allocator/partition_allocator/page_allocator_unittest.cc
+++ b/base/allocator/partition_allocator/page_allocator_unittest.cc
@@ -496,9 +496,9 @@
   memset(reinterpret_cast<void*>(buffer), 42, size);
 
   DecommitSystemPages(buffer, size,
-                      PageAccessibilityDisposition::kKeepPermissionsIfPossible);
+                      PageAccessibilityDisposition::kAllowKeepForPerf);
   RecommitSystemPages(buffer, size, PageAccessibilityConfiguration::kReadWrite,
-                      PageAccessibilityDisposition::kKeepPermissionsIfPossible);
+                      PageAccessibilityDisposition::kAllowKeepForPerf);
 
   uint8_t* recommitted_buffer = reinterpret_cast<uint8_t*>(buffer);
   uint32_t sum = 0;
@@ -570,8 +570,8 @@
 
     EXPECT_EQ(mapped_size_before + size, GetTotalMappedSize());
 
-    DecommitSystemPages(
-        data, size, PageAccessibilityDisposition::kKeepPermissionsIfPossible);
+    DecommitSystemPages(data, size,
+                        PageAccessibilityDisposition::kAllowKeepForPerf);
     EXPECT_EQ(mapped_size_before + size, GetTotalMappedSize());
 
     FreePages(data, size);
diff --git a/base/allocator/partition_allocator/partition_alloc-inl.h b/base/allocator/partition_allocator/partition_alloc-inl.h
index d44e6ac..c449ecc 100644
--- a/base/allocator/partition_allocator/partition_alloc-inl.h
+++ b/base/allocator/partition_allocator/partition_alloc-inl.h
@@ -7,7 +7,6 @@
 
 #include <cstring>
 
-#include "base/allocator/partition_allocator/partition_cookie.h"
 #include "base/allocator/partition_allocator/partition_ref_count.h"
 #include "base/allocator/partition_allocator/random.h"
 #include "build/build_config.h"
@@ -19,9 +18,8 @@
 #define PA_PREFETCH(x)
 #endif
 
-namespace base {
+namespace partition_alloc::internal {
 
-namespace internal {
 // This is a `memset` that resists being optimized away. Adapted from
 // boringssl/src/crypto/mem.c. (Copying and pasting is bad, but //base can't
 // depend on //third_party, and this is small enough.)
@@ -48,10 +46,19 @@
   counter--;
   return counter == 0;
 }
-#endif
+#endif  // !DCHECK_IS_ON()
 
-}  // namespace internal
+}  // namespace partition_alloc::internal
 
-}  // namespace base
+namespace base::internal {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::internal::SecureMemset;
+#if !DCHECK_IS_ON()
+using ::partition_alloc::internal::RandomPeriod;
+#endif  // !DCHECK_IS_ON()
+
+}  // namespace base::internal
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_INL_H_
diff --git a/base/allocator/partition_allocator/partition_alloc_forward.h b/base/allocator/partition_allocator/partition_alloc_forward.h
index db42efb..1e1793f 100644
--- a/base/allocator/partition_allocator/partition_alloc_forward.h
+++ b/base/allocator/partition_allocator/partition_alloc_forward.h
@@ -37,6 +37,11 @@
               "PartitionAlloc doesn't support a fundamental alignment larger "
               "than 16 bytes.");
 
+constexpr bool ThreadSafe = true;
+
+template <bool thread_safe>
+struct SlotSpanMetadata;
+
 }  // namespace partition_alloc::internal
 
 namespace base {
@@ -47,10 +52,8 @@
 
 namespace internal {
 
-template <bool thread_safe>
-struct SlotSpanMetadata;
-
-constexpr bool ThreadSafe = true;
+using ::partition_alloc::internal::SlotSpanMetadata;
+using ::partition_alloc::internal::ThreadSafe;
 
 #if (DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)) && \
     BUILDFLAG(USE_BACKUP_REF_PTR)
@@ -68,6 +71,23 @@
 
 }  // namespace base
 
+namespace partition_alloc {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::base::PartitionRoot;
+
+namespace internal {
+
+#if (DCHECK_IS_ON() || BUILDFLAG(ENABLE_BACKUP_REF_PTR_SLOW_CHECKS)) && \
+    BUILDFLAG(USE_BACKUP_REF_PTR)
+using ::base::internal::CheckThatSlotOffsetIsZero;
+#endif
+
+}  // namespace internal
+
+}  // namespace partition_alloc
+
 // From https://clang.llvm.org/docs/AttributeReference.html#malloc:
 //
 // The malloc attribute indicates that the function acts like a system memory
diff --git a/base/allocator/partition_allocator/partition_alloc_hooks.cc b/base/allocator/partition_allocator/partition_alloc_hooks.cc
index 84988e1..915aa9ee 100644
--- a/base/allocator/partition_allocator/partition_alloc_hooks.cc
+++ b/base/allocator/partition_allocator/partition_alloc_hooks.cc
@@ -9,13 +9,13 @@
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/allocator/partition_allocator/partition_lock.h"
 
-namespace base {
+namespace partition_alloc {
 
 namespace {
 
-internal::PartitionLock g_hook_lock;
+internal::Lock g_hook_lock;
 
-internal::PartitionLock& GetHooksLock() {
+internal::Lock& GetHooksLock() {
   return g_hook_lock;
 }
 
@@ -35,7 +35,7 @@
 
 void PartitionAllocHooks::SetObserverHooks(AllocationObserverHook* alloc_hook,
                                            FreeObserverHook* free_hook) {
-  internal::PartitionAutoLock guard(GetHooksLock());
+  internal::ScopedGuard guard(GetHooksLock());
 
   // Chained hooks are not supported. Registering a non-null hook when a
   // non-null hook is already registered indicates somebody is trying to
@@ -52,7 +52,7 @@
 void PartitionAllocHooks::SetOverrideHooks(AllocationOverrideHook* alloc_hook,
                                            FreeOverrideHook* free_hook,
                                            ReallocOverrideHook realloc_hook) {
-  internal::PartitionAutoLock guard(GetHooksLock());
+  internal::ScopedGuard guard(GetHooksLock());
 
   PA_CHECK((!allocation_override_hook_ && !free_override_hook_ &&
             !realloc_override_hook_) ||
@@ -118,4 +118,4 @@
   return false;
 }
 
-}  // namespace base
+}  // namespace partition_alloc
diff --git a/base/allocator/partition_allocator/partition_alloc_hooks.h b/base/allocator/partition_allocator/partition_alloc_hooks.h
index a8abc81..4e6c5df 100644
--- a/base/allocator/partition_allocator/partition_alloc_hooks.h
+++ b/base/allocator/partition_allocator/partition_alloc_hooks.h
@@ -10,7 +10,7 @@
 
 #include "base/base_export.h"
 
-namespace base {
+namespace partition_alloc {
 
 // PartitionAlloc supports setting hooks to observe allocations/frees as they
 // occur as well as 'override' hooks that allow overriding those operations.
@@ -80,6 +80,14 @@
   static std::atomic<ReallocOverrideHook*> realloc_override_hook_;
 };
 
+}  // namespace partition_alloc
+
+namespace base {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::PartitionAllocHooks;
+
 }  // namespace base
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ALLOC_HOOKS_H_
diff --git a/base/allocator/partition_allocator/partition_bucket.cc b/base/allocator/partition_allocator/partition_bucket.cc
index b45ebc8..20abdc3 100644
--- a/base/allocator/partition_allocator/partition_bucket.cc
+++ b/base/allocator/partition_allocator/partition_bucket.cc
@@ -27,8 +27,7 @@
 #include "base/debug/alias.h"
 #include "build/build_config.h"
 
-namespace base {
-namespace internal {
+namespace partition_alloc::internal {
 
 namespace {
 
@@ -169,7 +168,7 @@
   using ::partition_alloc::internal::ScopedUnlockGuard;
 
   PA_DCHECK((slot_span_alignment >= PartitionPageSize()) &&
-            bits::IsPowerOfTwo(slot_span_alignment));
+            base::bits::IsPowerOfTwo(slot_span_alignment));
 
   // No static EXCLUSIVE_LOCKS_REQUIRED(), as the checker doesn't understand
   // scoped unlocking.
@@ -285,7 +284,7 @@
           SystemPageSize(),
 #endif
           PageAccessibilityConfiguration::kReadWrite,
-          PageAccessibilityDisposition::kUpdatePermissions);
+          PageAccessibilityDisposition::kRequireUpdate);
     }
 
     // No need to hold root->lock_. Now that memory is reserved, no other
@@ -330,12 +329,12 @@
     // Since direct map metadata is larger than PartitionPage, make sure the
     // first and the last bytes are on the same system page, i.e. within the
     // super page metadata region.
-    PA_DCHECK(
-        bits::AlignDown(reinterpret_cast<uintptr_t>(metadata),
-                        SystemPageSize()) ==
-        bits::AlignDown(reinterpret_cast<uintptr_t>(metadata) +
-                            sizeof(PartitionDirectMapMetadata<thread_safe>) - 1,
-                        SystemPageSize()));
+    PA_DCHECK(base::bits::AlignDown(reinterpret_cast<uintptr_t>(metadata),
+                                    SystemPageSize()) ==
+              base::bits::AlignDown(
+                  reinterpret_cast<uintptr_t>(metadata) +
+                      sizeof(PartitionDirectMapMetadata<thread_safe>) - 1,
+                  SystemPageSize()));
     PA_DCHECK(page == &metadata->page);
     page->is_valid = true;
     PA_DCHECK(!page->has_valid_span_after_this);
@@ -371,8 +370,7 @@
     // single page, then this is likely hopeless anyway, and we will crash very
     // soon.
     const bool ok = root->TryRecommitSystemPagesForData(
-        slot_start, slot_size,
-        PageAccessibilityDisposition::kUpdatePermissions);
+        slot_start, slot_size, PageAccessibilityDisposition::kRequireUpdate);
     if (!ok) {
       if (!return_null) {
         PartitionOutOfMemoryCommitFailure(root, slot_size);
@@ -516,7 +514,7 @@
   PA_DCHECK(slot_span_committed_size <= slot_span_reservation_size);
 
   uintptr_t adjusted_next_partition_page =
-      bits::AlignUp(root->next_partition_page, slot_span_alignment);
+      base::bits::AlignUp(root->next_partition_page, slot_span_alignment);
   if (UNLIKELY(adjusted_next_partition_page + slot_span_reservation_size >
                root->next_partition_page_end)) {
     // AllocNewSuperPage() may crash (e.g. address space exhaustion), put data
@@ -531,7 +529,7 @@
     }
     // AllocNewSuperPage() updates root->next_partition_page, re-query.
     adjusted_next_partition_page =
-        bits::AlignUp(root->next_partition_page, slot_span_alignment);
+        base::bits::AlignUp(root->next_partition_page, slot_span_alignment);
     PA_CHECK(adjusted_next_partition_page + slot_span_reservation_size <=
              root->next_partition_page_end);
   }
@@ -565,7 +563,7 @@
 
     root->RecommitSystemPagesForData(
         slot_span_start, slot_span_committed_size,
-        PageAccessibilityDisposition::kUpdatePermissions);
+        PageAccessibilityDisposition::kRequireUpdate);
   }
 
   PA_CHECK(get_slots_per_span() <=
@@ -638,7 +636,7 @@
         SystemPageSize(),
 #endif
         PageAccessibilityConfiguration::kReadWrite,
-        PageAccessibilityDisposition::kUpdatePermissions);
+        PageAccessibilityDisposition::kRequireUpdate);
   }
 
   // If we were after a specific address, but didn't get it, assume that
@@ -695,9 +693,9 @@
       ScopedSyscallTimer timer{root};
       RecommitSystemPages(state_bitmap, state_bitmap_size_to_commit,
                           PageAccessibilityConfiguration::kReadWrite,
-                          PageAccessibilityDisposition::kUpdatePermissions);
+                          PageAccessibilityDisposition::kRequireUpdate);
     }
-    PCScan::RegisterNewSuperPage(root, super_page);
+    ::base::internal::PCScan::RegisterNewSuperPage(root, super_page);
   }
 
   return payload;
@@ -745,9 +743,9 @@
   uintptr_t return_slot =
       slot_span_start + (size * slot_span->num_allocated_slots);
   uintptr_t next_slot = return_slot + size;
-  uintptr_t commit_start = bits::AlignUp(return_slot, SystemPageSize());
+  uintptr_t commit_start = base::bits::AlignUp(return_slot, SystemPageSize());
   PA_DCHECK(next_slot > commit_start);
-  uintptr_t commit_end = bits::AlignUp(next_slot, SystemPageSize());
+  uintptr_t commit_end = base::bits::AlignUp(next_slot, SystemPageSize());
   // If the slot was partially committed, |return_slot| and |next_slot| fall
   // in different pages. If the slot was fully uncommitted, |return_slot| points
   // to the page start and |next_slot| doesn't, thus only the latter gets
@@ -766,15 +764,14 @@
 
   // If lazy commit is enabled, meaning system pages in the slot span come
   // in an initially decommitted state, commit them here.
-  // Note, we can't use
-  // PageAccessibilityDisposition::kKeepPermissionsIfPossible, because we have
-  // no knowledge which pages have been committed before (it doesn't matter on
-  // Windows anyway).
+  // Note, we can't use PageAccessibilityDisposition::kAllowKeepForPerf, because
+  // we have no knowledge which pages have been committed before (it doesn't
+  // matter on Windows anyway).
   if (kUseLazyCommit) {
     // TODO(lizeb): Handle commit failure.
     root->RecommitSystemPagesForData(
         commit_start, commit_end - commit_start,
-        PageAccessibilityDisposition::kUpdatePermissions);
+        PageAccessibilityDisposition::kRequireUpdate);
   }
 
   if (LIKELY(size <= kMaxMemoryTaggingSize)) {
@@ -900,7 +897,7 @@
     size_t slot_span_alignment,
     bool* is_already_zeroed) {
   PA_DCHECK((slot_span_alignment >= PartitionPageSize()) &&
-            bits::IsPowerOfTwo(slot_span_alignment));
+            base::bits::IsPowerOfTwo(slot_span_alignment));
 
   // The slow path is called when the freelist is empty. The only exception is
   // when a higher-order alignment is requested, in which case the freelist
@@ -963,8 +960,8 @@
             ->IncrementNumberOfNonemptySlotSpans();
 
         // Re-activating an empty slot span, update accounting.
-        size_t dirty_size = bits::AlignUp(new_slot_span->GetProvisionedSize(),
-                                          SystemPageSize());
+        size_t dirty_size = base::bits::AlignUp(
+            new_slot_span->GetProvisionedSize(), SystemPageSize());
         PA_DCHECK(root->empty_slot_spans_dirty_bytes >= dirty_size);
         root->empty_slot_spans_dirty_bytes -= dirty_size;
 
@@ -992,12 +989,12 @@
             SlotSpanMetadata<thread_safe>::ToSlotSpanStart(new_slot_span);
         // Since lazy commit isn't used, we have a guarantee that all slot span
         // pages have been previously committed, and then decommitted using
-        // PageAccessibilityDisposition::kKeepPermissionsIfPossible, so use the
+        // PageAccessibilityDisposition::kAllowKeepForPerf, so use the
         // same option as an optimization.
         // TODO(lizeb): Handle commit failure.
         root->RecommitSystemPagesForData(
             slot_span_start, new_slot_span->bucket->get_bytes_per_span(),
-            PageAccessibilityDisposition::kKeepPermissionsIfPossible);
+            PageAccessibilityDisposition::kAllowKeepForPerf);
       }
 
       new_slot_span->Reset();
@@ -1055,5 +1052,4 @@
 
 template struct PartitionBucket<ThreadSafe>;
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_bucket.h b/base/allocator/partition_allocator/partition_bucket.h
index 408f299b..494131e5 100644
--- a/base/allocator/partition_allocator/partition_bucket.h
+++ b/base/allocator/partition_allocator/partition_bucket.h
@@ -5,8 +5,7 @@
 #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
 #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
 
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
 #include <cstdint>
 
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
@@ -16,12 +15,13 @@
 #include "base/compiler_specific.h"
 #include "base/thread_annotations.h"
 
-namespace base {
-namespace internal {
+namespace partition_alloc::internal {
 
 namespace {
+
 constexpr int kPartitionNumSystemPagesPerSlotSpanBits = 8;
-}
+
+}  // namespace
 
 template <bool thread_safe>
 struct PartitionBucket {
@@ -193,7 +193,14 @@
       EXCLUSIVE_LOCKS_REQUIRED(root->lock_);
 };
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
+
+namespace base::internal {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::internal::PartitionBucket;
+
+}  // namespace base::internal
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_H_
diff --git a/base/allocator/partition_allocator/partition_bucket_lookup.h b/base/allocator/partition_allocator/partition_bucket_lookup.h
index 483cf55..f7f049e 100644
--- a/base/allocator/partition_allocator/partition_bucket_lookup.h
+++ b/base/allocator/partition_allocator/partition_bucket_lookup.h
@@ -13,8 +13,8 @@
 #include "base/bits.h"
 #include "base/compiler_specific.h"
 
-namespace base {
-namespace internal {
+namespace partition_alloc::internal {
+
 // Don't use an anonymous namespace for the constants because it can inhibit
 // collapsing them together, even when they are tagged as inline.
 
@@ -40,14 +40,14 @@
 }
 
 #if defined(PA_HAS_64_BITS_POINTERS)
-#define BITS_PER_SIZE_T 64
+#define PA_BITS_PER_SIZE_T 64
 static_assert(kBitsPerSizeT == 64, "");
 #else
-#define BITS_PER_SIZE_T 32
+#define PA_BITS_PER_SIZE_T 32
 static_assert(kBitsPerSizeT == 32, "");
 #endif  // defined(PA_HAS_64_BITS_POINTERS)
 
-constexpr inline uint8_t kOrderIndexShift[BITS_PER_SIZE_T + 1] = {
+constexpr inline uint8_t kOrderIndexShift[PA_BITS_PER_SIZE_T + 1] = {
     OrderIndexShift(0),  OrderIndexShift(1),  OrderIndexShift(2),
     OrderIndexShift(3),  OrderIndexShift(4),  OrderIndexShift(5),
     OrderIndexShift(6),  OrderIndexShift(7),  OrderIndexShift(8),
@@ -59,7 +59,7 @@
     OrderIndexShift(24), OrderIndexShift(25), OrderIndexShift(26),
     OrderIndexShift(27), OrderIndexShift(28), OrderIndexShift(29),
     OrderIndexShift(30), OrderIndexShift(31), OrderIndexShift(32),
-#if BITS_PER_SIZE_T == 64
+#if PA_BITS_PER_SIZE_T == 64
     OrderIndexShift(33), OrderIndexShift(34), OrderIndexShift(35),
     OrderIndexShift(36), OrderIndexShift(37), OrderIndexShift(38),
     OrderIndexShift(39), OrderIndexShift(40), OrderIndexShift(41),
@@ -74,7 +74,7 @@
 #endif
 };
 
-constexpr inline size_t kOrderSubIndexMask[BITS_PER_SIZE_T + 1] = {
+constexpr inline size_t kOrderSubIndexMask[PA_BITS_PER_SIZE_T + 1] = {
     OrderSubIndexMask(0),  OrderSubIndexMask(1),  OrderSubIndexMask(2),
     OrderSubIndexMask(3),  OrderSubIndexMask(4),  OrderSubIndexMask(5),
     OrderSubIndexMask(6),  OrderSubIndexMask(7),  OrderSubIndexMask(8),
@@ -86,7 +86,7 @@
     OrderSubIndexMask(24), OrderSubIndexMask(25), OrderSubIndexMask(26),
     OrderSubIndexMask(27), OrderSubIndexMask(28), OrderSubIndexMask(29),
     OrderSubIndexMask(30), OrderSubIndexMask(31), OrderSubIndexMask(32),
-#if BITS_PER_SIZE_T == 64
+#if PA_BITS_PER_SIZE_T == 64
     OrderSubIndexMask(33), OrderSubIndexMask(34), OrderSubIndexMask(35),
     OrderSubIndexMask(36), OrderSubIndexMask(37), OrderSubIndexMask(38),
     OrderSubIndexMask(39), OrderSubIndexMask(40), OrderSubIndexMask(41),
@@ -206,7 +206,8 @@
   // This forces the bucket table to be constant-initialized and immediately
   // materialized in the binary.
   constexpr BucketIndexLookup lookup{};
-  const uint8_t order = kBitsPerSizeT - bits::CountLeadingZeroBitsSizeT(size);
+  const uint8_t order =
+      kBitsPerSizeT - base::bits::CountLeadingZeroBitsSizeT(size);
   // The order index is simply the next few bits after the most significant
   // bit.
   const size_t order_index =
@@ -220,7 +221,14 @@
   return index;
 }
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
+
+namespace base::internal {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::internal::BucketIndexLookup;
+
+}  // namespace base::internal
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_BUCKET_LOOKUP_H_
diff --git a/base/allocator/partition_allocator/partition_cookie.h b/base/allocator/partition_allocator/partition_cookie.h
index 812bddfd..c4263832 100644
--- a/base/allocator/partition_allocator/partition_cookie.h
+++ b/base/allocator/partition_allocator/partition_cookie.h
@@ -9,8 +9,7 @@
 #include "base/allocator/partition_allocator/partition_alloc_check.h"
 #include "base/compiler_specific.h"
 
-namespace base {
-namespace internal {
+namespace partition_alloc::internal {
 
 static constexpr size_t kCookieSize = 16;
 
@@ -43,7 +42,20 @@
 
 #endif  // DCHECK_IS_ON()
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
+
+namespace base::internal {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::internal::kCookieSize;
+using ::partition_alloc::internal::kPartitionCookieSizeAdjustment;
+using ::partition_alloc::internal::PartitionCookieCheckValue;
+using ::partition_alloc::internal::PartitionCookieWriteValue;
+#if DCHECK_IS_ON()
+using ::partition_alloc::internal::kCookieValue;
+#endif  // DCHECK_IS_ON()
+
+}  // namespace base::internal
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_COOKIE_H_
diff --git a/base/allocator/partition_allocator/partition_direct_map_extent.h b/base/allocator/partition_allocator/partition_direct_map_extent.h
index 29c2614..6f8fd58a 100644
--- a/base/allocator/partition_allocator/partition_direct_map_extent.h
+++ b/base/allocator/partition_allocator/partition_direct_map_extent.h
@@ -9,8 +9,7 @@
 #include "base/allocator/partition_allocator/partition_bucket.h"
 #include "base/allocator/partition_allocator/partition_page.h"
 
-namespace base {
-namespace internal {
+namespace partition_alloc::internal {
 
 template <bool thread_safe>
 struct PartitionDirectMapExtent {
@@ -70,7 +69,15 @@
               ->direct_map_extent;
 }
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
+
+namespace base::internal {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::internal::PartitionDirectMapExtent;
+using ::partition_alloc::internal::PartitionDirectMapMetadata;
+
+}  // namespace base::internal
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_DIRECT_MAP_EXTENT_H_
diff --git a/base/allocator/partition_allocator/partition_freelist_entry.h b/base/allocator/partition_allocator/partition_freelist_entry.h
index cb20598..d8e9845 100644
--- a/base/allocator/partition_allocator/partition_freelist_entry.h
+++ b/base/allocator/partition_allocator/partition_freelist_entry.h
@@ -21,8 +21,7 @@
 #include "base/sys_byteorder.h"
 #include "build/build_config.h"
 
-namespace base {
-namespace internal {
+namespace partition_alloc::internal {
 
 namespace {
 
@@ -71,7 +70,7 @@
 #if defined(ARCH_CPU_BIG_ENDIAN)
     uintptr_t transformed = ~address;
 #else
-    uintptr_t transformed = ByteSwapUintPtrT(address);
+    uintptr_t transformed = base::ByteSwapUintPtrT(address);
 #endif
     return transformed;
   }
@@ -261,7 +260,7 @@
 // it's 0, it gets patched to 1), and ref-count gets added to it.
 namespace {
 constexpr size_t kSmallestUsedBucket =
-    bits::AlignUp(1 + sizeof(PartitionRefCount), kSmallestBucket);
+    base::bits::AlignUp(1 + sizeof(PartitionRefCount), kSmallestBucket);
 }
 static_assert(kSmallestUsedBucket >=
                   sizeof(PartitionFreelistEntry) + sizeof(PartitionRefCount),
@@ -304,7 +303,14 @@
   return GetNextInternal(extra, false);
 }
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
+
+namespace base::internal {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::internal::PartitionFreelistEntry;
+
+}  // namespace base::internal
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_FREELIST_ENTRY_H_
diff --git a/base/allocator/partition_allocator/partition_oom.cc b/base/allocator/partition_allocator/partition_oom.cc
index ba69c76..ec0db8f 100644
--- a/base/allocator/partition_allocator/partition_oom.cc
+++ b/base/allocator/partition_allocator/partition_oom.cc
@@ -9,8 +9,7 @@
 #include "base/debug/alias.h"
 #include "build/build_config.h"
 
-namespace base {
-namespace internal {
+namespace partition_alloc::internal {
 
 OomFunction g_oom_handling_function = nullptr;
 
@@ -34,5 +33,4 @@
 
 #endif  // !defined(ARCH_CPU_64_BITS)
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_oom.h b/base/allocator/partition_allocator/partition_oom.h
index 7cf1ec5..28c5eed4 100644
--- a/base/allocator/partition_allocator/partition_oom.h
+++ b/base/allocator/partition_allocator/partition_oom.h
@@ -14,9 +14,9 @@
 #include "base/compiler_specific.h"
 #include "build/build_config.h"
 
-namespace base {
+namespace partition_alloc {
 
-typedef void (*OomFunction)(size_t);
+using OomFunction = void (*)(size_t);
 
 namespace internal {
 
@@ -35,6 +35,26 @@
 
 }  // namespace internal
 
+}  // namespace partition_alloc
+
+namespace base {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::OomFunction;
+
+namespace internal {
+
+using ::partition_alloc::internal::g_oom_handling_function;
+using ::partition_alloc::internal::PartitionExcessiveAllocationSize;
+#if !defined(ARCH_CPU_64_BITS)
+using ::partition_alloc::internal::PartitionOutOfMemoryWithLargeVirtualSize;
+using ::partition_alloc::internal::
+    PartitionOutOfMemoryWithLotsOfUncommitedPages;
+#endif
+
+}  // namespace internal
+
 }  // namespace base
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_OOM_H_
diff --git a/base/allocator/partition_allocator/partition_page.cc b/base/allocator/partition_allocator/partition_page.cc
index d6b0e0d..020c5b4 100644
--- a/base/allocator/partition_allocator/partition_page.cc
+++ b/base/allocator/partition_allocator/partition_page.cc
@@ -23,8 +23,7 @@
 #include "base/bits.h"
 #include "base/dcheck_is_on.h"
 
-namespace base {
-namespace internal {
+namespace partition_alloc::internal {
 
 namespace {
 
@@ -65,7 +64,7 @@
       SlotSpanMetadata<thread_safe>::ToSlotSpanStart(slot_span);
   // The mapping may start at an unspecified location within a super page, but
   // we always reserve memory aligned to super page size.
-  reservation_start = bits::AlignDown(reservation_start, kSuperPageSize);
+  reservation_start = base::bits::AlignDown(reservation_start, kSuperPageSize);
 
   // All the metadata have been updated above, in particular the mapping has
   // been unlinked. We can safely release the memory outside the lock, which is
@@ -216,7 +215,8 @@
   PA_DCHECK(!bucket->is_direct_mapped());
   uintptr_t slot_span_start = SlotSpanMetadata::ToSlotSpanStart(this);
   // If lazy commit is enabled, only provisioned slots are committed.
-  size_t dirty_size = bits::AlignUp(GetProvisionedSize(), SystemPageSize());
+  size_t dirty_size =
+      base::bits::AlignUp(GetProvisionedSize(), SystemPageSize());
   size_t size_to_decommit =
       kUseLazyCommit ? dirty_size : bucket->get_bytes_per_span();
 
@@ -227,7 +227,7 @@
   PA_DCHECK(size_to_decommit > 0);
   root->DecommitSystemPagesForData(
       slot_span_start, size_to_decommit,
-      PageAccessibilityDisposition::kKeepPermissionsIfPossible);
+      PageAccessibilityDisposition::kAllowKeepForPerf);
 
   // We actually leave the decommitted slot span in the active list. We'll sweep
   // it on to the decommitted list when we next walk the active list.
@@ -301,6 +301,7 @@
 }
 
 namespace {
+
 void UnmapNow(uintptr_t reservation_start,
               size_t reservation_size,
               pool_handle pool) {
@@ -356,9 +357,9 @@
   AddressPoolManager::GetInstance()->UnreserveAndDecommit(
       pool, reservation_start, reservation_size);
 }
+
 }  // namespace
 
 template struct SlotSpanMetadata<ThreadSafe>;
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
diff --git a/base/allocator/partition_allocator/partition_page.h b/base/allocator/partition_allocator/partition_page.h
index a761453..81701eb 100644
--- a/base/allocator/partition_allocator/partition_page.h
+++ b/base/allocator/partition_allocator/partition_page.h
@@ -33,8 +33,7 @@
 #include "base/allocator/partition_allocator/partition_ref_count.h"
 #endif
 
-namespace base {
-namespace internal {
+namespace partition_alloc::internal {
 
 // An "extent" is a span of consecutive superpages. We link the partition's next
 // extent (if there is one) to the very start of a superpage's metadata area.
@@ -84,7 +83,7 @@
          (extent->number_of_consecutive_super_pages * kSuperPageSize);
 }
 
-using AllocationStateMap =
+using AllocationStateMap = ::base::internal::
     StateBitmap<kSuperPageSize, kSuperPageAlignment, kAlignment>;
 
 // Metadata of the slot span.
@@ -395,14 +394,14 @@
 // size is a multiple of partition page size.
 PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
 ReservedStateBitmapSize() {
-  return bits::AlignUp(sizeof(AllocationStateMap), PartitionPageSize());
+  return base::bits::AlignUp(sizeof(AllocationStateMap), PartitionPageSize());
 }
 
 // Size that should be committed for state bitmap (if present) inside a super
 // page. It is a multiple of system page size.
 PAGE_ALLOCATOR_CONSTANTS_DECLARE_CONSTEXPR ALWAYS_INLINE size_t
 CommittedStateBitmapSize() {
-  return bits::AlignUp(sizeof(AllocationStateMap), SystemPageSize());
+  return base::bits::AlignUp(sizeof(AllocationStateMap), SystemPageSize());
 }
 
 // Returns the address/pointer to the state bitmap in the super page. It's the
@@ -830,7 +829,25 @@
                         slot_span->bucket->get_pages_per_slot_span());
 }
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
+
+namespace base::internal {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::partition_alloc::internal::AllocationStateMap;
+using ::partition_alloc::internal::CommittedStateBitmapSize;
+using ::partition_alloc::internal::IterateSlotSpans;
+using ::partition_alloc::internal::PartitionPage;
+using ::partition_alloc::internal::PartitionSuperPageExtentEntry;
+using ::partition_alloc::internal::PartitionSuperPageToExtent;
+using ::partition_alloc::internal::PartitionSuperPageToMetadataArea;
+using ::partition_alloc::internal::ReservedStateBitmapSize;
+using ::partition_alloc::internal::SlotSpanMetadata;
+using ::partition_alloc::internal::StateBitmapFromAddr;
+using ::partition_alloc::internal::SuperPageStateBitmap;
+using ::partition_alloc::internal::SuperPageStateBitmapAddr;
+
+}  // namespace base::internal
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_PAGE_H_
diff --git a/base/allocator/partition_allocator/partition_ref_count.h b/base/allocator/partition_allocator/partition_ref_count.h
index d8df571..ac843e7 100644
--- a/base/allocator/partition_allocator/partition_ref_count.h
+++ b/base/allocator/partition_allocator/partition_ref_count.h
@@ -18,9 +18,7 @@
 #include "base/dcheck_is_on.h"
 #include "build/build_config.h"
 
-namespace base {
-
-namespace internal {
+namespace partition_alloc::internal {
 
 #if BUILDFLAG(USE_BACKUP_REF_PTR)
 
@@ -246,7 +244,21 @@
 
 constexpr size_t kPartitionRefCountSizeAdjustment = kInSlotRefCountBufferSize;
 
-}  // namespace internal
-}  // namespace base
+}  // namespace partition_alloc::internal
+
+namespace base::internal {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+#if BUILDFLAG(USE_BACKUP_REF_PTR)
+using ::partition_alloc::internal::kPartitionPastAllocationAdjustment;
+using ::partition_alloc::internal::PartitionRefCount;
+using ::partition_alloc::internal::PartitionRefCountPointer;
+#endif  // BUILDFLAG(USE_BACKUP_REF_PTR)
+using ::partition_alloc::internal::kInSlotRefCountBufferSize;
+using ::partition_alloc::internal::kPartitionRefCountOffsetAdjustment;
+using ::partition_alloc::internal::kPartitionRefCountSizeAdjustment;
+
+}  // namespace base::internal
 
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_REF_COUNT_H_
diff --git a/base/allocator/partition_allocator/partition_root.cc b/base/allocator/partition_allocator/partition_root.cc
index 332fd5a2..a9cba71c 100644
--- a/base/allocator/partition_allocator/partition_root.cc
+++ b/base/allocator/partition_allocator/partition_root.cc
@@ -748,18 +748,17 @@
   } else if (new_slot_size < current_slot_size) {
     // Shrink by decommitting unneeded pages and making them inaccessible.
     size_t decommit_size = current_slot_size - new_slot_size;
-    DecommitSystemPagesForData(
-        slot_start + new_slot_size, decommit_size,
-        PageAccessibilityDisposition::kUpdatePermissions);
+    DecommitSystemPagesForData(slot_start + new_slot_size, decommit_size,
+                               PageAccessibilityDisposition::kRequireUpdate);
     // Since the decommited system pages are still reserved, we don't need to
     // change the entries for decommitted pages in the reservation offset table.
   } else if (new_slot_size <= available_reservation_size) {
     // Grow within the actually reserved address space. Just need to make the
     // pages accessible again.
     size_t recommit_slot_size_growth = new_slot_size - current_slot_size;
-    RecommitSystemPagesForData(
-        slot_start + current_slot_size, recommit_slot_size_growth,
-        PageAccessibilityDisposition::kUpdatePermissions);
+    RecommitSystemPagesForData(slot_start + current_slot_size,
+                               recommit_slot_size_growth,
+                               PageAccessibilityDisposition::kRequireUpdate);
     // The recommited system pages had been already reserved and all the
     // entries in the reservation offset table (for entire reservation_size
     // region) have been already initialized.
diff --git a/base/allocator/partition_allocator/partition_root.h b/base/allocator/partition_allocator/partition_root.h
index 193e066..48680e05 100644
--- a/base/allocator/partition_allocator/partition_root.h
+++ b/base/allocator/partition_allocator/partition_root.h
@@ -49,6 +49,7 @@
 #include "base/allocator/partition_allocator/partition_alloc_hooks.h"
 #include "base/allocator/partition_allocator/partition_alloc_notreached.h"
 #include "base/allocator/partition_allocator/partition_bucket_lookup.h"
+#include "base/allocator/partition_allocator/partition_cookie.h"
 #include "base/allocator/partition_allocator/partition_direct_map_extent.h"
 #include "base/allocator/partition_allocator/partition_freelist_entry.h"
 #include "base/allocator/partition_allocator/partition_lock.h"
@@ -1815,4 +1816,12 @@
 
 }  // namespace base
 
+namespace partition_alloc::internal {
+
+// TODO(https://crbug.com/1288247): Remove these 'using' declarations once
+// the migration to the new namespaces gets done.
+using ::base::internal::ScopedSyscallTimer;
+
+}  // namespace partition_alloc::internal
+
 #endif  // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_ROOT_H_
diff --git a/base/allocator/partition_allocator/starscan/pcscan_internal.cc b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
index d1d3ab0..50c026c3 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_internal.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
@@ -289,7 +289,7 @@
   RecommitSystemPages(PartitionAddressSpace::RegularPoolBase(),
                       sizeof(QuarantineCardTable),
                       PageAccessibilityConfiguration::kReadWrite,
-                      PageAccessibilityDisposition::kUpdatePermissions);
+                      PageAccessibilityDisposition::kRequireUpdate);
 #endif
 }
 
@@ -1417,7 +1417,7 @@
       RecommitSystemPages(SuperPageStateBitmapAddr(super_page),
                           state_bitmap_size_to_commit,
                           PageAccessibilityConfiguration::kReadWrite,
-                          PageAccessibilityDisposition::kUpdatePermissions);
+                          PageAccessibilityDisposition::kRequireUpdate);
       super_pages.push_back(super_page);
     }
   }
diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc
index 6273caa..6ce961b 100644
--- a/cc/trees/layer_tree_host_pixeltest_filters.cc
+++ b/cc/trees/layer_tree_host_pixeltest_filters.cc
@@ -583,8 +583,8 @@
   filter->SetBackdropFilters(filters);
   filter->ClearBackdropFilterBounds();
 
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH) || \
-    defined(_MIPS_ARCH_LOONGSON) || defined(ARCH_CPU_ARM64)
+#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || defined(_MIPS_ARCH_LOONGSON) || \
+    defined(ARCH_CPU_ARM64)
 #if BUILDFLAG(IS_WIN)
   // Windows has 153 pixels off by at most 2: crbug.com/225027
   float percentage_pixels_large_error = 0.3825f;  // 153px / (200*200)
@@ -594,8 +594,8 @@
     percentage_pixels_large_error = 0.415f;  // 166px / (200*200)
     large_error_allowed = 1;
   }
-#elif BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
-  // There's a 1 pixel error on MacOS and ChromeOS
+#elif BUILDFLAG(IS_MAC)
+  // There's a 1 pixel error on MacOS
   float percentage_pixels_large_error = 0.0025f;  // 1px / (200*200)
   int large_error_allowed = 1;
 #elif defined(_MIPS_ARCH_LOONGSON)
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index ccd96c30..f9551a2 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-99.0.4844.17_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-99.0.4844.19_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
index c9ccaf7..388a10dd 100644
--- a/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
+++ b/chrome/browser/apps/app_service/publishers/publisher_unittest.cc
@@ -268,7 +268,8 @@
                  absl::optional<bool> handles_intents = absl::nullopt,
                  absl::optional<bool> allow_uninstall = absl::nullopt,
                  absl::optional<bool> has_badge = absl::nullopt,
-                 absl::optional<bool> paused = absl::nullopt) {
+                 absl::optional<bool> paused = absl::nullopt,
+                 WindowMode window_mode = WindowMode::kUnknown) {
     AppRegistryCache& cache =
         AppServiceProxyFactory::GetForProfile(profile())->AppRegistryCache();
 
@@ -304,6 +305,9 @@
     VerifyOptionalBool(allow_uninstall, cache.states_[app_id]->allow_uninstall);
     VerifyOptionalBool(has_badge, cache.states_[app_id]->has_badge);
     VerifyOptionalBool(paused, cache.states_[app_id]->paused);
+    if (window_mode != WindowMode::kUnknown) {
+      EXPECT_EQ(window_mode, cache.states_[app_id]->window_mode);
+    }
   }
 
   void VerifyAppIsRemoved(const std::string& app_id) {
@@ -507,6 +511,7 @@
     app->allow_uninstall = apps::mojom::OptionalBool::kTrue;
     app->has_badge = apps::mojom::OptionalBool::kTrue;
     app->paused = apps::mojom::OptionalBool::kTrue;
+    app->window_mode = apps::mojom::WindowMode::kBrowser;
     apps.push_back(std::move(app));
     web_apps_crosapi->OnApps(std::move(apps));
   }
@@ -550,7 +555,7 @@
             /*show_in_launcher=*/true, /*show_in_shelf=*/true,
             /*show_in_search=*/true, /*show_in_management=*/true,
             /*handles_intents=*/true, /*allow_uninstall=*/true,
-            /*has_badge=*/true, /*paused=*/true);
+            /*has_badge=*/true, /*paused=*/true, WindowMode::kBrowser);
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
@@ -624,7 +629,7 @@
             /*show_in_launcher=*/true, /*show_in_shelf=*/true,
             /*show_in_search=*/true, /*show_in_management=*/true,
             /*handles_intents=*/true, /*allow_uninstall=*/true,
-            /*has_badge=*/false, /*paused=*/false);
+            /*has_badge=*/false, /*paused=*/false, WindowMode::kWindow);
   VerifyIntentFilters(app_id);
 }
 
diff --git a/chrome/browser/ash/input_method/assistive_window_controller.cc b/chrome/browser/ash/input_method/assistive_window_controller.cc
index 4fbd68a..16e04d7 100644
--- a/chrome/browser/ash/input_method/assistive_window_controller.cc
+++ b/chrome/browser/ash/input_method/assistive_window_controller.cc
@@ -68,7 +68,7 @@
   // via some parameter.
   suggestion_window_view_ = ui::ime::SuggestionWindowView::Create(
       GetParentView(), this,
-      ui::ime::SuggestionWindowView::Orientation::kHorizontal);
+      ui::ime::SuggestionWindowView::Orientation::kVertical);
   views::Widget* widget = suggestion_window_view_->GetWidget();
   widget->AddObserver(this);
   widget->Show();
diff --git a/chrome/browser/ash/login/existing_user_controller_base_test.cc b/chrome/browser/ash/login/existing_user_controller_base_test.cc
index 7b37c69d..ce1d90e 100644
--- a/chrome/browser/ash/login/existing_user_controller_base_test.cc
+++ b/chrome/browser/ash/login/existing_user_controller_base_test.cc
@@ -6,6 +6,8 @@
 
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/login/users/mock_user_manager.h"
+#include "chrome/browser/ash/settings/device_settings_cache.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "components/user_manager/scoped_user_manager.h"
 
 namespace ash {
@@ -17,6 +19,12 @@
       : mock_user_manager_(mock_user_manager),
         test_local_state_(std::make_unique<TestingPrefServiceSimple>()) {
     RegisterPrefs(test_local_state_->registry());
+    device_settings_cache::RegisterPrefs(test_local_state_->registry());
+    TestingBrowserProcess::GetGlobal()->SetLocalState(test_local_state_.get());
+  }
+
+  ~FakeUserManagerWithLocalState() override {
+    TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
   }
 
   PrefService* GetLocalState() const override {
diff --git a/chrome/browser/ash/login/existing_user_controller_forced_online_auth_unittest.cc b/chrome/browser/ash/login/existing_user_controller_forced_online_auth_unittest.cc
index e59e779b9..5c534688 100644
--- a/chrome/browser/ash/login/existing_user_controller_forced_online_auth_unittest.cc
+++ b/chrome/browser/ash/login/existing_user_controller_forced_online_auth_unittest.cc
@@ -92,8 +92,8 @@
 // them has local copy of password sync token.
 TEST_F(ExistingUserControllerForcedOnlineAuthTest,
        SyncTokenCheckersCreationWithOneToken) {
-  user_manager::known_user::SetPasswordSyncToken(saml_login_account1_id_,
-                                                 kSamlToken1);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetPasswordSyncToken(saml_login_account1_id_, kSamlToken1);
   set_hide_user_names_on_signin();
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account2_id_);
@@ -108,10 +108,10 @@
 // sync tokens.
 TEST_F(ExistingUserControllerForcedOnlineAuthTest,
        SyncTokenCheckersCreationWithTwoTokens) {
-  user_manager::known_user::SetPasswordSyncToken(saml_login_account1_id_,
-                                                 kSamlToken1);
-  user_manager::known_user::SetPasswordSyncToken(saml_login_account2_id_,
-                                                 kSamlToken2);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetPasswordSyncToken(saml_login_account1_id_, kSamlToken1);
+  known_user.SetPasswordSyncToken(saml_login_account2_id_, kSamlToken2);
+
   set_hide_user_names_on_signin();
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account2_id_);
@@ -126,10 +126,10 @@
 // Tests sync token checkers removal in case of failed token validation.
 TEST_F(ExistingUserControllerForcedOnlineAuthTest,
        SyncTokenCheckersInvalidPasswordForTwoUsers) {
-  user_manager::known_user::SetPasswordSyncToken(saml_login_account1_id_,
-                                                 kSamlToken1);
-  user_manager::known_user::SetPasswordSyncToken(saml_login_account2_id_,
-                                                 kSamlToken2);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetPasswordSyncToken(saml_login_account1_id_, kSamlToken1);
+  known_user.SetPasswordSyncToken(saml_login_account2_id_, kSamlToken2);
+
   set_hide_user_names_on_signin();
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account2_id_);
@@ -144,10 +144,10 @@
 // visible.
 TEST_F(ExistingUserControllerForcedOnlineAuthTest,
        NoSyncTokenCheckersWhenPodsVisible) {
-  user_manager::known_user::SetPasswordSyncToken(saml_login_account1_id_,
-                                                 kSamlToken1);
-  user_manager::known_user::SetPasswordSyncToken(saml_login_account2_id_,
-                                                 kSamlToken2);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetPasswordSyncToken(saml_login_account1_id_, kSamlToken1);
+  known_user.SetPasswordSyncToken(saml_login_account2_id_, kSamlToken2);
+
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account2_id_);
   existing_user_controller()->Init(mock_user_manager()->GetUsers());
diff --git a/chrome/browser/ash/login/login_browsertest.cc b/chrome/browser/ash/login/login_browsertest.cc
index 9ef069c..86b0bda 100644
--- a/chrome/browser/ash/login/login_browsertest.cc
+++ b/chrome/browser/ash/login/login_browsertest.cc
@@ -37,6 +37,7 @@
 #include "chrome/browser/ash/login/test/user_adding_screen_utils.h"
 #include "chrome/browser/ash/login/ui/login_display_host_webui.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
@@ -226,10 +227,10 @@
   void SetUserOnlineLoginState() {
     const base::Time now = base::Time::Now();
 
-    user_manager::known_user::SetLastOnlineSignin(managed_user_id_,
-                                                  now - kLoginOnlineLongDelay);
-    user_manager::known_user::SetOfflineSigninLimit(managed_user_id_,
-                                                    kLoginOnlineShortDelay);
+    user_manager::KnownUser known_user(g_browser_process->local_state());
+    known_user.SetLastOnlineSignin(managed_user_id_,
+                                   now - kLoginOnlineLongDelay);
+    known_user.SetOfflineSigninLimit(managed_user_id_, kLoginOnlineShortDelay);
   }
 
  protected:
diff --git a/chrome/browser/ash/login/saml/in_session_password_change_manager.cc b/chrome/browser/ash/login/saml/in_session_password_change_manager.cc
index 8856e64..57a06ee4 100644
--- a/chrome/browser/ash/login/saml/in_session_password_change_manager.cc
+++ b/chrome/browser/ash/login/saml/in_session_password_change_manager.cc
@@ -438,8 +438,8 @@
   // Set token value in prefs for in-session operations and ephemeral users and
   // local settings for login screen sync.
   prefs->SetString(prefs::kSamlPasswordSyncToken, sync_token);
-  user_manager::known_user::SetPasswordSyncToken(primary_user_->GetAccountId(),
-                                                 sync_token);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetPasswordSyncToken(primary_user_->GetAccountId(), sync_token);
 }
 
 void InSessionPasswordChangeManager::OnTokenFetched(
diff --git a/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc b/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc
index dc95021..393ac43 100644
--- a/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc
+++ b/chrome/browser/ash/login/saml/in_session_password_sync_manager.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/ash/login/saml/password_sync_token_fetcher.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
 #include "chrome/browser/ash/settings/cros_settings.h"
+#include "chrome/browser/browser_process.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
 #include "components/session_manager/core/session_manager_observer.h"
@@ -124,8 +125,8 @@
 
   user_manager::UserManager::Get()->SaveForceOnlineSignin(
       primary_user_->GetAccountId(), false);
-  user_manager::known_user::SetLastOnlineSignin(primary_user_->GetAccountId(),
-                                                now);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetLastOnlineSignin(primary_user_->GetAccountId(), now);
 }
 
 void InSessionPasswordSyncManager::CreateTokenAsync() {
@@ -141,8 +142,8 @@
   // Set token value in prefs for in-session operations and ephemeral users and
   // local settings for login screen sync.
   prefs->SetString(prefs::kSamlPasswordSyncToken, token);
-  user_manager::known_user::SetPasswordSyncToken(primary_user_->GetAccountId(),
-                                                 token);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetPasswordSyncToken(primary_user_->GetAccountId(), token);
   lock_screen_reauth_reason_ = ReauthenticationReason::kNone;
 }
 
@@ -158,8 +159,8 @@
     // Set token fetched from the endpoint in prefs and local settings.
     PrefService* prefs = primary_profile_->GetPrefs();
     prefs->SetString(prefs::kSamlPasswordSyncToken, token);
-    user_manager::known_user::SetPasswordSyncToken(
-        primary_user_->GetAccountId(), token);
+    user_manager::KnownUser known_user(g_browser_process->local_state());
+    known_user.SetPasswordSyncToken(primary_user_->GetAccountId(), token);
     lock_screen_reauth_reason_ = ReauthenticationReason::kNone;
   } else {
     // This is the first time a sync token is created for the user: we need to
diff --git a/chrome/browser/ash/login/saml/in_session_password_sync_manager_unittest.cc b/chrome/browser/ash/login/saml/in_session_password_sync_manager_unittest.cc
index c3d7268..a89f8dd9 100644
--- a/chrome/browser/ash/login/saml/in_session_password_sync_manager_unittest.cc
+++ b/chrome/browser/ash/login/saml/in_session_password_sync_manager_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "chrome/browser/ash/login/saml/in_session_password_sync_manager.h"
+#include <memory>
 
 #include "ash/components/login/auth/user_context.h"
 #include "ash/constants/ash_features.h"
@@ -12,6 +13,7 @@
 #include "chrome/browser/ash/login/saml/mock_lock_handler.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/login/users/mock_user_manager.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -34,22 +36,6 @@
 
 const char kFakeToken[] = "fake-token";
 
-class FakeUserManagerWithLocalState : public FakeChromeUserManager {
- public:
-  FakeUserManagerWithLocalState()
-      : test_local_state_(std::make_unique<TestingPrefServiceSimple>()) {
-    RegisterPrefs(test_local_state_->registry());
-  }
-  ~FakeUserManagerWithLocalState() override = default;
-
-  PrefService* GetLocalState() const override {
-    return test_local_state_.get();
-  }
-
- private:
-  std::unique_ptr<TestingPrefServiceSimple> test_local_state_;
-};
-
 }  // namespace
 
 class InSessionPasswordSyncManagerTest : public testing::Test {
@@ -96,14 +82,14 @@
       features::kEnableSamlReauthenticationOnLockscreen);
 
   std::unique_ptr<FakeChromeUserManager> fake_user_manager =
-      std::make_unique<FakeUserManagerWithLocalState>();
+      std::make_unique<FakeChromeUserManager>();
   scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
       std::move(fake_user_manager));
 
   user_manager_ =
       static_cast<FakeChromeUserManager*>(user_manager::UserManager::Get());
-  known_user_ =
-      std::make_unique<user_manager::KnownUser>(user_manager_->GetLocalState());
+  known_user_ = std::make_unique<user_manager::KnownUser>(
+      g_browser_process->local_state());
 }
 
 InSessionPasswordSyncManagerTest::~InSessionPasswordSyncManagerTest() {
@@ -116,10 +102,10 @@
   secondary_profile_ = profile_manager_.CreateTestingProfile("test2");
 
   user_manager_->AddUserWithAffiliationAndTypeAndProfile(
-      saml_login_account_id1_, /* is_afiliated = */ false,
+      saml_login_account_id1_, /* is_affiliated = */ false,
       user_manager::UserType::USER_TYPE_REGULAR, primary_profile_);
   user_manager_->AddUserWithAffiliationAndTypeAndProfile(
-      saml_login_account_id2_, /* is_afiliated = */ false,
+      saml_login_account_id2_, /* is_affiliated = */ false,
       user_manager::UserType::USER_TYPE_REGULAR, secondary_profile_);
   user_manager_->AddUser(saml_login_account_id2_);
   user_manager_->LoginUser(saml_login_account_id1_);
@@ -292,9 +278,10 @@
   EXPECT_EQ(InSessionReauthReason(),
             InSessionPasswordSyncManager::ReauthenticationReason::kNone);
   EXPECT_FALSE(IsTokenFetcherCreated());
-  std::string sync_token =
+  const std::string* sync_token =
       known_user_->GetPasswordSyncToken(saml_login_account_id1_);
-  EXPECT_EQ(kFakeToken, sync_token);
+  ASSERT_TRUE(sync_token);
+  EXPECT_EQ(kFakeToken, *sync_token);
 }
 
 TEST_F(InSessionPasswordSyncManagerTest, PolicySetToFalse) {
diff --git a/chrome/browser/ash/login/saml/password_sync_token_checkers_collection.cc b/chrome/browser/ash/login/saml/password_sync_token_checkers_collection.cc
index a9699aee..0ea4139 100644
--- a/chrome/browser/ash/login/saml/password_sync_token_checkers_collection.cc
+++ b/chrome/browser/ash/login/saml/password_sync_token_checkers_collection.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/login/saml/password_sync_token_checkers_collection.h"
 
 #include "base/containers/contains.h"
+#include "chrome/browser/browser_process.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user_manager.h"
 
@@ -36,29 +37,32 @@
     if (!user->using_saml() || user->force_online_signin())
       continue;
 
-    const std::string sync_token =
-        user_manager::known_user::GetPasswordSyncToken(user->GetAccountId());
-    if (!sync_token.empty() &&
-        !base::Contains(sync_token_checkers_, sync_token)) {
+    user_manager::KnownUser known_user(g_browser_process->local_state());
+    const std::string* sync_token =
+        known_user.GetPasswordSyncToken(user->GetAccountId());
+    if (sync_token && !sync_token->empty() &&
+        !base::Contains(sync_token_checkers_, *sync_token)) {
       sync_token_checkers_.insert(
-          {sync_token,
+          {*sync_token,
            std::make_unique<PasswordSyncTokenLoginChecker>(
-               user->GetAccountId(), sync_token, &sync_token_retry_backoff_)});
+               user->GetAccountId(), *sync_token, &sync_token_retry_backoff_)});
       if (observer)
-        sync_token_checkers_[sync_token]->AddObserver(observer);
-      sync_token_checkers_[sync_token]->AddObserver(this);
-      sync_token_checkers_[sync_token]->RecordTokenPollingStart();
-      sync_token_checkers_[sync_token]->CheckForPasswordNotInSync();
+        sync_token_checkers_[*sync_token]->AddObserver(observer);
+      sync_token_checkers_[*sync_token]->AddObserver(this);
+      sync_token_checkers_[*sync_token]->RecordTokenPollingStart();
+      sync_token_checkers_[*sync_token]->CheckForPasswordNotInSync();
     }
   }
 }
 
 void PasswordSyncTokenCheckersCollection::OnInvalidSyncToken(
     const AccountId& account_id) {
-  const std::string sync_token =
-      user_manager::known_user::GetPasswordSyncToken(account_id);
-  if (base::Contains(sync_token_checkers_, sync_token))
-    sync_token_checkers_.erase(sync_token);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  const std::string* sync_token = known_user.GetPasswordSyncToken(account_id);
+  if (!sync_token)
+    return;
+  if (base::Contains(sync_token_checkers_, *sync_token))
+    sync_token_checkers_.erase(*sync_token);
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/saml/password_sync_token_verifier.cc b/chrome/browser/ash/login/saml/password_sync_token_verifier.cc
index 92ab4e03..8706a8c1f 100644
--- a/chrome/browser/ash/login/saml/password_sync_token_verifier.cc
+++ b/chrome/browser/ash/login/saml/password_sync_token_verifier.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/ash/login/saml/in_session_password_sync_manager.h"
 #include "chrome/browser/ash/login/saml/in_session_password_sync_manager_factory.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/browser_process.h"
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/known_user.h"
 #include "content/public/browser/storage_partition.h"
@@ -126,8 +127,8 @@
   // Set token value in prefs for in-session operations and ephemeral users and
   // local settings for login screen sync.
   prefs->SetString(prefs::kSamlPasswordSyncToken, sync_token);
-  user_manager::known_user::SetPasswordSyncToken(primary_user_->GetAccountId(),
-                                                 sync_token);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetPasswordSyncToken(primary_user_->GetAccountId(), sync_token);
   password_sync_token_fetcher_.reset();
   RecordTokenPollingStart();
   RecheckAfter(retry_backoff_.GetTimeUntilRelease());
@@ -139,8 +140,8 @@
     // Set token fetched from the endpoint in prefs and local settings.
     PrefService* prefs = primary_profile_->GetPrefs();
     prefs->SetString(prefs::kSamlPasswordSyncToken, sync_token);
-    user_manager::known_user::SetPasswordSyncToken(
-        primary_user_->GetAccountId(), sync_token);
+    user_manager::KnownUser known_user(g_browser_process->local_state());
+    known_user.SetPasswordSyncToken(primary_user_->GetAccountId(), sync_token);
     RecordTokenPollingStart();
     RecheckAfter(retry_backoff_.GetTimeUntilRelease());
   } else {
diff --git a/chrome/browser/ash/login/saml/password_sync_token_verifier_unittest.cc b/chrome/browser/ash/login/saml/password_sync_token_verifier_unittest.cc
index 4e978ce..a829033 100644
--- a/chrome/browser/ash/login/saml/password_sync_token_verifier_unittest.cc
+++ b/chrome/browser/ash/login/saml/password_sync_token_verifier_unittest.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/login/users/mock_user_manager.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
@@ -31,22 +32,6 @@
 
 constexpr base::TimeDelta kSyncTokenCheckBelowInterval = base::Minutes(4);
 
-class FakeUserManagerWithLocalState : public FakeChromeUserManager {
- public:
-  FakeUserManagerWithLocalState()
-      : test_local_state_(std::make_unique<TestingPrefServiceSimple>()) {
-    RegisterPrefs(test_local_state_->registry());
-  }
-  ~FakeUserManagerWithLocalState() override = default;
-
-  PrefService* GetLocalState() const override {
-    return test_local_state_.get();
-  }
-
- private:
-  std::unique_ptr<TestingPrefServiceSimple> test_local_state_;
-};
-
 }  // namespace
 
 class PasswordSyncTokenVerifierTest : public testing::Test {
@@ -79,14 +64,14 @@
 
 PasswordSyncTokenVerifierTest::PasswordSyncTokenVerifierTest() {
   std::unique_ptr<FakeChromeUserManager> fake_user_manager =
-      std::make_unique<FakeUserManagerWithLocalState>();
+      std::make_unique<FakeChromeUserManager>();
   scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
       std::move(fake_user_manager));
 
   user_manager_ =
       static_cast<FakeChromeUserManager*>(user_manager::UserManager::Get());
-  known_user_ =
-      std::make_unique<user_manager::KnownUser>(user_manager_->GetLocalState());
+  known_user_ = std::make_unique<user_manager::KnownUser>(
+      g_browser_process->local_state());
 }
 
 PasswordSyncTokenVerifierTest::~PasswordSyncTokenVerifierTest() {
@@ -98,7 +83,7 @@
   primary_profile_ = profile_manager_.CreateTestingProfile("test1");
 
   user_manager_->AddUserWithAffiliationAndTypeAndProfile(
-      saml_login_account_id_, /* is_afiliated = */ false,
+      saml_login_account_id_, /* is_affiliated = */ false,
       user_manager::UserType::USER_TYPE_REGULAR, primary_profile_);
   user_manager_->LoginUser(saml_login_account_id_);
   // ActiveUser in FakeChromeUserManager needs to be set explicitly.
@@ -185,7 +170,7 @@
   CreatePasswordSyncTokenVerifier();
   verifier_->FetchSyncTokenOnReauth();
   verifier_->OnTokenFetched(kSyncToken);
-  EXPECT_EQ(known_user_->GetPasswordSyncToken(saml_login_account_id_),
+  EXPECT_EQ(*known_user_->GetPasswordSyncToken(saml_login_account_id_),
             kSyncToken);
   EXPECT_EQ(
       primary_profile_->GetPrefs()->GetString(prefs::kSamlPasswordSyncToken),
@@ -197,7 +182,7 @@
   verifier_->FetchSyncTokenOnReauth();
   verifier_->OnApiCallFailed(PasswordSyncTokenFetcher::ErrorType::kGetNoList);
   verifier_->OnTokenCreated(kSyncToken);
-  EXPECT_EQ(known_user_->GetPasswordSyncToken(saml_login_account_id_),
+  EXPECT_EQ(*known_user_->GetPasswordSyncToken(saml_login_account_id_),
             kSyncToken);
   EXPECT_EQ(
       primary_profile_->GetPrefs()->GetString(prefs::kSamlPasswordSyncToken),
@@ -210,7 +195,7 @@
   // Token API not initilized for the user - request token creation.
   verifier_->OnTokenFetched(std::string());
   verifier_->OnTokenCreated(kSyncToken);
-  EXPECT_EQ(known_user_->GetPasswordSyncToken(saml_login_account_id_),
+  EXPECT_EQ(*known_user_->GetPasswordSyncToken(saml_login_account_id_),
             kSyncToken);
   EXPECT_EQ(
       primary_profile_->GetPrefs()->GetString(prefs::kSamlPasswordSyncToken),
diff --git a/chrome/browser/ash/login/screens/offline_login_screen.cc b/chrome/browser/ash/login/screens/offline_login_screen.cc
index 51fccaa0..c5509ec 100644
--- a/chrome/browser/ash/login/screens/offline_login_screen.cc
+++ b/chrome/browser/ash/login/screens/offline_login_screen.cc
@@ -174,15 +174,16 @@
 void OfflineLoginScreen::HandleEmailSubmitted(const std::string& email) {
   bool offline_limit_expired = false;
   const std::string sanitized_email = gaia::SanitizeEmail(email);
-  const AccountId account_id = user_manager::known_user::GetAccountId(
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  const AccountId account_id = known_user.GetAccountId(
       sanitized_email, std::string(), AccountType::UNKNOWN);
   const absl::optional<base::TimeDelta> offline_signin_interval =
-      user_manager::known_user::GetOfflineSigninLimit(account_id);
+      known_user.GetOfflineSigninLimit(account_id);
 
   // Further checks only if the limit is set.
   if (offline_signin_interval) {
     const base::Time last_online_signin =
-        user_manager::known_user::GetLastOnlineSignin(account_id);
+        known_user.GetLastOnlineSignin(account_id);
 
     offline_limit_expired =
         login::TimeToOnlineSignIn(last_online_signin,
diff --git a/chrome/browser/ash/login/screens/user_selection_screen.cc b/chrome/browser/ash/login/screens/user_selection_screen.cc
index 46781ecb..d41b42f 100644
--- a/chrome/browser/ash/login/screens/user_selection_screen.cc
+++ b/chrome/browser/ash/login/screens/user_selection_screen.cc
@@ -478,13 +478,14 @@
     return true;
   }
 
+  user_manager::KnownUser known_user(g_browser_process->local_state());
   const absl::optional<base::TimeDelta> offline_signin_time_limit =
-      user_manager::known_user::GetOfflineSigninLimit(user->GetAccountId());
+      known_user.GetOfflineSigninLimit(user->GetAccountId());
   if (!offline_signin_time_limit)
     return false;
 
   const base::Time last_gaia_signin_time =
-      user_manager::known_user::GetLastOnlineSignin(user->GetAccountId());
+      known_user.GetLastOnlineSignin(user->GetAccountId());
   if (last_gaia_signin_time == base::Time())
     return false;
   const base::Time now = base::DefaultClock::GetInstance()->Now();
diff --git a/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc b/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc
index d9bf96d..82bdefc8e 100644
--- a/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc
+++ b/chrome/browser/ash/login/screens/user_selection_screen_browsertest.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/ash/login/test/offline_login_test_mixin.h"
 #include "chrome/browser/ash/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/ash/login/test/test_predicate_waiter.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
 #include "chromeos/dbus/userdataauth/fake_userdataauth_client.h"
@@ -141,16 +142,17 @@
     const auto& users = login_manager_mixin_.users();
     const base::Time now = base::DefaultClock::GetInstance()->Now();
 
+    user_manager::KnownUser known_user(g_browser_process->local_state());
     // User with expired offline login timeout.
-    user_manager::known_user::SetLastOnlineSignin(users[0].account_id,
-                                                  now - kLoginOnlineLongDelay);
-    user_manager::known_user::SetOfflineSigninLimit(users[0].account_id,
-                                                    kLoginOnlineShortDelay);
+    known_user.SetLastOnlineSignin(users[0].account_id,
+                                   now - kLoginOnlineLongDelay);
+    known_user.SetOfflineSigninLimit(users[0].account_id,
+                                     kLoginOnlineShortDelay);
 
     // User withoin offline login timeout.
-    user_manager::known_user::SetLastOnlineSignin(users[1].account_id, now);
-    user_manager::known_user::SetOfflineSigninLimit(users[1].account_id,
-                                                    kLoginOnlineShortDelay);
+    known_user.SetLastOnlineSignin(users[1].account_id, now);
+    known_user.SetOfflineSigninLimit(users[1].account_id,
+                                     kLoginOnlineShortDelay);
   }
 
  protected:
@@ -183,15 +185,15 @@
   void SetUpLocalState() override {
     const base::Time now = base::DefaultClock::GetInstance()->Now();
 
-    user_manager::known_user::SetLastOnlineSignin(
-        test_user_over_the_limit_.account_id, now - kLoginOnlineLongDelay);
-    user_manager::known_user::SetOfflineSigninLimit(
-        test_user_over_the_limit_.account_id, kLoginOnlineShortDelay);
+    user_manager::KnownUser known_user(g_browser_process->local_state());
+    known_user.SetLastOnlineSignin(test_user_over_the_limit_.account_id,
+                                   now - kLoginOnlineLongDelay);
+    known_user.SetOfflineSigninLimit(test_user_over_the_limit_.account_id,
+                                     kLoginOnlineShortDelay);
 
-    user_manager::known_user::SetLastOnlineSignin(
-        test_user_under_the_limit_.account_id, now);
-    user_manager::known_user::SetOfflineSigninLimit(
-        test_user_under_the_limit_.account_id, kLoginOnlineShortDelay);
+    known_user.SetLastOnlineSignin(test_user_under_the_limit_.account_id, now);
+    known_user.SetOfflineSigninLimit(test_user_under_the_limit_.account_id,
+                                     kLoginOnlineShortDelay);
   }
 
  protected:
diff --git a/chrome/browser/ash/login/signin/offline_signin_limiter.cc b/chrome/browser/ash/login/signin/offline_signin_limiter.cc
index 2dffe63..7ee51b5 100644
--- a/chrome/browser/ash/login/signin/offline_signin_limiter.cc
+++ b/chrome/browser/ash/login/signin/offline_signin_limiter.cc
@@ -23,6 +23,7 @@
 #include "chrome/browser/ash/login/login_pref_names.h"
 #include "chrome/browser/ash/login/reauth_stats.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/prefs/pref_service.h"
 #include "components/session_manager/core/session_manager.h"
@@ -410,8 +411,9 @@
     return;
   }
 
-  user_manager::known_user::SetLastOnlineSignin(user->GetAccountId(), time);
-  user_manager::known_user::SetOfflineSigninLimit(user->GetAccountId(), limit);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetLastOnlineSignin(user->GetAccountId(), time);
+  known_user.SetOfflineSigninLimit(user->GetAccountId(), limit);
 }
 
 }  // namespace ash
diff --git a/chrome/browser/ash/login/ui/login_display_host_mojo.cc b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
index 2324bb5..9d5f6a079 100644
--- a/chrome/browser/ash/login/ui/login_display_host_mojo.cc
+++ b/chrome/browser/ash/login/ui/login_display_host_mojo.cc
@@ -822,13 +822,14 @@
     const AccountId& account_id) {
   bool offline_limit_expired = false;
 
+  user_manager::KnownUser known_user(g_browser_process->local_state());
   const absl::optional<base::TimeDelta> offline_signin_interval =
-      user_manager::known_user::GetOfflineSigninLimit(account_id);
+      known_user.GetOfflineSigninLimit(account_id);
 
   // Check if the limit is set only.
   if (offline_signin_interval) {
     const base::Time last_online_signin =
-        user_manager::known_user::GetLastOnlineSignin(account_id);
+        known_user.GetLastOnlineSignin(account_id);
     offline_limit_expired =
         login::TimeToOnlineSignIn(last_online_signin,
                                   offline_signin_interval.value()) <=
diff --git a/chrome/browser/ash/login/user_online_signin_notifier.cc b/chrome/browser/ash/login/user_online_signin_notifier.cc
index 130e14a7..593add9 100644
--- a/chrome/browser/ash/login/user_online_signin_notifier.cc
+++ b/chrome/browser/ash/login/user_online_signin_notifier.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ash/login/user_online_signin_notifier.h"
 
 #include "chrome/browser/ash/login/helper.h"
+#include "chrome/browser/browser_process.h"
 #include "components/user_manager/known_user.h"
 #include "components/user_manager/user_manager.h"
 
@@ -19,15 +20,16 @@
 
 void UserOnlineSigninNotifier::CheckForPolicyEnforcedOnlineSignin() {
   base::TimeDelta min_delta = base::TimeDelta::Max();
+  user_manager::KnownUser known_user(g_browser_process->local_state());
   for (auto* user : users_) {
     const absl::optional<base::TimeDelta> offline_signin_limit =
-        user_manager::known_user::GetOfflineSigninLimit(user->GetAccountId());
+        known_user.GetOfflineSigninLimit(user->GetAccountId());
     if (!offline_signin_limit) {
       continue;
     }
 
     const base::Time last_online_signin =
-        user_manager::known_user::GetLastOnlineSignin(user->GetAccountId());
+        known_user.GetLastOnlineSignin(user->GetAccountId());
     base::TimeDelta time_to_next_online_signin = login::TimeToOnlineSignIn(
         last_online_signin, offline_signin_limit.value());
     if (time_to_next_online_signin.is_positive() &&
diff --git a/chrome/browser/ash/login/user_online_signin_notifier_unittest.cc b/chrome/browser/ash/login/user_online_signin_notifier_unittest.cc
index 7a579ff..7e029a81 100644
--- a/chrome/browser/ash/login/user_online_signin_notifier_unittest.cc
+++ b/chrome/browser/ash/login/user_online_signin_notifier_unittest.cc
@@ -16,7 +16,6 @@
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
 #include "components/user_manager/known_user.h"
-#include "components/user_manager/scoped_user_manager.h"
 #include "content/public/test/browser_task_environment.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -62,9 +61,10 @@
 // Tests login screen update when SAMLOfflineSigninTimeLimit policy is set.
 TEST_F(UserOnlineSigninNotifierTest, SamlOnlineAuthSingleUser) {
   const base::Time now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetLastOnlineSignin(saml_login_account1_id_, now);
-  user_manager::known_user::SetOfflineSigninLimit(saml_login_account1_id_,
-                                                  kLoginOnlineShortDelay);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetLastOnlineSignin(saml_login_account1_id_, now);
+  known_user.SetOfflineSigninLimit(saml_login_account1_id_,
+                                   kLoginOnlineShortDelay);
 
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
   user_online_signin_notifier_ = std::make_unique<UserOnlineSigninNotifier>(
@@ -84,9 +84,10 @@
 // Verfies that `OfflineSigninLimiter` does affect SAML and non SAML user.
 TEST_F(UserOnlineSigninNotifierTest, OfflineLimiteOutOfSessionSAMLAndNonSAML) {
   const base::Time now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetLastOnlineSignin(saml_login_account1_id_, now);
-  user_manager::known_user::SetOfflineSigninLimit(saml_login_account1_id_,
-                                                  kLoginOnlineShortDelay);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetLastOnlineSignin(saml_login_account1_id_, now);
+  known_user.SetOfflineSigninLimit(saml_login_account1_id_,
+                                   kLoginOnlineShortDelay);
 
   mock_user_manager()->AddUser(saml_login_account1_id_);
   user_online_signin_notifier_ = std::make_unique<UserOnlineSigninNotifier>(
@@ -104,15 +105,16 @@
 // Tests login screen update functionality for 2 SAML users.
 TEST_F(UserOnlineSigninNotifierTest, SamlOnlineAuthTwoSamlUsers) {
   base::Time now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetLastOnlineSignin(saml_login_account1_id_, now);
-  user_manager::known_user::SetOfflineSigninLimit(saml_login_account1_id_,
-                                                  kLoginOnlineLongDelay);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetLastOnlineSignin(saml_login_account1_id_, now);
+  known_user.SetOfflineSigninLimit(saml_login_account1_id_,
+                                   kLoginOnlineLongDelay);
 
   task_environment_.FastForwardBy(kLoginOnlineShortDelay);
   now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetLastOnlineSignin(saml_login_account2_id_, now);
-  user_manager::known_user::SetOfflineSigninLimit(saml_login_account2_id_,
-                                                  kLoginOnlineVeryLongDelay);
+  known_user.SetLastOnlineSignin(saml_login_account2_id_, now);
+  known_user.SetOfflineSigninLimit(saml_login_account2_id_,
+                                   kLoginOnlineVeryLongDelay);
 
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account2_id_);
@@ -135,13 +137,14 @@
 // Tests login screen update functionality for 2 users: SAML and non-SAML.
 TEST_F(UserOnlineSigninNotifierTest, SamlOnlineAuthSamlAndNonSamlUsers) {
   const base::Time now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetLastOnlineSignin(saml_login_account1_id_, now);
-  user_manager::known_user::SetLastOnlineSignin(saml_login_account2_id_, now);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetLastOnlineSignin(saml_login_account1_id_, now);
+  known_user.SetLastOnlineSignin(saml_login_account2_id_, now);
 
-  user_manager::known_user::SetOfflineSigninLimit(saml_login_account1_id_,
-                                                  kLoginOnlineShortDelay);
-  user_manager::known_user::SetOfflineSigninLimit(saml_login_account2_id_,
-                                                  kLoginOnlineLongDelay);
+  known_user.SetOfflineSigninLimit(saml_login_account1_id_,
+                                   kLoginOnlineShortDelay);
+  known_user.SetOfflineSigninLimit(saml_login_account2_id_,
+                                   kLoginOnlineLongDelay);
 
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
   mock_user_manager()->AddUser(saml_login_account2_id_);
@@ -164,9 +167,9 @@
 // Tests unset policy value in local state.
 TEST_F(UserOnlineSigninNotifierTest, SamlOnlineAuthSamlPolicyNotSet) {
   const base::Time now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetLastOnlineSignin(saml_login_account1_id_, now);
-  user_manager::known_user::SetOfflineSigninLimit(saml_login_account1_id_,
-                                                  absl::nullopt);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetLastOnlineSignin(saml_login_account1_id_, now);
+  known_user.SetOfflineSigninLimit(saml_login_account1_id_, absl::nullopt);
 
   mock_user_manager()->AddPublicAccountWithSAML(saml_login_account1_id_);
   user_online_signin_notifier_ = std::make_unique<UserOnlineSigninNotifier>(
@@ -188,8 +191,9 @@
 TEST_F(UserOnlineSigninNotifierTest,
        GaiaOnlineAuthSingleUserNoLastOnlineSignin) {
   const base::Time now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetOfflineSigninLimit(gaia_login_account1_id_,
-                                                  kLoginOnlineShortDelay);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetOfflineSigninLimit(gaia_login_account1_id_,
+                                   kLoginOnlineShortDelay);
 
   mock_user_manager()->AddUser(gaia_login_account1_id_);
   user_online_signin_notifier_ = std::make_unique<UserOnlineSigninNotifier>(
@@ -204,7 +208,7 @@
   // enforce the next login to be online. No timer should be running.
   EXPECT_FALSE(online_login_refresh_timer()->IsRunning());
   // User logged in online after enforcement.
-  user_manager::known_user::SetLastOnlineSignin(gaia_login_account1_id_, now);
+  known_user.SetLastOnlineSignin(gaia_login_account1_id_, now);
   user_online_signin_notifier_ = std::make_unique<UserOnlineSigninNotifier>(
       mock_user_manager()->GetUsers());
   user_online_signin_notifier_->AddObserver(
@@ -223,9 +227,10 @@
 // and the last online sign in has been set.
 TEST_F(UserOnlineSigninNotifierTest, GaiaOnlineAuthSingleUserLastOnlineSignin) {
   const base::Time now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetLastOnlineSignin(gaia_login_account1_id_, now);
-  user_manager::known_user::SetOfflineSigninLimit(gaia_login_account1_id_,
-                                                  kLoginOnlineShortDelay);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetLastOnlineSignin(gaia_login_account1_id_, now);
+  known_user.SetOfflineSigninLimit(gaia_login_account1_id_,
+                                   kLoginOnlineShortDelay);
 
   mock_user_manager()->AddUser(gaia_login_account1_id_);
   user_online_signin_notifier_ = std::make_unique<UserOnlineSigninNotifier>(
@@ -245,15 +250,16 @@
 // Tests login screen update functionality for 2 Gaia without SAML users.
 TEST_F(UserOnlineSigninNotifierTest, GaiaOnlineAuthTwoGaiaUsers) {
   base::Time now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetLastOnlineSignin(gaia_login_account1_id_, now);
-  user_manager::known_user::SetOfflineSigninLimit(gaia_login_account1_id_,
-                                                  kLoginOnlineLongDelay);
+  user_manager::KnownUser known_user(g_browser_process->local_state());
+  known_user.SetLastOnlineSignin(gaia_login_account1_id_, now);
+  known_user.SetOfflineSigninLimit(gaia_login_account1_id_,
+                                   kLoginOnlineLongDelay);
 
   task_environment_.FastForwardBy(kLoginOnlineShortDelay);
   now = base::DefaultClock::GetInstance()->Now();
-  user_manager::known_user::SetLastOnlineSignin(gaia_login_account2_id_, now);
-  user_manager::known_user::SetOfflineSigninLimit(gaia_login_account2_id_,
-                                                  kLoginOnlineVeryLongDelay);
+  known_user.SetLastOnlineSignin(gaia_login_account2_id_, now);
+  known_user.SetOfflineSigninLimit(gaia_login_account2_id_,
+                                   kLoginOnlineVeryLongDelay);
 
   mock_user_manager()->AddUser(gaia_login_account1_id_);
   mock_user_manager()->AddUser(gaia_login_account2_id_);
@@ -279,15 +285,14 @@
 // Tests unset `GaiaOfflineTimeLimitDays` policy value in local state.
 TEST_F(UserOnlineSigninNotifierTest, GaiaOnlineAuthGaiaPolicyNotSet) {
   const base::Time now = base::DefaultClock::GetInstance()->Now();
+  user_manager::KnownUser known_user(g_browser_process->local_state());
   // No `LastOnlineSignin` value, case where devices didn't store that value in
   // the first Gaia login.
-  user_manager::known_user::SetOfflineSigninLimit(gaia_login_account1_id_,
-                                                  absl::nullopt);
+  known_user.SetOfflineSigninLimit(gaia_login_account1_id_, absl::nullopt);
 
   // Case where the user has already stored last online signin.
-  user_manager::known_user::SetLastOnlineSignin(gaia_login_account2_id_, now);
-  user_manager::known_user::SetOfflineSigninLimit(gaia_login_account2_id_,
-                                                  absl::nullopt);
+  known_user.SetLastOnlineSignin(gaia_login_account2_id_, now);
+  known_user.SetOfflineSigninLimit(gaia_login_account2_id_, absl::nullopt);
 
   mock_user_manager()->AddUser(gaia_login_account1_id_);
   mock_user_manager()->AddUser(gaia_login_account2_id_);
diff --git a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
index 76d74ff..850b0b90 100644
--- a/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
+++ b/chrome/browser/ash/policy/dlp/dlp_content_manager_ash_browsertest.cc
@@ -228,8 +228,6 @@
   std::unique_ptr<DlpContentManagerTestHelper> helper_;
   base::HistogramTester histogram_tester_;
   MockDlpRulesManager* mock_rules_manager_;
-
- private:
   std::vector<DlpPolicyEvent> events_;
 };
 
@@ -909,12 +907,18 @@
 
   helper_->ChangeConfidentiality(web_contents, kScreenShareWarned);
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 1);
+  CheckEvents(DlpRulesManager::Restriction::kScreenShare,
+              DlpRulesManager::Level::kWarn, 1u);
 
   // Hit Enter to "Share anyway".
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
       browser(), ui::VKEY_RETURN, /*control=*/false,
       /*shift=*/false, /*alt=*/false, /*command=*/false));
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 0);
+  EXPECT_EQ(events_.size(), 2u);
+  EXPECT_THAT(events_[1],
+              IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenShare)));
 
   EXPECT_TRUE(helper_->HasContentCachedForRestriction(
       web_contents, DlpRulesManager::Restriction::kScreenShare));
@@ -922,6 +926,7 @@
   // should not trigger a new warning.
   helper_->ChangeConfidentiality(web_contents, kScreenShareWarned);
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 0);
+  EXPECT_EQ(events_.size(), 2u);
 }
 
 IN_PROC_BROWSER_TEST_F(DlpContentManagerAshBrowserTest,
@@ -958,6 +963,8 @@
 
   helper_->ChangeConfidentiality(web_contents, kScreenShareWarned);
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 1);
+  CheckEvents(DlpRulesManager::Restriction::kScreenShare,
+              DlpRulesManager::Level::kWarn, 1u);
 
   // Hit Esc to "Cancel".
   ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
@@ -969,6 +976,8 @@
   // should not trigger a new warning.
   helper_->ChangeConfidentiality(web_contents, kScreenShareWarned);
   EXPECT_EQ(helper_->ActiveWarningDialogsCount(), 0);
+  CheckEvents(DlpRulesManager::Restriction::kScreenShare,
+              DlpRulesManager::Level::kWarn, 1u);
 }
 
 IN_PROC_BROWSER_TEST_F(DlpContentManagerAshBrowserTest,
@@ -1124,7 +1133,14 @@
   helper_->ChangeConfidentiality(web_contents, kScreenShareWarned);
 
   StartTabScreenShare(web_contents, blink::mojom::MediaStreamRequestResult::OK);
-
+  EXPECT_EQ(events_.size(), 2u);
+  EXPECT_THAT(events_[0],
+              IsDlpPolicyEvent(CreateDlpPolicyEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenShare,
+                  DlpRulesManager::Level::kWarn)));
+  EXPECT_THAT(events_[1],
+              IsDlpPolicyEvent(CreateDlpPolicyWarningProceededEvent(
+                  kSrcPattern, DlpRulesManager::Restriction::kScreenShare)));
   EXPECT_FALSE(display_service_tester.GetNotification(
       kScreenShareBlockedNotificationId));
   EXPECT_TRUE(helper_->HasContentCachedForRestriction(
@@ -1149,7 +1165,8 @@
 
   StartTabScreenShare(
       web_contents, blink::mojom::MediaStreamRequestResult::PERMISSION_DENIED);
-
+  CheckEvents(DlpRulesManager::Restriction::kScreenShare,
+              DlpRulesManager::Level::kWarn, 1u);
   EXPECT_FALSE(display_service_tester.GetNotification(
       kScreenShareBlockedNotificationId));
   EXPECT_FALSE(helper_->HasAnyContentCached());
diff --git a/chrome/browser/ash/web_applications/camera_app/camera_system_web_app_info.cc b/chrome/browser/ash/web_applications/camera_app/camera_system_web_app_info.cc
index 83198bd3..a2cceec 100644
--- a/chrome/browser/ash/web_applications/camera_app/camera_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/camera_app/camera_system_web_app_info.cc
@@ -5,9 +5,9 @@
 #include "chrome/browser/ash/web_applications/camera_app/camera_system_web_app_info.h"
 
 #include "ash/constants/ash_pref_names.h"
+#include "ash/grit/ash_camera_app_resources.h"
 #include "ash/webui/camera_app_ui/resources/strings/grit/ash_camera_app_strings.h"
 #include "ash/webui/camera_app_ui/url_constants.h"
-#include "ash/webui/grit/ash_camera_app_resources.h"
 #include "chrome/browser/ash/web_applications/camera_app/chrome_camera_app_ui_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
diff --git a/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc b/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc
index 941ae050..a38608ea 100644
--- a/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/demo_mode_web_app_info.cc
@@ -5,8 +5,8 @@
 #include "chrome/browser/ash/web_applications/demo_mode_web_app_info.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/grit/ash_demo_mode_app_resources.h"
 #include "ash/webui/demo_mode_app_ui/url_constants.h"
-#include "ash/webui/grit/ash_demo_mode_app_resources.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/ash/web_applications/diagnostics_system_web_app_info.cc b/chrome/browser/ash/web_applications/diagnostics_system_web_app_info.cc
index 8da3c87..c59d19d2 100644
--- a/chrome/browser/ash/web_applications/diagnostics_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/diagnostics_system_web_app_info.cc
@@ -7,8 +7,8 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "ash/grit/ash_diagnostics_app_resources.h"
 #include "ash/webui/diagnostics_ui/url_constants.h"
-#include "ash/webui/grit/ash_diagnostics_app_resources.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/ash/web_applications/eche_app_info.cc b/chrome/browser/ash/web_applications/eche_app_info.cc
index 802506c..ed6e70b 100644
--- a/chrome/browser/ash/web_applications/eche_app_info.cc
+++ b/chrome/browser/ash/web_applications/eche_app_info.cc
@@ -7,8 +7,8 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "ash/grit/ash_eche_bundle_resources.h"
 #include "ash/webui/eche_app_ui/url_constants.h"
-#include "ash/webui/grit/ash_eche_bundle_resources.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
diff --git a/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc b/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc
index 15c9e5f5..74dfc36 100644
--- a/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/firmware_update_system_web_app_info.cc
@@ -7,9 +7,9 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "ash/grit/ash_firmware_update_app_resources.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/webui/firmware_update_ui/url_constants.h"
-#include "ash/webui/grit/ash_firmware_update_app_resources.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/system_web_apps/system_web_app_types.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/ash/web_applications/help_app/help_app_web_app_info.cc b/chrome/browser/ash/web_applications/help_app/help_app_web_app_info.cc
index 8e4290b..bdcacc0 100644
--- a/chrome/browser/ash/web_applications/help_app/help_app_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/help_app/help_app_web_app_info.cc
@@ -7,8 +7,8 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "ash/grit/ash_help_app_resources.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/webui/grit/ash_help_app_resources.h"
 #include "ash/webui/help_app_ui/url_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
diff --git a/chrome/browser/ash/web_applications/media_app/media_web_app_info.cc b/chrome/browser/ash/web_applications/media_app/media_web_app_info.cc
index 66090da..274e735 100644
--- a/chrome/browser/ash/web_applications/media_app/media_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/media_app/media_web_app_info.cc
@@ -9,8 +9,8 @@
 #include <string>
 
 #include "ash/constants/ash_features.h"
+#include "ash/grit/ash_media_app_resources.h"
 #include "ash/style/ash_color_provider.h"
-#include "ash/webui/grit/ash_media_app_resources.h"
 #include "ash/webui/media_app_ui/url_constants.h"
 #include "base/files/file_path.h"
 #include "base/strings/string_split.h"
diff --git a/chrome/browser/ash/web_applications/os_feedback_system_web_app_info.cc b/chrome/browser/ash/web_applications/os_feedback_system_web_app_info.cc
index 22badf3..9dcd295d 100644
--- a/chrome/browser/ash/web_applications/os_feedback_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/os_feedback_system_web_app_info.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
-#include "ash/webui/grit/ash_os_feedback_resources.h"
+#include "ash/grit/ash_os_feedback_resources.h"
 #include "ash/webui/os_feedback_ui/url_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/ash/web_applications/personalization_app/personalization_app_info.cc b/chrome/browser/ash/web_applications/personalization_app/personalization_app_info.cc
index ecf5d285..9035431 100644
--- a/chrome/browser/ash/web_applications/personalization_app/personalization_app_info.cc
+++ b/chrome/browser/ash/web_applications/personalization_app/personalization_app_info.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
-#include "ash/webui/grit/ash_personalization_app_resources.h"
+#include "ash/grit/ash_personalization_app_resources.h"
 #include "ash/webui/personalization_app/personalization_app_url_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/ash/web_applications/print_management_web_app_info.cc b/chrome/browser/ash/web_applications/print_management_web_app_info.cc
index 1d20e54..654ab0a0 100644
--- a/chrome/browser/ash/web_applications/print_management_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/print_management_web_app_info.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/webui/grit/ash_print_management_resources.h"
+#include "ash/grit/ash_print_management_resources.h"
 #include "ash/webui/print_management/url_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
index c9a543f..d09385f 100644
--- a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
@@ -6,7 +6,7 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
-#include "ash/webui/grit/ash_projector_app_trusted_resources.h"
+#include "ash/grit/ash_projector_app_trusted_resources.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
diff --git a/chrome/browser/ash/web_applications/sample_system_web_app_info.cc b/chrome/browser/ash/web_applications/sample_system_web_app_info.cc
index 2431ead..ec5c507 100644
--- a/chrome/browser/ash/web_applications/sample_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/sample_system_web_app_info.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/webui/grit/ash_sample_system_web_app_resources.h"
+#include "ash/grit/ash_sample_system_web_app_resources.h"
 #include "ash/webui/sample_system_web_app_ui/url_constants.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
diff --git a/chrome/browser/ash/web_applications/scanning_system_web_app_info.cc b/chrome/browser/ash/web_applications/scanning_system_web_app_info.cc
index d872b28..e9fef26 100644
--- a/chrome/browser/ash/web_applications/scanning_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/scanning_system_web_app_info.cc
@@ -6,7 +6,7 @@
 
 #include <memory>
 
-#include "ash/webui/grit/ash_scanning_app_resources.h"
+#include "ash/grit/ash_scanning_app_resources.h"
 #include "ash/webui/scanning/url_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/ash/web_applications/shimless_rma_system_web_app_info.cc b/chrome/browser/ash/web_applications/shimless_rma_system_web_app_info.cc
index 9045f7f..f673b82 100644
--- a/chrome/browser/ash/web_applications/shimless_rma_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/shimless_rma_system_web_app_info.cc
@@ -7,8 +7,8 @@
 #include <memory>
 
 #include "ash/constants/ash_features.h"
+#include "ash/grit/ash_shimless_rma_resources.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/webui/grit/ash_shimless_rma_resources.h"
 #include "ash/webui/shimless_rma/url_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/ash/web_applications/shortcut_customization_system_web_app_info.cc b/chrome/browser/ash/web_applications/shortcut_customization_system_web_app_info.cc
index 3b23eb5..c778fec 100644
--- a/chrome/browser/ash/web_applications/shortcut_customization_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/shortcut_customization_system_web_app_info.cc
@@ -6,8 +6,8 @@
 
 #include <memory>
 
+#include "ash/grit/ash_shortcut_customization_app_resources.h"
 #include "ash/strings/grit/ash_strings.h"
-#include "ash/webui/grit/ash_shortcut_customization_app_resources.h"
 #include "ash/webui/shortcut_customization_ui/url_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
diff --git a/chrome/browser/ash/web_applications/terminal_source.cc b/chrome/browser/ash/web_applications/terminal_source.cc
index 02f9772..d93c270 100644
--- a/chrome/browser/ash/web_applications/terminal_source.cc
+++ b/chrome/browser/ash/web_applications/terminal_source.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/common/webui_url_constants.h"
+#include "components/content_settings/core/common/content_settings_types.h"
 #include "components/prefs/pref_service.h"
 #include "net/base/escape.h"
 #include "net/base/mime_util.h"
@@ -95,9 +96,10 @@
   const url::Origin terminal_origin = url::Origin::Create(GURL(source));
   CHECK(!terminal_origin.opaque());
   for (auto permission :
-       {ContentSettingsType::JAVASCRIPT, ContentSettingsType::NOTIFICATIONS,
-        ContentSettingsType::CLIPBOARD_READ_WRITE, ContentSettingsType::COOKIES,
-        ContentSettingsType::IMAGES, ContentSettingsType::SOUND}) {
+       {ContentSettingsType::CLIPBOARD_READ_WRITE, ContentSettingsType::COOKIES,
+        ContentSettingsType::IMAGES, ContentSettingsType::JAVASCRIPT,
+        ContentSettingsType::NOTIFICATIONS, ContentSettingsType::POPUPS,
+        ContentSettingsType::SOUND}) {
     webui_allowlist->RegisterAutoGrantedPermission(terminal_origin, permission);
   }
 }
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
index 38cced5..77575b4c 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.cc
@@ -383,13 +383,20 @@
       std::move(callback).Run(true);
       return;
     }
+
+    ReportWarningEvent(info.restriction_info.url,
+                       DlpRulesManager::Restriction::kScreenShare);
+
+    auto reporting_callback = base::BindOnce(
+        &MaybeReportWarningProceededEvent, info.restriction_info.url,
+        DlpRulesManager::Restriction::kScreenShare, reporting_manager_);
     // base::Unretained(this) is safe here because DlpContentManager is
     // initialized as a singleton that's always available in the system.
     warn_notifier_->ShowDlpScreenShareWarningDialog(
         base::BindOnce(&DlpContentManager::OnDlpWarnDialogReply,
                        base::Unretained(this), info.confidential_contents,
                        DlpRulesManager::Restriction::kScreenShare,
-                       std::move(callback)),
+                       std::move(reporting_callback).Then(std::move(callback))),
         info.confidential_contents, application_title);
     return;
   }
@@ -458,11 +465,15 @@
         screen_share->Pause();
         screen_share->HideNotifications();
       }
+
+      ReportWarningEvent(info.restriction_info.url,
+                         DlpRulesManager::Restriction::kScreenShare);
+
       // base::Unretained(this) is safe here because DlpContentManager is
       // initialized as a singleton that's always available in the system.
       warn_notifier_->ShowDlpScreenShareWarningDialog(
           base::BindOnce(&DlpContentManager::OnDlpScreenShareWarnDialogReply,
-                         base::Unretained(this), info.confidential_contents,
+                         base::Unretained(this), info,
                          screen_share->GetWeakPtr()),
           info.confidential_contents, screen_share->GetApplicationTitle());
       return;
@@ -477,7 +488,7 @@
 }
 
 void DlpContentManager::OnDlpScreenShareWarnDialogReply(
-    const DlpConfidentialContents& confidential_contents,
+    const ConfidentialContentsInfo& info,
     base::WeakPtr<ScreenShareInfo> screen_share,
     bool should_proceed) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -488,8 +499,12 @@
     return;
 
   if (should_proceed) {
+    ReportWarningProceededEvent(info.restriction_info.url,
+                                DlpRulesManager::Restriction::kScreenShare,
+                                reporting_manager_);
+
     screen_share->Resume();
-    for (const auto& content : confidential_contents.GetContents()) {
+    for (const auto& content : info.confidential_contents.GetContents()) {
       user_allowed_contents_cache_.Cache(
           content, DlpRulesManager::Restriction::kScreenShare);
     }
@@ -500,6 +515,8 @@
   }
 }
 
+// TODO(1293512): Consider moving reporting of warning proceeded events inside
+// OnDlpWarnDialogReply().
 void DlpContentManager::OnDlpWarnDialogReply(
     const DlpConfidentialContents& confidential_contents,
     DlpRulesManager::Restriction restriction,
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
index 118f88c7..a7bbb10 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager.h
@@ -241,7 +241,7 @@
   // also saves the |confidential_contents| that were allowed to be shared by
   // the user to avoid future warnings.
   void OnDlpScreenShareWarnDialogReply(
-      const DlpConfidentialContents& confidential_contents,
+      const ConfidentialContentsInfo& info,
       base::WeakPtr<ScreenShareInfo> screen_share,
       bool should_proceed);
 
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_browsertest.cc b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_browsertest.cc
index ae8cb0a..d848691b 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_content_manager_browsertest.cc
+++ b/chrome/browser/chromeos/policy/dlp/dlp_content_manager_browsertest.cc
@@ -456,8 +456,8 @@
       cloned_tab_observer_;
 };
 
-// TODO(crbug.com/1291074): Flaky on ChromeOS.
-#if BUILDFLAG(IS_CHROMEOS)
+// TODO(crbug.com/1291074): Flaky on ChromeOS Lacros.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
 #define MAYBE_PrintingRestricted DISABLED_PrintingRestricted
 #else
 #define MAYBE_PrintingRestricted PrintingRestricted
@@ -507,8 +507,14 @@
       display_service_tester.GetNotification(kPrintBlockedNotificationId));
 }
 
+// TODO(crbug.com/1291074): Flaky on ChromeOS Lacros.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#define MAYBE_PrintingReported DISABLED_PrintingReported
+#else
+#define MAYBE_PrintingReported PrintingReported
+#endif
 IN_PROC_BROWSER_TEST_F(DlpContentManagerReportingBrowserTest,
-                       PrintingReported) {
+                       MAYBE_PrintingReported) {
   SetupDlpRulesManager();
   SetupReportQueue();
   SetAddRecordCheck(
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc
index 9f7e4251..46d224f7 100644
--- a/chrome/browser/devtools/devtools_browsertest.cc
+++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -19,6 +19,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/path_service.h"
 #include "base/run_loop.h"
+#include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
@@ -34,6 +35,7 @@
 #include "chrome/browser/devtools/devtools_window_testing.h"
 #include "chrome/browser/devtools/protocol/browser_handler.h"
 #include "chrome/browser/extensions/chrome_extension_test_notification_observer.h"
+#include "chrome/browser/extensions/component_loader.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -511,19 +513,9 @@
     return GetExtensionByPath(registry->enabled_extensions(), path);
   }
 
-  // Loads a dynamically generated extension populated with a bunch of test
-  // pages. |name| is the extension name to use in the manifest.
-  // |devtools_page|, if non-empty, indicates which test page should be be
-  // listed as a devtools_page in the manifest.  If |devtools_page| is empty, a
-  // non-devtools extension is created instead. |panel_iframe_src| controls the
-  // src= attribute of the <iframe> element in the 'panel.html' test page.
-  const Extension* LoadExtensionForTest(const std::string& name,
-                                        const std::string& devtools_page,
-                                        const std::string& panel_iframe_src) {
-    test_extension_dirs_.push_back(
-        std::make_unique<extensions::TestExtensionDir>());
-    extensions::TestExtensionDir* dir = test_extension_dirs_.back().get();
-
+  std::string BuildExtensionManifest(const std::string& name,
+                                     const std::string& devtools_page = "",
+                                     const std::string& key = "") {
     extensions::DictionaryBuilder manifest;
     manifest.Set("name", name)
         .Set("version", "1")
@@ -539,8 +531,26 @@
     // manifest.
     if (!devtools_page.empty())
       manifest.Set("devtools_page", devtools_page);
+    if (!key.empty())
+      manifest.Set("key", key);
+    return manifest.ToJSON();
+  }
 
-    dir->WriteManifest(manifest.ToJSON());
+  // Builds an extension populated with a bunch of test
+  // pages. |name| is the extension name to use in the manifest.
+  // |devtools_page|, if non-empty, indicates which test page should be be
+  // listed as a devtools_page in the manifest.  If |devtools_page| is empty, a
+  // non-devtools extension is created instead. |panel_iframe_src| controls the
+  // src= attribute of the <iframe> element in the 'panel.html' test page.
+  extensions::TestExtensionDir* BuildExtensionForTest(
+      const std::string& name,
+      const std::string& devtools_page,
+      const std::string& panel_iframe_src) {
+    test_extension_dirs_.push_back(
+        std::make_unique<extensions::TestExtensionDir>());
+    extensions::TestExtensionDir* dir = test_extension_dirs_.back().get();
+
+    dir->WriteManifest(BuildExtensionManifest(name, devtools_page));
 
     GURL http_frame_url =
         embedded_test_server()->GetURL("a.com", "/popup_iframe.html");
@@ -601,8 +611,16 @@
                    "</iframe><iframe src='data:text/html,foo'>"
                    "</iframe><iframe src='" +
                        web_url.spec() + "'></iframe></body></html>");
+    return dir;
+  }
 
-    // Install the extension.
+  // Loads a dynamically generated extension populated with a bunch of test
+  // pages.
+  const Extension* LoadExtensionForTest(const std::string& name,
+                                        const std::string& devtools_page,
+                                        const std::string& panel_iframe_src) {
+    extensions::TestExtensionDir* dir =
+        BuildExtensionForTest(name, devtools_page, panel_iframe_src);
     return LoadExtensionFromPath(dir->UnpackedPath());
   }
 
@@ -1631,9 +1649,49 @@
   RunTest("testConsoleContextNames", kPageWithContentScript);
 }
 
-IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, TestEvaluateOnChromeScheme) {
-  LoadExtension("chrome_scheme");
-  RunTest("waitForTestResultsAsMessage", kArbitraryPage);
+constexpr char kExtensionId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
+constexpr char kPublicKey[] =
+    "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8c4fBSPZ6utYoZ8NiWF/"
+    "DSaimBhihjwgOsskyleFGaurhi3TDClTVSGPxNkgCzrz0wACML7M4aNjpd05qupdbR2d294j"
+    "kDuI7caxEGUucpP7GJRRHnm8Sx+"
+    "y0ury28n8jbN0PnInKKWcxpIXXmNQyC19HBuO3QIeUq9Dqc+7YFQIDAQAB";
+
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, CantInspectChromeScheme) {
+  LoadExtension("can_inspect_url");
+  RunTest("waitForTestResultsAsMessage",
+          base::StrCat({kArbitraryPage, "#chrome://version/"}));
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, CantInspectDevtoolsScheme) {
+  LoadExtension("can_inspect_url");
+  RunTest(
+      "waitForTestResultsAsMessage",
+      base::StrCat({kArbitraryPage,
+                    "#devtools://devtools/bundled/devtools_compatibility.js"}));
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsExtensionTest, CantInspectComponentExtension) {
+  extensions::ExtensionService* extension_service =
+      extensions::ExtensionSystem::Get(browser()->profile())
+          ->extension_service();
+  extensions::ComponentLoader* component_loader =
+      extension_service->component_loader();
+  extensions::ExtensionRegistry* extension_registry =
+      extensions::ExtensionRegistry::Get(browser()->profile());
+
+  extensions::TestExtensionDir* extension_dir = BuildExtensionForTest(
+      "Component extension", "" /* devtools_page */, "" /* panel_iframe_src */);
+  std::string manifest = BuildExtensionManifest(
+      "Component extension", "" /* devtools_page */, kPublicKey);
+  component_loader->set_ignore_allowlist_for_testing(true);
+  std::string extension_id =
+      component_loader->Add(manifest, extension_dir->UnpackedPath());
+  ASSERT_TRUE(extension_registry->enabled_extensions().GetByID(extension_id));
+
+  LoadExtension("can_inspect_url");
+  RunTest("waitForTestResultsAsMessage",
+          base::StrCat({kArbitraryPage, "#chrome-extension://", kExtensionId,
+                        "/simple_test_page.html"}));
 }
 
 // Tests that scripts are not duplicated after Scripts Panel switch.
diff --git a/chrome/browser/devtools/devtools_ui_bindings.cc b/chrome/browser/devtools/devtools_ui_bindings.cc
index 9fd5f39..d13d272 100644
--- a/chrome/browser/devtools/devtools_ui_bindings.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/devtools/devtools_file_watcher.h"
 #include "chrome/browser/devtools/devtools_window.h"
 #include "chrome/browser/devtools/url_constants.h"
+#include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
@@ -76,6 +77,7 @@
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
 #include "extensions/common/constants.h"
 #include "extensions/common/permissions/permissions_data.h"
 #include "google_apis/google_api_keys.h"
@@ -340,10 +342,6 @@
   if (key == "ws" || key == "service-backend")
     return SanitizeEndpoint(value);
 
-  // Only support undocked for old frontends.
-  if (key == "dockSide" && value == "undocked")
-    return value;
-
   if (key == "panel" &&
       (value == "elements" || value == "console" || value == "sources"))
     return value;
@@ -1516,9 +1514,13 @@
     return;
 
   base::ListValue results;
+  base::ListValue component_extension_origins;
   bool have_user_installed_devtools_extensions = false;
   for (const scoped_refptr<const extensions::Extension>& extension :
        registry->enabled_extensions()) {
+    if (extensions::Manifest::IsComponentLocation(extension->location())) {
+      component_extension_origins.Append(extension->origin().Serialize());
+    }
     if (extensions::chrome_manifest_urls::GetDevToolsPage(extension.get())
             .is_empty()) {
       continue;
@@ -1559,6 +1561,8 @@
                               is_developer_mode);
   }
 
+  CallClientMethod("DevToolsAPI", "setOriginsForbiddenForExtensions",
+                   std::move(component_extension_origins));
   CallClientMethod("DevToolsAPI", "addExtensions", std::move(results));
 }
 
diff --git a/chrome/browser/devtools/devtools_ui_bindings_unittest.cc b/chrome/browser/devtools/devtools_ui_bindings_unittest.cc
index 819fb43..af4e929 100644
--- a/chrome/browser/devtools/devtools_ui_bindings_unittest.cc
+++ b/chrome/browser/devtools/devtools_ui_bindings_unittest.cc
@@ -40,10 +40,6 @@
        "?service-backend=ws://localhost:9222/services",
        "devtools://devtools/"
        "?service-backend=ws://localhost:9222/services"},
-      {"devtools://devtools/?dockSide=undocked",
-       "devtools://devtools/?dockSide=undocked"},
-      {"devtools://devtools/?dockSide=dock-to-bottom", "devtools://devtools/"},
-      {"devtools://devtools/?dockSide=bottom", "devtools://devtools/"},
       {"devtools://devtools/?remoteBase="
        "http://example.com:1234/remote-base#hash",
        "devtools://devtools/?remoteBase="
diff --git a/chrome/browser/devtools/devtools_window.cc b/chrome/browser/devtools/devtools_window.cc
index 7cd1cdea..d8a40b4 100644
--- a/chrome/browser/devtools/devtools_window.cc
+++ b/chrome/browser/devtools/devtools_window.cc
@@ -230,22 +230,21 @@
 // static
 GURL DecorateFrontendURL(const GURL& base_url) {
   std::string frontend_url = base_url.spec();
-  std::string url_string(
-      frontend_url +
-      ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
-      "dockSide=undocked");  // TODO(dgozman): remove this support in M38.
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
 
   if (command_line->HasSwitch(switches::kDevToolsFlags)) {
-    url_string += "&" + command_line->GetSwitchValueASCII(
-        switches::kDevToolsFlags);
+    frontend_url = frontend_url +
+                   ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
+                   command_line->GetSwitchValueASCII(switches::kDevToolsFlags);
   }
 
   if (command_line->HasSwitch(switches::kCustomDevtoolsFrontend)) {
-    url_string += "&debugFrontend=true";
+    frontend_url = frontend_url +
+                   ((frontend_url.find("?") == std::string::npos) ? "?" : "&") +
+                   "debugFrontend=true";
   }
 
-  return GURL(url_string);
+  return GURL(frontend_url);
 }
 
 }  // namespace
@@ -1039,7 +1038,6 @@
       close_on_detach_(true),
       // This initialization allows external front-end to work without changes.
       // We don't wait for docking call, but instead immediately show undocked.
-      // Passing "dockSide=undocked" parameter ensures proper UI.
       life_stage_(can_dock ? kNotLoaded : kIsDockedSet),
       action_on_load_(DevToolsToggleAction::NoOp()),
       intercepted_page_beforeunload_(false),
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 07e195f..61e52a3 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -630,6 +630,9 @@
   { key::kWebHidBlockedForUrls,
     prefs::kManagedWebHidBlockedForUrls,
     base::Value::Type::LIST },
+  { key::kBrowserAddPersonEnabled,
+    prefs::kBrowserAddPersonEnabled,
+    base::Value::Type::BOOLEAN },
 #endif  // !BUILDFLAG(IS_ANDROID)
   { key::kDefaultFileHandlingGuardSetting,
     prefs::kManagedDefaultFileHandlingGuardSetting,
@@ -1374,9 +1377,6 @@
   { key::kNativeMessagingUserLevelHosts,
     extensions::pref_names::kNativeMessagingUserLevelHosts,
     base::Value::Type::BOOLEAN },
-  { key::kBrowserAddPersonEnabled,
-    prefs::kBrowserAddPersonEnabled,
-    base::Value::Type::BOOLEAN },
   { key::kPrintPreviewUseSystemDefaultPrinter,
     prefs::kPrintPreviewUseSystemDefaultPrinter,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/reputation/reputation_service.cc b/chrome/browser/reputation/reputation_service.cc
index 515eafd..5c117f2 100644
--- a/chrome/browser/reputation/reputation_service.cc
+++ b/chrome/browser/reputation/reputation_service.cc
@@ -8,9 +8,11 @@
 #include <string>
 #include <utility>
 
+#include "base/feature_list.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/singleton.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/task/post_task.h"
 #include "chrome/browser/lookalikes/lookalike_url_blocking_page.h"
 #include "chrome/browser/lookalikes/lookalike_url_navigation_throttle.h"
 #include "chrome/browser/lookalikes/lookalike_url_service.h"
@@ -31,6 +33,10 @@
 
 namespace {
 
+const base::Feature kHaveReputationServiceGetDomainInfoOnWorkerThread{
+    "kHaveReputationServiceGetDomainInfoOnWorkerThread",
+    base::FEATURE_DISABLED_BY_DEFAULT};
+
 using security_state::SafetyTipStatus;
 
 // This factory helps construct and find the singleton ReputationService linked
@@ -171,12 +177,41 @@
     bool has_delayed_warning,
     ReputationCheckCallback callback,
     const std::vector<DomainInfo>& engaged_sites) {
+  if (base::FeatureList::IsEnabled(
+          kHaveReputationServiceGetDomainInfoOnWorkerThread)) {
+    // Get the DomainInfo for |url| on a worker thread and pass it back to the
+    // rest of the reputation check.
+    base::ThreadPool::PostTaskAndReplyWithResult(
+        FROM_HERE,
+        {base::TaskPriority::USER_BLOCKING,
+         base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
+        base::BindOnce([](const GURL& url) { return GetDomainInfo(url); }, url),
+        base::BindOnce(
+            &ReputationService::GetReputationStatusWithEngagedSitesImpl,
+            weak_factory_.GetWeakPtr(), url, has_delayed_warning,
+            std::move(callback), engaged_sites, base::TimeDelta()));
+    return;
+  }
+
+  base::TimeTicks get_domain_info_start = base::TimeTicks::Now();
+  const DomainInfo domain = GetDomainInfo(url);
+
+  GetReputationStatusWithEngagedSitesImpl(
+      url, has_delayed_warning, std::move(callback), engaged_sites,
+      base::TimeTicks::Now() - get_domain_info_start, domain);
+}
+
+void ReputationService::GetReputationStatusWithEngagedSitesImpl(
+    const GURL& url,
+    bool has_delayed_warning,
+    ReputationCheckCallback callback,
+    const std::vector<DomainInfo>& engaged_sites,
+    base::TimeDelta get_domain_info_on_main_thread_duration,
+    const DomainInfo navigated_domain) {
   base::TimeTicks start = base::TimeTicks::Now();
 
-  const DomainInfo navigated_domain = GetDomainInfo(url);
-
   UMA_HISTOGRAM_TIMES("Security.SafetyTips.GetDomainInfoTime",
-                      base::TimeTicks::Now() - start);
+                      get_domain_info_on_main_thread_duration);
 
   ReputationCheckResult result;
 
@@ -290,5 +325,6 @@
 
   UMA_HISTOGRAM_TIMES(
       "Security.SafetyTips.GetReputationStatusWithEngagedSitesTime",
-      base::TimeTicks::Now() - start);
+      get_domain_info_on_main_thread_duration +
+          (base::TimeTicks::Now() - start));
 }
diff --git a/chrome/browser/reputation/reputation_service.h b/chrome/browser/reputation/reputation_service.h
index 3a239ee3..9da7229 100644
--- a/chrome/browser/reputation/reputation_service.h
+++ b/chrome/browser/reputation/reputation_service.h
@@ -107,6 +107,18 @@
       ReputationCheckCallback callback,
       const std::vector<DomainInfo>& engaged_sites);
 
+  // Callback once we have |navigated_domain|, i.e., the DomainInfo for |url|.
+  // |get_domain_info_on_main_thread_duration| holds the delay that computing
+  // |navigated_domain| incurred on the main thread for metrics purposes. It
+  // will be 0 if |navigated_domain| was computed off of the main thread.
+  void GetReputationStatusWithEngagedSitesImpl(
+      const GURL& url,
+      bool has_delayed_warning,
+      ReputationCheckCallback callback,
+      const std::vector<DomainInfo>& engaged_sites,
+      base::TimeDelta get_domain_info_on_main_thread_duration,
+      const DomainInfo navigated_domain);
+
   // Set of eTLD+1s that we've warned about, and the user has explicitly
   // ignored.  Used to avoid re-warning the user.
   std::set<std::string> warning_dismissed_etld1s_;
diff --git a/chrome/browser/resources/BUILD.gn b/chrome/browser/resources/BUILD.gn
index cafb3bb..1560fedc 100644
--- a/chrome/browser/resources/BUILD.gn
+++ b/chrome/browser/resources/BUILD.gn
@@ -140,7 +140,6 @@
         "internals/user_education:closure_compile",
         "media_router:closure_compile",
         "nearby_internals:closure_compile",
-        "new_tab_page:closure_compile",
         "new_tab_page_instant:closure_compile",
         "ntp4:closure_compile",
         "omnibox:closure_compile",
diff --git a/chrome/browser/resources/new_tab_page/BUILD.gn b/chrome/browser/resources/new_tab_page/BUILD.gn
index 56248706..aa26ef6f 100644
--- a/chrome/browser/resources/new_tab_page/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/BUILD.gn
@@ -4,7 +4,6 @@
 
 import("//chrome/browser/resources/tools/optimize_webui.gni")
 import("//chrome/common/features.gni")
-import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/grit_rule.gni")
 import("//tools/grit/preprocess_if_expr.gni")
 import("//tools/polymer/html_to_js.gni")
@@ -12,132 +11,9 @@
 import("//ui/webui/resources/tools/generate_grd.gni")
 import("//ui/webui/webui_features.gni")
 
-js_type_check("closure_compile") {
-  is_polymer3 = true
-
-  closure_flags = default_closure_args + mojom_js_args + [
-                    "js_module_root=" + rebase_path(".", root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/new_tab_page/foo",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/cart",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/photos",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/drive",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/task_module",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/realbox",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/new_tab_page",
-                            root_build_dir),
-
-                    # TODO(crbug.com/1286395): Remove |hide_warnings_for| when the bug is fixed.
-                    "hide_warnings_for=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/ui/webui/resources/cr_components/most_visited",
-                            root_build_dir),
-                  ]
-
-  deps = [
-    ":app",
-    ":lazy_load",
-    ":new_tab_page",
-    ":new_tab_page_proxy",
-    ":utils",
-    ":window_proxy",
-    "modules:module_descriptor",
-    "modules:module_descriptors",
-    "modules:module_registry",
-    "modules:modules",
-    "modules/dummy_v2:foo_proxy",
-  ]
-}
-
-js_library("new_tab_page") {
-  deps = [
-    ":app",
-    ":background_manager",
-    ":utils",
-    ":window_proxy",
-    "modules:info_dialog",
-    "modules:module_descriptor",
-    "modules:module_header",
-    "modules:module_registry",
-    "modules:modules",
-    "//ui/webui/resources/js/browser_command:browser_command_proxy",
-  ]
-}
-
-js_library("lazy_load") {
-  deps = [ "modules/dummy_v2:module" ]
-}
-
-js_library("window_proxy") {
-}
-
-js_library("new_tab_page_proxy") {
-  deps = [ "//chrome/browser/ui/webui/new_tab_page:mojo_bindings_webui_js" ]
-}
-
-js_library("app") {
-  deps = [
-    ":background_manager",
-    ":customize_dialog_types",
-    ":i18n_setup",
-    ":metrics_utils",
-    ":new_tab_page_proxy",
-    ":window_proxy",
-    "modules:module_registry",
-    "//chrome/browser/ui/webui/new_tab_page:mojo_bindings_webui_js",
-    "//skia/public/mojom:mojom_webui_js",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
-    "//ui/webui/resources/js:event_tracker.m",
-    "//ui/webui/resources/js/browser_command:browser_command_proxy",
-    "//ui/webui/resources/js/browser_command:mojo_bindings_webui_js",
-    "//ui/webui/resources/js/cr/ui:focus_outline_manager.m",
-  ]
-}
-
-js_library("customize_dialog_types") {
-}
-
-js_library("utils") {
-}
-
-js_library("metrics_utils") {
-  deps = [ ":i18n_setup" ]
-  externs_list = [ "//third_party/closure_compiler/externs/metrics_private.js" ]
-}
-
-js_library("background_manager") {
-  deps = [
-    ":utils",
-    ":window_proxy",
-    "//chrome/browser/ui/webui/new_tab_page:mojo_bindings_webui_js",
-    "//skia/public/mojom:mojom_webui_js",
-    "//ui/webui/resources/js:event_tracker.m",
-    "//ui/webui/resources/js:promise_resolver.m",
-  ]
-}
-
-js_library("i18n_setup") {
-  deps = [
-    "//ui/webui/resources/js:i18n_behavior.m",
-    "//ui/webui/resources/js:load_time_data.m",
-  ]
-}
-
 html_to_js("web_components_local") {
   js_files = [
-    "app.js",
+    "app.ts",
     "customize_backgrounds.ts",
     "customize_dialog.ts",
     "customize_shortcuts.ts",
@@ -171,16 +47,15 @@
   in_folder = "./"
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = [
-    "background_manager.js",
-    "window_proxy.js",
-    "new_tab_page_proxy.js",
-    "lazy_load.js",
-    "customize_dialog_types.js",
-    "new_tab_page.js",
-    "utils.js",
-    "utils_ts.ts",
-    "metrics_utils.js",
-    "i18n_setup.js",
+    "background_manager.ts",
+    "window_proxy.ts",
+    "new_tab_page_proxy.ts",
+    "lazy_load.ts",
+    "customize_dialog_types.ts",
+    "new_tab_page.ts",
+    "utils.ts",
+    "metrics_utils.ts",
+    "i18n_setup.ts",
   ]
 }
 
@@ -190,7 +65,7 @@
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = [
-    "app.js",
+    "app.ts",
     "middle_slot_promo.ts",
     "customize_dialog.ts",
     "voice_search_overlay.ts",
@@ -372,19 +247,19 @@
   out_dir = "$target_gen_dir/tsc"
   tsconfig_base = "tsconfig_base.json"
   in_files = [
-               "app.js",
-               "background_manager.js",
+               "app.ts",
+               "background_manager.ts",
                "customize_backgrounds.ts",
                "customize_dialog.ts",
-               "customize_dialog_types.js",
+               "customize_dialog_types.ts",
                "customize_modules.ts",
                "customize_shortcuts.ts",
                "doodle_share_dialog.ts",
-               "i18n_setup.js",
+               "i18n_setup.ts",
                "iframe.ts",
-               "lazy_load.js",
+               "lazy_load.ts",
                "logo.ts",
-               "metrics_utils.js",
+               "metrics_utils.ts",
                "middle_slot_promo.ts",
                "mini_page.ts",
                "modules/cart/chrome_cart_proxy.ts",
@@ -393,36 +268,35 @@
                "modules/drive/drive_module_proxy.ts",
                "modules/drive/module.ts",
                "modules/drive_v2/module.ts",
-               "modules/info_dialog.js",
-               "modules/module_descriptor.js",
-               "modules/module_descriptors.js",
-               "modules/module_header.js",
-               "modules/module_registry.js",
-               "modules/modules.js",
-               "modules/module_wrapper.js",
+               "modules/info_dialog.ts",
+               "modules/module_descriptor.ts",
+               "modules/module_descriptors.ts",
+               "modules/module_header.ts",
+               "modules/module_registry.ts",
+               "modules/modules.ts",
+               "modules/module_wrapper.ts",
                "modules/photos/module.ts",
                "modules/photos/photos_module_proxy.ts",
                "modules/recipes_v2/module.ts",
                "modules/task_module/module.ts",
                "modules/task_module/task_module_handler_proxy.ts",
-               "new_tab_page.js",
-               "new_tab_page_proxy.js",
+               "new_tab_page.ts",
+               "new_tab_page_proxy.ts",
                "realbox/realbox_action.ts",
                "realbox/realbox_browser_proxy.ts",
                "realbox/realbox_dropdown.ts",
                "realbox/realbox_icon.ts",
                "realbox/realbox.ts",
                "realbox/realbox_match.ts",
-               "utils.js",
-               "utils_ts.ts",
+               "utils.ts",
                "voice_search_overlay.ts",
-               "window_proxy.js",
+               "window_proxy.ts",
              ] + mojo_js_files
 
   if (!is_official_build) {
     in_files += [
-      "modules/dummy_v2/foo_proxy.js",
-      "modules/dummy_v2/module.js",
+      "modules/dummy_v2/foo_proxy.ts",
+      "modules/dummy_v2/module.ts",
     ]
   }
 
diff --git a/chrome/browser/resources/new_tab_page/app.js b/chrome/browser/resources/new_tab_page/app.ts
similarity index 75%
rename from chrome/browser/resources/new_tab_page/app.js
rename to chrome/browser/resources/new_tab_page/app.ts
index 23cd232..1ece5a1 100644
--- a/chrome/browser/resources/new_tab_page/app.js
+++ b/chrome/browser/resources/new_tab_page/app.ts
@@ -16,11 +16,12 @@
 import {FocusOutlineManager} from 'chrome://resources/js/cr/ui/focus_outline_manager.m.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.m.js';
 import {SkColor} from 'chrome://resources/mojo/skia/public/mojom/skcolor.mojom-webui.js';
-import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {BackgroundManager} from './background_manager.js';
 import {CustomizeDialogPage} from './customize_dialog_types.js';
-import {I18nBehavior, loadTimeData} from './i18n_setup.js';
+import {I18nMixin, loadTimeData} from './i18n_setup.js';
+import {IframeElement} from './iframe.js';
 import {recordLoadDuration} from './metrics_utils.js';
 import {ModuleRegistry} from './modules/module_registry.js';
 import {BackgroundImage, PageCallbackRouter, PageHandlerRemote, Theme} from './new_tab_page.mojom-webui.js';
@@ -29,37 +30,38 @@
 import {Action as VoiceAction, recordVoiceAction} from './voice_search_overlay.js';
 import {WindowProxy} from './window_proxy.js';
 
-/**
- * @typedef {{
- *   commandId: Command<number>,
- *   clickInfo: ClickInfo
- * }}
- */
-let CommandData;
+
+type ExecutePromoBrowserCommandData = {
+  commandId: Command,
+  clickInfo: ClickInfo,
+};
+
+type CanShowPromoWithBrowserCommandData = {
+  frameType: string,
+  messageType: string,
+  commandId: Command,
+};
 
 /**
  * Elements on the NTP. This enum must match the numbering for NTPElement in
  * enums.xml. These values are persisted to logs. Entries should not be
  * renumbered, removed or reused.
- * @enum {number}
  */
-export const NtpElement = {
-  kOther: 0,
-  kBackground: 1,
-  kOneGoogleBar: 2,
-  kLogo: 3,
-  kRealbox: 4,
-  kMostVisited: 5,
-  kMiddleSlotPromo: 6,
-  kModule: 7,
-  kCustomize: 8,
-};
+export enum NtpElement {
+  kOther = 0,
+  kBackground = 1,
+  kOneGoogleBar = 2,
+  kLogo = 3,
+  kRealbox = 4,
+  kMostVisited = 5,
+  kMiddleSlotPromo = 6,
+  kModule = 7,
+  kCustomize = 8,
+}
 
-/** @const {string} */
-const CUSTOMIZE_URL_PARAM = 'customize';
+const CUSTOMIZE_URL_PARAM: string = 'customize';
 
-/** @param {NtpElement} element */
-function recordClick(element) {
+function recordClick(element: NtpElement) {
   chrome.metricsPrivate.recordEnumerationValue(
       'NewTabPage.Click', element, Object.keys(NtpElement).length);
 }
@@ -72,23 +74,20 @@
   document.body.appendChild(script);
 }
 
-/**
- * @polymer
- * @extends {PolymerElement}
- */
-class AppElement extends mixinBehaviors
-([I18nBehavior], PolymerElement) {
+interface AppElement {
+  $: {
+    oneGoogleBarClipPath: HTMLElement,
+  };
+}
+
+class AppElement extends I18nMixin
+(PolymerElement) {
   static get is() {
     return 'ntp-app';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
-      /** @private */
       oneGoogleBarIframePath_: {
         type: String,
         value: () => {
@@ -100,13 +99,11 @@
         },
       },
 
-      /** @private */
       oneGoogleBarLoaded_: {
         type: Boolean,
         observer: 'notifyOneGoogleBarDarkThemeEnabledChange_',
       },
 
-      /** @private */
       oneGoogleBarDarkThemeEnabled_: {
         type: Boolean,
         computed: `computeOneGoogleBarDarkThemeEnabled_(oneGoogleBarLoaded_,
@@ -114,30 +111,25 @@
         observer: 'notifyOneGoogleBarDarkThemeEnabledChange_',
       },
 
-      /** @private {!Theme} */
       theme_: {
         observer: 'onThemeChange_',
         type: Object,
       },
 
-      /** @private */
       showCustomizeDialog_: {
         type: Boolean,
         value: () =>
             WindowProxy.getInstance().url.searchParams.has(CUSTOMIZE_URL_PARAM),
       },
 
-      /** @private {?string} */
       selectedCustomizeDialogPage_: {
         type: String,
         value: () =>
             WindowProxy.getInstance().url.searchParams.get(CUSTOMIZE_URL_PARAM),
       },
 
-      /** @private */
       showVoiceSearchOverlay_: Boolean,
 
-      /** @private */
       showBackgroundImage_: {
         computed: 'computeShowBackgroundImage_(theme_)',
         observer: 'onShowBackgroundImageChange_',
@@ -145,99 +137,83 @@
         type: Boolean,
       },
 
-      /** @private */
       backgroundImageAttribution1_: {
         type: String,
         computed: `computeBackgroundImageAttribution1_(theme_)`,
       },
 
-      /** @private */
       backgroundImageAttribution2_: {
         type: String,
         computed: `computeBackgroundImageAttribution2_(theme_)`,
       },
 
-      /** @private */
       backgroundImageAttributionUrl_: {
         type: String,
         computed: `computeBackgroundImageAttributionUrl_(theme_)`,
       },
 
-      /** @private {SkColor} */
       backgroundColor_: {
         computed: 'computeBackgroundColor_(showBackgroundImage_, theme_)',
         type: Object,
       },
 
-      /** @private */
       logoColor_: {
         type: String,
         computed: 'computeLogoColor_(theme_)',
       },
 
-      /** @private */
       singleColoredLogo_: {
         computed: 'computeSingleColoredLogo_(theme_)',
         type: Boolean,
       },
 
-      /** @private */
       realboxShown_: {
         type: Boolean,
         computed: 'computeRealboxShown_(theme_)',
       },
 
-      /** @private */
       logoEnabled_: {
         type: Boolean,
         value: () => loadTimeData.getBoolean('logoEnabled'),
       },
 
-      /** @private */
       oneGoogleBarEnabled_: {
         type: Boolean,
         value: () => loadTimeData.getBoolean('oneGoogleBarEnabled'),
       },
 
-      /** @private */
       shortcutsEnabled_: {
         type: Boolean,
         value: () => loadTimeData.getBoolean('shortcutsEnabled'),
       },
 
-      /** @private */
       modulesRedesignedLayoutEnabled_: {
         type: Boolean,
         value: () => loadTimeData.getBoolean('modulesRedesignedLayoutEnabled'),
         reflectToAttribute: true,
       },
 
-      /** @private */
       middleSlotPromoEnabled_: {
         type: Boolean,
         value: () => loadTimeData.getBoolean('middleSlotPromoEnabled'),
       },
 
-      /** @private */
       modulesEnabled_: {
         type: Boolean,
         value: () => loadTimeData.getBoolean('modulesEnabled'),
       },
 
-      /** @private */
       modulesRedesignedEnabled_: {
         type: Boolean,
         value: () => loadTimeData.getBoolean('modulesRedesignedEnabled'),
         reflectToAttribute: true,
       },
 
-      /** @private */
       middleSlotPromoLoaded_: {
         type: Boolean,
         value: false,
       },
 
-      /** @private */
       modulesLoaded_: {
         type: Boolean,
         value: false,
@@ -247,7 +223,6 @@
        * In order to avoid flicker, the promo and modules are hidden until both
        * are loaded. If modules are disabled, the promo is shown as soon as it
        * is loaded.
-       * @private
        */
       promoAndModulesLoaded_: {
         type: Boolean,
@@ -259,36 +234,64 @@
       /**
        * If true, renders additional elements that were not deemed crucial to
        * to show up immediately on load.
-       * @private
        */
       lazyRender_: Boolean,
     };
   }
 
+  private oneGoogleBarIframePath_: string;
+  private oneGoogleBarLoaded_: boolean;
+  private oneGoogleBarDarkThemeEnabled_: boolean;
+  private theme_: Theme;
+  private showCustomizeDialog_: boolean;
+  private selectedCustomizeDialogPage_: string|null;
+  private showVoiceSearchOverlay_: boolean;
+  private showBackgroundImage_: boolean;
+  private backgroundImageAttribution1_: string;
+  private backgroundImageAttribution2_: string;
+  private backgroundImageAttributionUrl_: string;
+  private backgroundColor_: SkColor;
+  private logoColor_: string;
+  private singleColoredLogo_: boolean;
+  private realboxShown_: boolean;
+  private logoEnabled_: boolean;
+  private oneGoogleBarEnabled_: boolean;
+  private shortcutsEnabled_: boolean;
+  private modulesRedesignedLayoutEnabled_: boolean;
+  private middleSlotPromoEnabled_: boolean;
+  private modulesEnabled_: boolean;
+  private modulesRedesignedEnabled_: boolean;
+  private middleSlotPromoLoaded_: boolean;
+  private modulesLoaded_: boolean;
+  private promoAndModulesLoaded_: boolean;
+  private lazyRender_: boolean;
+
+  private callbackRouter_: PageCallbackRouter;
+  private pageHandler_: PageHandlerRemote;
+  private backgroundManager_: BackgroundManager;
+  private setThemeListenerId_: number|null = null;
+  private eventTracker_: EventTracker = new EventTracker();
+  private shouldPrintPerformance_: boolean;
+  private backgroundImageLoadStartEpoch_: number;
+  private backgroundImageLoadStart_: number = 0;
+
+  // Suppress TypeScript's error TS2376 to intentionally allow calling
+  // performance.mark() before calling super().
+  // @ts-ignore:next-line
   constructor() {
     performance.mark('app-creation-start');
     super();
-    /** @private {!PageCallbackRouter} */
     this.callbackRouter_ = NewTabPageProxy.getInstance().callbackRouter;
-    /** @private {!PageHandlerRemote} */
     this.pageHandler_ = NewTabPageProxy.getInstance().handler;
-    /** @private {!BackgroundManager} */
     this.backgroundManager_ = BackgroundManager.getInstance();
-    /** @private {?number} */
-    this.setThemeListenerId_ = null;
-    /** @private {!EventTracker} */
-    this.eventTracker_ = new EventTracker();
-    /** @private {boolean} */
     this.shouldPrintPerformance_ =
         new URLSearchParams(location.search).has('print_perf');
+
     /**
      * Initialized with the start of the performance timeline in case the
      * background image load is not triggered by JS.
-     * @private {number}
      */
     this.backgroundImageLoadStartEpoch_ = performance.timeOrigin;
-    /** @private {number} */
-    this.backgroundImageLoadStart_ = 0;
 
     chrome.metricsPrivate.recordValue(
         {
@@ -301,16 +304,14 @@
         Math.floor(document.documentElement.clientHeight));
   }
 
-  /** @override */
   connectedCallback() {
     super.connectedCallback();
     this.setThemeListenerId_ =
-        this.callbackRouter_.setTheme.addListener(theme => {
+        this.callbackRouter_.setTheme.addListener((theme: Theme) => {
           performance.measure('theme-set');
           this.theme_ = theme;
         });
-    this.eventTracker_.add(window, 'message', (event) => {
-      /** @type {!Object} */
+    this.eventTracker_.add(window, 'message', (event: MessageEvent) => {
       const data = event.data;
       // Something in OneGoogleBar is sending a message that is received here.
       // Need to ignore it.
@@ -321,9 +322,9 @@
         this.handleOneGoogleBarMessage_(event);
       }
     });
-    this.eventTracker_.add(window, 'keydown', e => this.onWindowKeydown_(e));
+    this.eventTracker_.add(window, 'keydown', this.onWindowKeydown_.bind(this));
     this.eventTracker_.add(
-        window, 'click', e => this.onWindowClick_(e), /*capture=*/ true);
+        window, 'click', this.onWindowClick_.bind(this), /*capture=*/ true);
     if (this.shouldPrintPerformance_) {
       // It is possible that the background image has already loaded by now.
       // If it has, we request it to re-send the load time so that we can
@@ -345,14 +346,12 @@
     FocusOutlineManager.forDocument(document);
   }
 
-  /** @override */
   disconnectedCallback() {
     super.disconnectedCallback();
-    this.callbackRouter_.removeListener(assert(this.setThemeListenerId_));
+    this.callbackRouter_.removeListener(this.setThemeListenerId_!);
     this.eventTracker_.removeAll();
   }
 
-  /** @override */
   ready() {
     super.ready();
     this.pageHandler_.onAppRendered(WindowProxy.getInstance().now());
@@ -365,76 +364,50 @@
     performance.measure('app-creation', 'app-creation-start');
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  computeOneGoogleBarDarkThemeEnabled_() {
+  private computeOneGoogleBarDarkThemeEnabled_(): boolean {
     return this.theme_ && this.theme_.isDark;
   }
 
-  /** @private */
-  notifyOneGoogleBarDarkThemeEnabledChange_() {
+  private notifyOneGoogleBarDarkThemeEnabledChange_() {
     if (this.oneGoogleBarLoaded_) {
-      $$(this, '#oneGoogleBar').postMessage({
+      $$<IframeElement>(this, '#oneGoogleBar')!.postMessage({
         type: 'enableDarkTheme',
         enabled: this.oneGoogleBarDarkThemeEnabled_,
       });
     }
   }
 
-  /**
-   * @return {string}
-   * @private
-   */
-  computeBackgroundImageAttribution1_() {
+  private computeBackgroundImageAttribution1_(): string {
     return this.theme_ && this.theme_.backgroundImageAttribution1 || '';
   }
 
-  /**
-   * @return {string}
-   * @private
-   */
-  computeBackgroundImageAttribution2_() {
+  private computeBackgroundImageAttribution2_(): string {
     return this.theme_ && this.theme_.backgroundImageAttribution2 || '';
   }
 
-  /**
-   * @return {string}
-   * @private
-   */
-  computeBackgroundImageAttributionUrl_() {
+  private computeBackgroundImageAttributionUrl_(): string {
     return this.theme_ && this.theme_.backgroundImageAttributionUrl ?
         this.theme_.backgroundImageAttributionUrl.url :
         '';
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  computeRealboxShown_() {
+  private computeRealboxShown_(): boolean {
     // If realbox is to match the Omnibox's theme, keep it hidden until the
     // theme arrives. Otherwise mismatching colors will cause flicker.
     return !loadTimeData.getBoolean('realboxMatchOmniboxTheme') ||
         !!this.theme_;
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  computePromoAndModulesLoaded_() {
+  private computePromoAndModulesLoaded_(): boolean {
     return (!loadTimeData.getBoolean('middleSlotPromoEnabled') ||
             this.middleSlotPromoLoaded_) &&
         (!loadTimeData.getBoolean('modulesEnabled') || this.modulesLoaded_);
   }
 
-  /** @private */
-  async onLazyRendered_() {
+  private async onLazyRendered_() {
     // Integration tests use this attribute to determine when lazy load has
     // completed.
-    document.documentElement.setAttribute('lazy-loaded', true);
+    document.documentElement.setAttribute('lazy-loaded', String(true));
     // Instantiate modules even if |modulesEnabled| is false to counterfactually
     // trigger a HaTS survey in a potential control group.
     if (!loadTimeData.getBoolean('modulesLoadEnabled') ||
@@ -448,44 +421,30 @@
     }
   }
 
-  /**
-   * TODO(crbug.com/1273590): Remove suppression when app.js is converted to
-   * TypeScript.
-   * @suppress {checkTypes}
-   * @private
-   */
-  onOpenVoiceSearch_() {
+  private onOpenVoiceSearch_() {
     this.showVoiceSearchOverlay_ = true;
     recordVoiceAction(VoiceAction.kActivateSearchBox);
   }
 
-  /** @private */
-  onCustomizeClick_() {
+  private onCustomizeClick_() {
     this.showCustomizeDialog_ = true;
   }
 
-  /** @private */
-  onCustomizeDialogClose_() {
+  private onCustomizeDialogClose_() {
     this.showCustomizeDialog_ = false;
     // Let customize dialog decide what page to show on next open.
     this.selectedCustomizeDialogPage_ = null;
   }
 
-  /** @private */
-  onVoiceSearchOverlayClose_() {
+  private onVoiceSearchOverlayClose_() {
     this.showVoiceSearchOverlay_ = false;
   }
 
   /**
    * Handles <CTRL> + <SHIFT> + <.> (also <CMD> + <SHIFT> + <.> on mac) to open
    * voice search.
-   * @param {KeyboardEvent} e
-   * @private
-   * TODO(crbug.com/1273590): Remove suppression when app.js is converted to
-   * TypeScript.
-   * @suppress {checkTypes}
    */
-  onWindowKeydown_(e) {
+  private onWindowKeydown_(e: KeyboardEvent) {
     let ctrlKeyPressed = e.ctrlKey;
     // <if expr="is_macosx">
     ctrlKeyPressed = ctrlKeyPressed || e.metaKey;
@@ -496,38 +455,26 @@
     }
   }
 
-  /**
-   * @param {SkColor} skColor
-   * @return {string}
-   * @private
-   */
-  rgbaOrInherit_(skColor) {
+  private rgbaOrInherit_(skColor: SkColor|null): string {
     return skColor ? skColorToRgba(skColor) : 'inherit';
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  computeShowBackgroundImage_() {
+  private computeShowBackgroundImage_(): boolean {
     return !!this.theme_ && !!this.theme_.backgroundImage;
   }
 
-  /** @private */
-  onShowBackgroundImageChange_() {
+  private onShowBackgroundImageChange_() {
     this.backgroundManager_.setShowBackgroundImage(this.showBackgroundImage_);
   }
 
-  /** @private */
-  onThemeChange_() {
+  private onThemeChange_() {
     if (this.theme_) {
       this.backgroundManager_.setBackgroundColor(this.theme_.backgroundColor);
     }
     this.updateBackgroundImagePath_();
   }
 
-  /** @private */
-  onPromoAndModulesLoadedChange_() {
+  private onPromoAndModulesLoadedChange_() {
     if (this.promoAndModulesLoaded_ &&
         loadTimeData.getBoolean('modulesEnabled')) {
       recordLoadDuration(
@@ -542,10 +489,8 @@
    * The ntp-untrusted-iframe |path| is set directly. When using a data binding
    * instead, the quick updates to the |path| result in iframe loading an error
    * page.
-   * @private
    */
-  updateBackgroundImagePath_() {
-    /** @type {BackgroundImage|undefined} */
+  private updateBackgroundImagePath_() {
     const backgroundImage = this.theme_ && this.theme_.backgroundImage;
 
     if (backgroundImage) {
@@ -553,32 +498,20 @@
     }
   }
 
-  /**
-   * @return {SkColor}
-   * @private
-   */
-  computeBackgroundColor_() {
+  private computeBackgroundColor_(): SkColor|null {
     if (this.showBackgroundImage_) {
       return null;
     }
     return this.theme_ && this.theme_.backgroundColor;
   }
 
-  /**
-   * @return {SkColor}
-   * @private
-   */
-  computeLogoColor_() {
+  private computeLogoColor_(): SkColor|null {
     return this.theme_ &&
         (this.theme_.logoColor ||
          (this.theme_.isDark ? hexColorToSkColor('#ffffff') : null));
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  computeSingleColoredLogo_() {
+  private computeSingleColoredLogo_(): boolean {
     return this.theme_ && (!!this.theme_.logoColor || this.theme_.isDark);
   }
 
@@ -588,12 +521,13 @@
    * command can be shown back to the source promo frame. |commandSource| and
    * |commandOrigin| are used only to send the response back to the source promo
    * frame and should not be used for anything else.
-   * @param {Object} messageData Data received from the source promo frame.
-   * @param {Window} commandSource Source promo frame.
-   * @param {string} commandOrigin Origin of the source promo frame.
-   * @private
+   * @param  messageData Data received from the source promo frame.
+   * @param commandSource Source promo frame.
+   * @param commandOrigin Origin of the source promo frame.
    */
-  canShowPromoWithBrowserCommand_(messageData, commandSource, commandOrigin) {
+  private canShowPromoWithBrowserCommand_(
+      messageData: CanShowPromoWithBrowserCommandData, commandSource: Window,
+      commandOrigin: string) {
     // Make sure we don't send unsupported commands to the browser.
     /** @type {!Command} */
     const commandId = Object.values(Command).includes(messageData.commandId) ?
@@ -602,8 +536,10 @@
 
     BrowserCommandProxy.getInstance().handler.canExecuteCommand(commandId).then(
         ({canExecute}) => {
-          const response = {messageType: messageData.messageType};
-          response[messageData.commandId] = canExecute;
+          const response = {
+            messageType: messageData.messageType,
+            [messageData.commandId]: canExecute,
+          };
           commandSource.postMessage(response, commandOrigin);
         });
   }
@@ -614,23 +550,20 @@
    * status response back to the source promo frame. |commandSource| and
    * |commandOrigin| are used only to send the execution status response back to
    * the source promo frame and should not be used for anything else.
-   * @param {!CommandData} commandData Command and mouse click info.
-   * @param {Window} commandSource Source promo frame.
-   * @param {string} commandOrigin Origin of the source promo frame.
-   * @private
+   * @param commandData Command and mouse click info.
+   * @param commandSource Source promo frame.
+   * @param commandOrigin Origin of the source promo frame.
    */
-  executePromoBrowserCommand_(commandData, commandSource, commandOrigin) {
+  private executePromoBrowserCommand_(
+      commandData: ExecutePromoBrowserCommandData, commandSource: Window,
+      commandOrigin: string) {
     // Make sure we don't send unsupported commands to the browser.
-    /** @type {!Command} */
     const commandId = Object.values(Command).includes(commandData.commandId) ?
         commandData.commandId :
         Command.kUnknownCommand;
 
     BrowserCommandProxy.getInstance()
-        .handler
-        .executeCommand(
-            commandId,
-            /** @type {!ClickInfo} */ (commandData.clickInfo))
+        .handler.executeCommand(commandId, commandData.clickInfo)
         .then(({commandExecuted}) => {
           commandSource.postMessage(commandExecuted, commandOrigin);
         });
@@ -642,14 +575,11 @@
    *
    * 'overlaysUpdated' message includes the updated array of overlay rects that
    * are shown.
-   * @param {!MessageEvent} event
-   * @private
    */
-  handleOneGoogleBarMessage_(event) {
-    /** @type {!Object} */
+  private handleOneGoogleBarMessage_(event: MessageEvent) {
     const data = event.data;
     if (data.messageType === 'loaded') {
-      const oneGoogleBar = $$(this, '#oneGoogleBar');
+      const oneGoogleBar = $$<IframeElement>(this, '#oneGoogleBar')!;
       oneGoogleBar.style.clipPath = 'url(#oneGoogleBarClipPath)';
       oneGoogleBar.style.zIndex = '1000';
       this.oneGoogleBarLoaded_ = true;
@@ -658,45 +588,43 @@
       this.$.oneGoogleBarClipPath.querySelectorAll('rect').forEach(el => {
         el.remove();
       });
-      const overlayRects = /** @type {!Array<!DOMRect>} */ (data.data);
+      const overlayRects = data.data as DOMRect[];
       overlayRects.forEach(({x, y, width, height}) => {
         const rectElement =
             document.createElementNS('http://www.w3.org/2000/svg', 'rect');
         // Add 8px around every rect to ensure shadows are not cutoff.
-        rectElement.setAttribute('x', x - 8);
-        rectElement.setAttribute('y', y - 8);
-        rectElement.setAttribute('width', width + 16);
-        rectElement.setAttribute('height', height + 16);
+        rectElement.setAttribute('x', `${x - 8}`);
+        rectElement.setAttribute('y', `${y - 8}`);
+        rectElement.setAttribute('width', `${width + 16}`);
+        rectElement.setAttribute('height', `${height + 16}`);
         this.$.oneGoogleBarClipPath.appendChild(rectElement);
       });
     } else if (data.messageType === 'can-show-promo-with-browser-command') {
-      this.canShowPromoWithBrowserCommand_(data, event.source, event.origin);
+      this.canShowPromoWithBrowserCommand_(
+          data, event.source as Window, event.origin);
     } else if (data.messageType === 'execute-browser-command') {
       this.executePromoBrowserCommand_(
-          /** @type {!CommandData} */ (data.data), event.source, event.origin);
+          data.data, event.source as Window, event.origin);
     } else if (data.messageType === 'click') {
       recordClick(NtpElement.kOneGoogleBar);
     }
   }
 
-  /** @private */
-  onMiddleSlotPromoLoaded_() {
+  private onMiddleSlotPromoLoaded_() {
     this.middleSlotPromoLoaded_ = true;
   }
 
-  /** @private */
-  onModulesLoaded_() {
+  private onModulesLoaded_() {
     this.modulesLoaded_ = true;
   }
 
-  /** @private */
-  onCustomizeModule_() {
+  private onCustomizeModule_() {
     this.showCustomizeDialog_ = true;
     this.selectedCustomizeDialogPage_ = CustomizeDialogPage.MODULES;
   }
 
-  /** @private */
-  printPerformanceDatum_(name, time, auxTime = 0) {
+  private printPerformanceDatum_(
+      name: string, time: number, auxTime: number = 0) {
     if (!this.shouldPrintPerformance_) {
       return;
     }
@@ -710,14 +638,13 @@
   /**
    * Prints performance measurements to the console. Also, installs  performance
    * observer to continuously print performance measurements after.
-   * @private
    */
-  printPerformance_() {
+  private printPerformance_() {
     if (!this.shouldPrintPerformance_) {
       return;
     }
     const entryTypes = ['paint', 'measure'];
-    const log = (entry) => {
+    const log = (entry: PerformanceEntry) => {
       this.printPerformanceDatum_(
           entry.name, entry.duration ? entry.duration : entry.startTime,
           entry.duration && entry.startTime ? entry.startTime : 0);
@@ -736,11 +663,7 @@
     });
   }
 
-  /**
-   * @param {Event} e
-   * @private
-   */
-  onWindowClick_(e) {
+  private onWindowClick_(e: Event) {
     if (e.composedPath() && e.composedPath()[0] === $$(this, '#content')) {
       recordClick(NtpElement.kBackground);
       return;
@@ -770,6 +693,10 @@
     }
     recordClick(NtpElement.kOther);
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(AppElement.is, AppElement);
diff --git a/chrome/browser/resources/new_tab_page/background_manager.js b/chrome/browser/resources/new_tab_page/background_manager.ts
similarity index 76%
rename from chrome/browser/resources/new_tab_page/background_manager.js
rename to chrome/browser/resources/new_tab_page/background_manager.ts
index c5274656..f308550 100644
--- a/chrome/browser/resources/new_tab_page/background_manager.js
+++ b/chrome/browser/resources/new_tab_page/background_manager.ts
@@ -26,13 +26,11 @@
  * |PromiseResolver| that resolves to the captured load time.
  */
 class LoadTimeResolver {
-  /** @param {string} url */
-  constructor(url) {
-    /** @private {!PromiseResolver<number>} */
-    this.resolver_ = new PromiseResolver();
-    /** @private {!EventTracker} */
-    this.eventTracker_ = new EventTracker();
-    this.eventTracker_.add(window, 'message', ({data}) => {
+  private resolver_: PromiseResolver<number> = new PromiseResolver();
+  private eventTracker_: EventTracker = new EventTracker();
+
+  constructor(url: string) {
+    this.eventTracker_.add(window, 'message', ({data}: MessageEvent) => {
       if (data.frameType === 'background-image' &&
           data.messageType === 'loaded' && url === data.url) {
         this.resolve_(data.time);
@@ -40,8 +38,7 @@
     });
   }
 
-  /** @return {!Promise<number>} */
-  get promise() {
+  get promise(): Promise<number> {
     return this.resolver_.promise;
   }
 
@@ -50,58 +47,48 @@
     this.eventTracker_.removeAll();
   }
 
-  /** @param {number} loadTime */
-  resolve_(loadTime) {
+  private resolve_(loadTime: number) {
     this.resolver_.resolve(loadTime);
     this.eventTracker_.removeAll();
   }
 }
 
-/** @type {BackgroundManager} */
-let instance = null;
+let instance: BackgroundManager|null = null;
 
 export class BackgroundManager {
-  /** @return {!BackgroundManager} */
-  static getInstance() {
+  static getInstance(): BackgroundManager {
     return instance || (instance = new BackgroundManager());
   }
 
-  /** @param {BackgroundManager} newInstance */
-  static setInstance(newInstance) {
+  static setInstance(newInstance: BackgroundManager) {
     instance = newInstance;
   }
 
+  private backgroundImage_: HTMLIFrameElement;
+  private loadTimeResolver_: LoadTimeResolver|null = null;
+  private url_: string;
+
   constructor() {
-    /** @private {!HTMLIFrameElement} */
     this.backgroundImage_ =
         strictQuery(document.body, '#backgroundImage', HTMLIFrameElement);
-    /** @private {LoadTimeResolver} */
-    this.loadTimeResolver_ = null;
-    /** @private {string} */
     this.url_ = this.backgroundImage_.src;
   }
 
   /**
    * Sets whether the background image should be shown.
-   * @param {boolean} show True, if the background image should be shown.
+   * @param show True, if the background image should be shown.
    */
-  setShowBackgroundImage(show) {
+  setShowBackgroundImage(show: boolean) {
     document.body.toggleAttribute('show-background-image', show);
   }
 
-  /**
-   * Sets the background color.
-   * @param {SkColor} color The background color.
-   */
-  setBackgroundColor(color) {
+  /** Sets the background color. */
+  setBackgroundColor(color: SkColor) {
     document.body.style.backgroundColor = skColorToRgba(color);
   }
 
-  /**
-   * Sets the background image.
-   * @param {!BackgroundImage} image The background image.
-   */
-  setBackgroundImage(image) {
+  /** Sets the background image. */
+  setBackgroundImage(image: BackgroundImage) {
     const url =
         new URL('chrome-untrusted://new-tab-page/custom_background_image');
     url.searchParams.append('url', image.url.url);
@@ -132,7 +119,7 @@
     }
     // We use |contentWindow.location.replace| because reloading the iframe by
     // setting its |src| adds a history entry.
-    this.backgroundImage_.contentWindow.location.replace(url.href);
+    this.backgroundImage_.contentWindow!.location.replace(url.href);
     // We track the URL separately because |contentWindow.location.replace| does
     // not update the iframe's src attribute.
     this.url_ = url.href;
@@ -148,9 +135,8 @@
    * setup we ensure that the load time is (re)sent _after_ both the NTP top
    * frame and the background image iframe have installed the required message
    * listeners.
-   * @return {!Promise<number>}
    */
-  getBackgroundImageLoadTime() {
+  getBackgroundImageLoadTime(): Promise<number> {
     if (!this.loadTimeResolver_) {
       this.loadTimeResolver_ = new LoadTimeResolver(this.backgroundImage_.src);
       WindowProxy.getInstance().postMessage(
diff --git a/chrome/browser/resources/new_tab_page/customize_dialog.ts b/chrome/browser/resources/new_tab_page/customize_dialog.ts
index 3cba2931..d761748 100644
--- a/chrome/browser/resources/new_tab_page/customize_dialog.ts
+++ b/chrome/browser/resources/new_tab_page/customize_dialog.ts
@@ -152,7 +152,8 @@
     }
     e.preventDefault();
     e.stopPropagation();
-    this.selectedPage = (e.target as HTMLElement).getAttribute('page-name')!;
+    this.selectedPage = (e.target as HTMLElement).getAttribute('page-name') as
+        CustomizeDialogPage;
   }
 
   private onSelectedPageChange_() {
diff --git a/chrome/browser/resources/new_tab_page/customize_dialog_types.js b/chrome/browser/resources/new_tab_page/customize_dialog_types.js
deleted file mode 100644
index ac70996..0000000
--- a/chrome/browser/resources/new_tab_page/customize_dialog_types.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// 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.
-
-/** @enum {string} */
-export const CustomizeDialogPage = {
-  BACKGROUNDS: 'backgrounds',
-  SHORTCUTS: 'shortcuts',
-  MODULES: 'modules',
-  THEMES: 'themes',
-};
diff --git a/chrome/browser/resources/new_tab_page/customize_dialog_types.ts b/chrome/browser/resources/new_tab_page/customize_dialog_types.ts
new file mode 100644
index 0000000..1f8a02ac
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/customize_dialog_types.ts
@@ -0,0 +1,10 @@
+// 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.
+
+export enum CustomizeDialogPage {
+  BACKGROUNDS = 'backgrounds',
+  SHORTCUTS = 'shortcuts',
+  MODULES = 'modules',
+  THEMES = 'themes',
+}
diff --git a/chrome/browser/resources/new_tab_page/i18n_setup.js b/chrome/browser/resources/new_tab_page/i18n_setup.ts
similarity index 100%
rename from chrome/browser/resources/new_tab_page/i18n_setup.js
rename to chrome/browser/resources/new_tab_page/i18n_setup.ts
diff --git a/chrome/browser/resources/new_tab_page/lazy_load.js b/chrome/browser/resources/new_tab_page/lazy_load.ts
similarity index 100%
rename from chrome/browser/resources/new_tab_page/lazy_load.js
rename to chrome/browser/resources/new_tab_page/lazy_load.ts
diff --git a/chrome/browser/resources/new_tab_page/logo.ts b/chrome/browser/resources/new_tab_page/logo.ts
index 78bff37..c1c1c4c 100644
--- a/chrome/browser/resources/new_tab_page/logo.ts
+++ b/chrome/browser/resources/new_tab_page/logo.ts
@@ -18,7 +18,7 @@
 import {IframeElement} from './iframe.js';
 import {Doodle, DoodleImageType, DoodleShareChannel, ImageDoodle, PageHandlerRemote} from './new_tab_page.mojom-webui.js';
 import {NewTabPageProxy} from './new_tab_page_proxy.js';
-import {$$} from './utils_ts.js';
+import {$$} from './utils.js';
 import {WindowProxy} from './window_proxy.js';
 
 const SHARE_BUTTON_SIZE_PX: number = 26;
diff --git a/chrome/browser/resources/new_tab_page/metrics_utils.js b/chrome/browser/resources/new_tab_page/metrics_utils.ts
similarity index 68%
rename from chrome/browser/resources/new_tab_page/metrics_utils.js
rename to chrome/browser/resources/new_tab_page/metrics_utils.ts
index 2318d5e..21dec6e6 100644
--- a/chrome/browser/resources/new_tab_page/metrics_utils.js
+++ b/chrome/browser/resources/new_tab_page/metrics_utils.ts
@@ -4,12 +4,8 @@
 
 import {loadTimeData} from './i18n_setup.js';
 
-/**
- * Records |durationMs| in the |metricName| histogram.
- * @param {string} metricName
- * @param {number} durationMs
- */
-export function recordDuration(metricName, durationMs) {
+/** Records |durationMs| in the |metricName| histogram. */
+export function recordDuration(metricName: string, durationMs: number) {
   chrome.metricsPrivate.recordValue(
       {
         metricName,
@@ -24,23 +20,17 @@
 /**
  * Records the duration between navigation start and |msSinceEpoch| in the
  * |metricName| histogram.
- * @param {string} metricName
- * @param {number} msSinceEpoch
  */
-export function recordLoadDuration(metricName, msSinceEpoch) {
+export function recordLoadDuration(metricName: string, msSinceEpoch: number) {
   recordDuration(
-      metricName,
-      msSinceEpoch - /** @type {number} */
-          (loadTimeData.getValue('navigationStartTime')));
+      metricName, msSinceEpoch - loadTimeData.getValue('navigationStartTime'));
 }
 
 /**
  * Records |value| (expected to be between 0 and 10) into the ten-bucket
  * |metricName| histogram.
- * @param {string} metricName
- * @param {number} value
  */
-export function recordPerdecage(metricName, value) {
+export function recordPerdecage(metricName: string, value: number) {
   chrome.metricsPrivate.recordValue(
       {
         metricName,
@@ -55,9 +45,8 @@
 /**
  * Records that an event has happened rather than a value in the |metricName|
  * histogram.
- * @param {string} metricName
  */
-export function recordOccurence(metricName) {
+export function recordOccurence(metricName: string) {
   chrome.metricsPrivate.recordValue(
       {
         metricName,
diff --git a/chrome/browser/resources/new_tab_page/modules/BUILD.gn b/chrome/browser/resources/new_tab_page/modules/BUILD.gn
index 1831aca8..659078c 100644
--- a/chrome/browser/resources/new_tab_page/modules/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/modules/BUILD.gn
@@ -3,88 +3,16 @@
 # found in the LICENSE file.
 
 import("//chrome/common/features.gni")
-import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/grit/preprocess_if_expr.gni")
 import("//tools/polymer/html_to_js.gni")
 import("//ui/webui/webui_features.gni")
 
-js_library("modules") {
-  deps = [
-    ":module_descriptor",
-    ":module_registry",
-    ":module_wrapper",
-    "..:new_tab_page_proxy",
-    "..:utils",
-    "..:utils",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
-    "//ui/webui/resources/cr_elements/cr_toast:cr_toast",
-    "//ui/webui/resources/js:assert.m",
-    "//ui/webui/resources/js:event_tracker.m",
-    "//ui/webui/resources/js:load_time_data.m",
-  ]
-}
-
-js_library("module_descriptor") {
-  deps = [
-    "..:metrics_utils",
-    "..:window_proxy",
-  ]
-}
-
-js_library("module_descriptors") {
-  deps = [
-    ":module_descriptor",
-    "..:i18n_setup",
-    "//ui/webui/resources/js:load_time_data.m",
-  ]
-  if (!is_official_build) {
-    deps += [ "dummy_v2:module" ]
-  }
-}
-
-js_library("module_wrapper") {
-  deps = [
-    ":module_descriptor",
-    "..:metrics_utils",
-    "..:window_proxy",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/js:assert.m",
-  ]
-  externs_list = [ "//third_party/closure_compiler/externs/metrics_private.js" ]
-}
-
-js_library("module_header") {
-  deps = [
-    "..:i18n_setup",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_action_menu:cr_action_menu",
-  ]
-}
-
-js_library("module_registry") {
-  deps = [
-    ":module_descriptor",
-    ":module_descriptors",
-    "..:new_tab_page_proxy",
-  ]
-}
-
-js_library("info_dialog") {
-  deps = [
-    "..:i18n_setup",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_button:cr_button.m",
-    "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
-  ]
-}
-
 html_to_js("web_components_local") {
   js_files = [
-    "module_header.js",
-    "module_wrapper.js",
-    "modules.js",
-    "info_dialog.js",
+    "module_header.ts",
+    "module_wrapper.ts",
+    "modules.ts",
+    "info_dialog.ts",
   ]
 }
 
@@ -112,16 +40,16 @@
 
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = [
-    "module_descriptor.js",
-    "module_descriptors.js",
-    "module_registry.js",
+    "module_descriptor.ts",
+    "module_descriptors.ts",
+    "module_registry.ts",
     "task_module/task_module_handler_proxy.ts",
     "cart/chrome_cart_proxy.ts",
     "drive/drive_module_proxy.ts",
     "photos/photos_module_proxy.ts",
   ]
   if (!is_official_build) {
-    in_files += [ "dummy_v2/foo_proxy.js" ]
+    in_files += [ "dummy_v2/foo_proxy.ts" ]
   }
 }
 
@@ -131,10 +59,10 @@
   in_folder = target_gen_dir
   out_folder = "$target_gen_dir/$preprocess_folder"
   in_files = [
-    "module_header.js",
-    "module_wrapper.js",
-    "modules.js",
-    "info_dialog.js",
+    "module_header.ts",
+    "module_wrapper.ts",
+    "modules.ts",
+    "info_dialog.ts",
     "task_module/module.ts",
     "recipes_v2/module.ts",
     "cart/module.ts",
@@ -144,6 +72,6 @@
     "photos/module.ts",
   ]
   if (!is_official_build) {
-    in_files += [ "dummy_v2/module.js" ]
+    in_files += [ "dummy_v2/module.ts" ]
   }
 }
diff --git a/chrome/browser/resources/new_tab_page/modules/dummy_v2/BUILD.gn b/chrome/browser/resources/new_tab_page/modules/dummy_v2/BUILD.gn
index aedf686..5f76427 100644
--- a/chrome/browser/resources/new_tab_page/modules/dummy_v2/BUILD.gn
+++ b/chrome/browser/resources/new_tab_page/modules/dummy_v2/BUILD.gn
@@ -2,24 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//third_party/closure_compiler/compile_js.gni")
 import("//tools/polymer/html_to_js.gni")
 
-js_library("module") {
-  deps = [
-    ":foo_proxy",
-    "..:module_descriptor",
-    "../..:i18n_setup",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_auto_img",
-    "//ui/webui/resources/cr_elements/cr_grid",
-  ]
-}
-
-js_library("foo_proxy") {
-  deps = [ "//chrome/browser/ui/webui/new_tab_page/foo:mojo_bindings_webui_js" ]
-}
-
 html_to_js("web_components") {
-  js_files = [ "module.js" ]
+  js_files = [ "module.ts" ]
 }
diff --git a/chrome/browser/resources/new_tab_page/modules/dummy_v2/foo_proxy.js b/chrome/browser/resources/new_tab_page/modules/dummy_v2/foo_proxy.ts
similarity index 70%
rename from chrome/browser/resources/new_tab_page/modules/dummy_v2/foo_proxy.js
rename to chrome/browser/resources/new_tab_page/modules/dummy_v2/foo_proxy.ts
index f21a984..8d9d144 100644
--- a/chrome/browser/resources/new_tab_page/modules/dummy_v2/foo_proxy.js
+++ b/chrome/browser/resources/new_tab_page/modules/dummy_v2/foo_proxy.ts
@@ -10,20 +10,16 @@
  * and receiving the browser response.
  */
 
-/** @type {?FooHandlerRemote} */
-let handler = null;
+let handler: FooHandlerRemote|null = null;
 
 export class FooProxy {
-  /** @return {!FooHandlerRemote} */
-  static getHandler() {
+  static getHandler(): FooHandlerRemote {
     return handler || (handler = FooHandler.getRemote());
   }
 
-  /** @param {!FooHandlerRemote} newHandler */
-  static setHandler(newHandler) {
+  static setHandler(newHandler: FooHandlerRemote) {
     handler = newHandler;
   }
 
-  /** @private */
-  constructor() {}
+  private constructor() {}
 }
diff --git a/chrome/browser/resources/new_tab_page/modules/dummy_v2/module.js b/chrome/browser/resources/new_tab_page/modules/dummy_v2/module.ts
similarity index 68%
rename from chrome/browser/resources/new_tab_page/modules/dummy_v2/module.js
rename to chrome/browser/resources/new_tab_page/modules/dummy_v2/module.ts
index d3a320e..e61b17143 100644
--- a/chrome/browser/resources/new_tab_page/modules/dummy_v2/module.js
+++ b/chrome/browser/resources/new_tab_page/modules/dummy_v2/module.ts
@@ -7,10 +7,10 @@
 import '../../strings.m.js';
 import '../module_header.js';
 
-import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {FooDataItem} from '../../foo.mojom-webui.js';
-import {I18nBehavior, loadTimeData} from '../../i18n_setup.js';
+import {I18nMixin, loadTimeData} from '../../i18n_setup.js';
 import {ModuleDescriptorV2, ModuleHeight} from '../module_descriptor.js';
 
 import {FooProxy} from './foo_proxy.js';
@@ -18,42 +18,34 @@
 /**
  * A dummy module, which serves as an example and a helper to build out the NTP
  * module framework.
- * @polymer
- * @extends {PolymerElement}
  */
-class DummyModuleElement extends mixinBehaviors
-([I18nBehavior], PolymerElement) {
+class DummyModuleElement extends I18nMixin
+(PolymerElement) {
   static get is() {
     return 'ntp-dummy-module';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
-      /** @type {!Array<!FooDataItem>} */
       tiles: Array,
-
-      /** @type {!string} */
       title: String,
     };
   }
 
+  tiles: FooDataItem[];
+  title: string;
+
   constructor() {
     super();
     this.initializeData_();
   }
 
-  /** @private */
-  async initializeData_() {
+  private async initializeData_() {
     const tileData = await FooProxy.getHandler().getData();
     this.tiles = tileData.data;
   }
 
-  /** @private */
-  onDisableButtonClick_() {
+  private onDisableButtonClick_() {
     this.dispatchEvent(new CustomEvent('disable-module', {
       bubbles: true,
       composed: true,
@@ -64,99 +56,87 @@
       },
     }));
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(DummyModuleElement.is, DummyModuleElement);
 
-/**
- * @param {!string} titleId
- * @return {!DummyModuleElement}
- */
-function createDummyElement(titleId) {
+function createDummyElement(titleId: string): DummyModuleElement {
   const element = new DummyModuleElement();
   element.title = loadTimeData.getString(titleId);
   return element;
 }
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor = new ModuleDescriptorV2(
+export const dummyV2Descriptor: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy',
     /*name=*/ loadTimeData.getString('modulesDummyTitle'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummyTitle')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor02 = new ModuleDescriptorV2(
+export const dummyV2Descriptor02: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy2',
     /*name=*/ loadTimeData.getString('modulesDummy2Title'),
     /*height*/ ModuleHeight.TALL,
     () => Promise.resolve(createDummyElement('modulesDummy2Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor03 = new ModuleDescriptorV2(
+export const dummyV2Descriptor03: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy3',
     /*name=*/ loadTimeData.getString('modulesDummy3Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy3Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor04 = new ModuleDescriptorV2(
+export const dummyV2Descriptor04: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy4',
     /*name=*/ loadTimeData.getString('modulesDummy4Title'),
     /*height*/ ModuleHeight.TALL,
     () => Promise.resolve(createDummyElement('modulesDummy4Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor05 = new ModuleDescriptorV2(
+export const dummyV2Descriptor05: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy5',
     /*name=*/ loadTimeData.getString('modulesDummy5Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy5Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor06 = new ModuleDescriptorV2(
+export const dummyV2Descriptor06: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy6',
     /*name=*/ loadTimeData.getString('modulesDummy6Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy6Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor07 = new ModuleDescriptorV2(
+export const dummyV2Descriptor07: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy7',
     /*name=*/ loadTimeData.getString('modulesDummy7Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy7Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor08 = new ModuleDescriptorV2(
+export const dummyV2Descriptor08: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy8',
     /*name=*/ loadTimeData.getString('modulesDummy8Title'),
     /*height*/ ModuleHeight.TALL,
     () => Promise.resolve(createDummyElement('modulesDummy8Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor09 = new ModuleDescriptorV2(
+export const dummyV2Descriptor09: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy9',
     /*name=*/ loadTimeData.getString('modulesDummy9Title'),
     /*height*/ ModuleHeight.TALL,
     () => Promise.resolve(createDummyElement('modulesDummy9Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor10 = new ModuleDescriptorV2(
+export const dummyV2Descriptor10: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy10',
     /*name=*/ loadTimeData.getString('modulesDummy10Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy10Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor11 = new ModuleDescriptorV2(
+export const dummyV2Descriptor11: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy11',
     /*name=*/ loadTimeData.getString('modulesDummy11Title'),
     /*height*/ ModuleHeight.SHORT,
     () => Promise.resolve(createDummyElement('modulesDummy11Title')));
 
-/** @type {!ModuleDescriptorV2} */
-export const dummyV2Descriptor12 = new ModuleDescriptorV2(
+export const dummyV2Descriptor12: ModuleDescriptorV2 = new ModuleDescriptorV2(
     /*id=*/ 'dummy12',
     /*name=*/ loadTimeData.getString('modulesDummy12Title'),
     /*height*/ ModuleHeight.SHORT,
diff --git a/chrome/browser/resources/new_tab_page/modules/info_dialog.js b/chrome/browser/resources/new_tab_page/modules/info_dialog.js
deleted file mode 100644
index f0499fe..0000000
--- a/chrome/browser/resources/new_tab_page/modules/info_dialog.js
+++ /dev/null
@@ -1,37 +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.
-
-import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
-import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
-
-import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {I18nBehavior} from '../i18n_setup.js';
-
-/**
- * Info dialog that can be populated with custom text via slotting.
- * @polymer
- * @extends {PolymerElement}
- */
-export class InfoDialogElement extends mixinBehaviors
-([I18nBehavior], PolymerElement) {
-  static get is() {
-    return 'ntp-info-dialog';
-  }
-
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
-  showModal() {
-    this.$.dialog.showModal();
-  }
-
-  /** @private */
-  onCloseClick_() {
-    this.$.dialog.close();
-  }
-}
-
-customElements.define(InfoDialogElement.is, InfoDialogElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/info_dialog.ts b/chrome/browser/resources/new_tab_page/modules/info_dialog.ts
new file mode 100644
index 0000000..362fb11
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/modules/info_dialog.ts
@@ -0,0 +1,39 @@
+// 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://resources/cr_elements/cr_button/cr_button.m.js';
+import 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+
+import {CrDialogElement} from 'chrome://resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {I18nMixin} from '../i18n_setup.js';
+
+export interface InfoDialogElement {
+  $: {
+    dialog: CrDialogElement,
+  };
+}
+
+/** Info dialog that can be populated with custom text via slotting. */
+export class InfoDialogElement extends I18nMixin
+(PolymerElement) {
+  static get is() {
+    return 'ntp-info-dialog';
+  }
+
+  showModal() {
+    this.$.dialog.showModal();
+  }
+
+  private onCloseClick_() {
+    this.$.dialog.close();
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+}
+
+customElements.define(InfoDialogElement.is, InfoDialogElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/module_descriptor.js b/chrome/browser/resources/new_tab_page/modules/module_descriptor.js
deleted file mode 100644
index eaf7c93..0000000
--- a/chrome/browser/resources/new_tab_page/modules/module_descriptor.js
+++ /dev/null
@@ -1,118 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {recordDuration, recordLoadDuration} from '../metrics_utils.js';
-import {WindowProxy} from '../window_proxy.js';
-
-/**
- * @fileoverview Provides the module descriptor. Each module must create a
- * module descriptor and register it at the NTP.
- */
-
-/** @typedef {function(): !Promise<?HTMLElement>} */
-export let InitializeModuleCallback;
-
-/** @typedef {function(): !Promise<!HTMLElement>} */
-export let InitializeModuleCallbackV2;
-
-/** @typedef {{element: !HTMLElement, descriptor: !ModuleDescriptor}} */
-export let Module;
-
-/**
- * @enum {number}
- * @const
- */
-export const ModuleHeight = {
-  DYNAMIC: -1,
-  SHORT: 166,
-  TALL: 358,
-};
-
-export class ModuleDescriptor {
-  /**
-   * @param {string} id
-   * @param {string} name
-   * @param {!InitializeModuleCallback} initializeCallback
-   */
-  constructor(id, name, initializeCallback) {
-    /** @private {string} */
-    this.id_ = id;
-    /** @private {string} */
-    this.name_ = name;
-    /** @private {!InitializeModuleCallback} */
-    this.initializeCallback_ = initializeCallback;
-  }
-
-  /** @return {string} */
-  get id() {
-    return this.id_;
-  }
-
-  /** @return {string} */
-  get name() {
-    return this.name_;
-  }
-
-  /** @return {number} */
-  get height() {
-    return ModuleHeight.DYNAMIC;
-  }
-
-  /**
-   * Initializes the module and returns the module element on success.
-   * @param {number} timeout Timeout in milliseconds after which initialization
-   *     aborts.
-   * @return {!Promise<?HTMLElement>}
-   */
-  async initialize(timeout) {
-    const loadStartTime = WindowProxy.getInstance().now();
-    const element = await Promise.race([
-      this.initializeCallback_(), new Promise(resolve => {
-        WindowProxy.getInstance().setTimeout(() => {
-          resolve(null);
-        }, timeout);
-      })
-    ]);
-    if (!element) {
-      return null;
-    }
-    const loadEndTime = WindowProxy.getInstance().now();
-    const duration = loadEndTime - loadStartTime;
-    recordLoadDuration('NewTabPage.Modules.Loaded', loadEndTime);
-    recordLoadDuration(`NewTabPage.Modules.Loaded.${this.id_}`, loadEndTime);
-    recordDuration('NewTabPage.Modules.LoadDuration', duration);
-    recordDuration(`NewTabPage.Modules.LoadDuration.${this.id_}`, duration);
-    return element;
-  }
-}
-
-export class ModuleDescriptorV2 extends ModuleDescriptor {
-  /**
-   * @param {string} id
-   * @param {string} name
-   * @param {!ModuleHeight} height
-   * @param {!InitializeModuleCallbackV2} initializeCallback
-   */
-  constructor(id, name, height, initializeCallback) {
-    super(id, name, initializeCallback);
-    /** @private {!ModuleHeight} */
-    this.height_ = height;
-  }
-
-  /** @override */
-  get height() {
-    return this.height_;
-  }
-
-  /**
-   * Like |ModuleDescriptor.initialize()| but returns an empty element on
-   * timeout.
-   * @param {number} timeout
-   * @return {!Promise<!HTMLElement>}
-   */
-  async initialize(timeout) {
-    return (await super.initialize(timeout)) ||
-        /** @type {!HTMLElement} */ (document.createElement('div'));
-  }
-}
diff --git a/chrome/browser/resources/new_tab_page/modules/module_descriptor.ts b/chrome/browser/resources/new_tab_page/modules/module_descriptor.ts
new file mode 100644
index 0000000..760e6d1
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/modules/module_descriptor.ts
@@ -0,0 +1,99 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {recordDuration, recordLoadDuration} from '../metrics_utils.js';
+import {WindowProxy} from '../window_proxy.js';
+
+/**
+ * @fileoverview Provides the module descriptor. Each module must create a
+ * module descriptor and register it at the NTP.
+ */
+
+export type InitializeModuleCallback = () => Promise<HTMLElement|null>;
+
+export type InitializeModuleCallbackV2 = () => Promise<HTMLElement>;
+
+export type Module = {
+  element: HTMLElement,
+  descriptor: ModuleDescriptor,
+};
+
+export enum ModuleHeight {
+  DYNAMIC = -1,
+  SHORT = 166,
+  TALL = 358,
+}
+
+export class ModuleDescriptor {
+  private id_: string;
+  private name_: string;
+  private initializeCallback_: InitializeModuleCallback;
+
+  constructor(
+      id: string, name: string, initializeCallback: InitializeModuleCallback) {
+    this.id_ = id;
+    this.name_ = name;
+    this.initializeCallback_ = initializeCallback;
+  }
+
+  get id(): string {
+    return this.id_;
+  }
+
+  get name(): string {
+    return this.name_;
+  }
+
+  get height(): ModuleHeight {
+    return ModuleHeight.DYNAMIC;
+  }
+
+  /**
+   * Initializes the module and returns the module element on success.
+   * @param timeout Timeout in milliseconds after which initialization aborts.
+   */
+  async initialize(timeout: number): Promise<HTMLElement|null> {
+    const loadStartTime = WindowProxy.getInstance().now();
+    const element = await Promise.race([
+      this.initializeCallback_(), new Promise<null>(resolve => {
+        WindowProxy.getInstance().setTimeout(() => {
+          resolve(null);
+        }, timeout);
+      })
+    ]);
+    if (!element) {
+      return null;
+    }
+    const loadEndTime = WindowProxy.getInstance().now();
+    const duration = loadEndTime - loadStartTime;
+    recordLoadDuration('NewTabPage.Modules.Loaded', loadEndTime);
+    recordLoadDuration(`NewTabPage.Modules.Loaded.${this.id_}`, loadEndTime);
+    recordDuration('NewTabPage.Modules.LoadDuration', duration);
+    recordDuration(`NewTabPage.Modules.LoadDuration.${this.id_}`, duration);
+    return element;
+  }
+}
+
+export class ModuleDescriptorV2 extends ModuleDescriptor {
+  private height_: ModuleHeight;
+
+  constructor(
+      id: string, name: string, height: ModuleHeight,
+      initializeCallback: InitializeModuleCallbackV2) {
+    super(id, name, initializeCallback);
+    this.height_ = height;
+  }
+
+  get height() {
+    return this.height_;
+  }
+
+  /**
+   * Like |ModuleDescriptor.initialize()| but returns an empty element on
+   * timeout.
+   */
+  async initialize(timeout: number): Promise<HTMLElement> {
+    return (await super.initialize(timeout)) || document.createElement('div');
+  }
+}
diff --git a/chrome/browser/resources/new_tab_page/modules/module_descriptors.js b/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts
similarity index 94%
rename from chrome/browser/resources/new_tab_page/modules/module_descriptors.js
rename to chrome/browser/resources/new_tab_page/modules/module_descriptors.ts
index 7e284b7..a774ffd 100644
--- a/chrome/browser/resources/new_tab_page/modules/module_descriptors.js
+++ b/chrome/browser/resources/new_tab_page/modules/module_descriptors.ts
@@ -20,11 +20,9 @@
 import {recipeTasksDescriptor as recipeTasksV2Descriptor} from './recipes_v2/module.js';
 import {recipeTasksDescriptor, shoppingTasksDescriptor} from './task_module/module.js';
 
-/** @type {!Array<!ModuleDescriptor>} */
-export const descriptors = [];
+export const descriptors: ModuleDescriptor[] = [];
 
-/** @type {!Array<!ModuleDescriptorV2>} */
-export const descriptorsV2 = [];
+export const descriptorsV2: ModuleDescriptorV2[] = [];
 
 if (loadTimeData.getBoolean('shoppingTasksModuleEnabled')) {
   descriptors.push(shoppingTasksDescriptor);
diff --git a/chrome/browser/resources/new_tab_page/modules/module_header.js b/chrome/browser/resources/new_tab_page/modules/module_header.js
deleted file mode 100644
index 182bf11..0000000
--- a/chrome/browser/resources/new_tab_page/modules/module_header.js
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
-
-import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {I18nBehavior, loadTimeData} from '../i18n_setup.js';
-
-/**
- * Element that displays a header inside a module.
- * @polymer
- * @extends {PolymerElement}
- */
-export class ModuleHeaderElement extends mixinBehaviors
-([I18nBehavior], PolymerElement) {
-  static get is() {
-    return 'ntp-module-header';
-  }
-
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
-  static get properties() {
-    return {
-      /**
-       * The src for the icon showing on the header.
-       * @type {string}
-       */
-      iconSrc: String,
-
-      /**
-       * The chip text showing on the header.
-       * @type {string}
-       */
-      chipText: String,
-
-      /**
-       * The description text showing in the header.
-       * @type {string}
-       */
-      descriptionText: String,
-
-      /**
-       * True if the header should display an info button.
-       * @type {boolean}
-       */
-      showInfoButton: {
-        type: Boolean,
-        value: false,
-      },
-
-      /**
-       * True if the redesigned modules are enabled. Will put the info
-       * button in the action menu dropdown instead of separate button next to
-       * the action menu.
-       * @type {boolean}
-       */
-      showInfoButtonDropdown: {
-        type: Boolean,
-        value: false,
-      },
-
-      /**
-       * True if the header should display a dismiss button.
-       * @type {boolean}
-       */
-      showDismissButton: {
-        type: Boolean,
-        value: false,
-      },
-
-      /**
-       * False if the header should display a menu button that lets the user
-       * open the module action menu.
-       * @type {boolean}
-       */
-      hideMenuButton: {
-        type: Boolean,
-        value: false,
-      },
-
-      /** @type {string} */
-      dismissText: String,
-
-      /** @type {string} */
-      disableText: String,
-
-      /** @private */
-      modulesRedesignedEnabled_: {
-        type: Boolean,
-        value: () => loadTimeData.getBoolean('modulesRedesignedEnabled'),
-        reflectToAttribute: true,
-      },
-    };
-  }
-
-  /** @private */
-  onInfoButtonClick_() {
-    this.$.actionMenu.close();
-    this.dispatchEvent(new Event('info-button-click', {bubbles: true}));
-  }
-
-  /**
-   * @param {!Event} e
-   * @private
-   */
-  onMenuButtonClick_(e) {
-    this.$.actionMenu.showAt(e.target);
-  }
-
-  /** @private */
-  onDismissButtonClick_() {
-    this.$.actionMenu.close();
-    this.dispatchEvent(new Event('dismiss-button-click', {bubbles: true}));
-  }
-
-  /** @private */
-  onDisableButtonClick_() {
-    this.$.actionMenu.close();
-    this.dispatchEvent(new Event('disable-button-click', {bubbles: true}));
-  }
-
-  /** @private */
-  onCustomizeButtonClick_() {
-    this.$.actionMenu.close();
-    this.dispatchEvent(
-        new Event('customize-module', {bubbles: true, composed: true}));
-  }
-}
-
-customElements.define(ModuleHeaderElement.is, ModuleHeaderElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/module_header.ts b/chrome/browser/resources/new_tab_page/modules/module_header.ts
new file mode 100644
index 0000000..5ac7c58
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/modules/module_header.ts
@@ -0,0 +1,119 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+
+import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {I18nMixin, loadTimeData} from '../i18n_setup.js';
+
+export interface ModuleHeaderElement {
+  $: {
+    actionMenu: CrActionMenuElement,
+  };
+}
+
+/** Element that displays a header inside a module.  */
+export class ModuleHeaderElement extends I18nMixin
+(PolymerElement) {
+  static get is() {
+    return 'ntp-module-header';
+  }
+
+  static get properties() {
+    return {
+      /** The src for the icon showing on the header. */
+      iconSrc: String,
+
+      /** The chip text showing on the header. */
+      chipText: String,
+
+      /** The description text showing in the header. */
+      descriptionText: String,
+
+      /** True if the header should display an info button. */
+      showInfoButton: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * True if the redesigned modules are enabled. Will put the info
+       * button in the action menu dropdown instead of separate button next to
+       * the action menu.
+       */
+      showInfoButtonDropdown: {
+        type: Boolean,
+        value: false,
+      },
+
+      /** True if the header should display a dismiss button. */
+      showDismissButton: {
+        type: Boolean,
+        value: false,
+      },
+
+      /**
+       * False if the header should display a menu button that lets the user
+       * open the module action menu.
+       */
+      hideMenuButton: {
+        type: Boolean,
+        value: false,
+      },
+
+      dismissText: String,
+      disableText: String,
+
+      modulesRedesignedEnabled_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('modulesRedesignedEnabled'),
+        reflectToAttribute: true,
+      },
+    };
+  }
+
+  iconSrc: string;
+  chipText: string;
+  descriptionText: string;
+  showInfoButton: boolean;
+  showInfoButtonDropdown: boolean;
+  showDismissButton: boolean;
+  hideMenuButton: boolean;
+  dismissText: string;
+  disableText: string;
+  private modulesRedesignedEnabled_: boolean;
+
+  private onInfoButtonClick_() {
+    this.$.actionMenu.close();
+    this.dispatchEvent(new Event('info-button-click', {bubbles: true}));
+  }
+
+  private onMenuButtonClick_(e: Event) {
+    this.$.actionMenu.showAt(e.target as HTMLElement);
+  }
+
+  private onDismissButtonClick_() {
+    this.$.actionMenu.close();
+    this.dispatchEvent(new Event('dismiss-button-click', {bubbles: true}));
+  }
+
+  private onDisableButtonClick_() {
+    this.$.actionMenu.close();
+    this.dispatchEvent(new Event('disable-button-click', {bubbles: true}));
+  }
+
+  private onCustomizeButtonClick_() {
+    this.$.actionMenu.close();
+    this.dispatchEvent(
+        new Event('customize-module', {bubbles: true, composed: true}));
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+}
+
+customElements.define(ModuleHeaderElement.is, ModuleHeaderElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/module_registry.js b/chrome/browser/resources/new_tab_page/modules/module_registry.ts
similarity index 75%
rename from chrome/browser/resources/new_tab_page/modules/module_registry.js
rename to chrome/browser/resources/new_tab_page/modules/module_registry.ts
index eddbf3d..0403439 100644
--- a/chrome/browser/resources/new_tab_page/modules/module_registry.js
+++ b/chrome/browser/resources/new_tab_page/modules/module_registry.ts
@@ -13,12 +13,10 @@
  * provides management function such as instantiating the local module UIs.
  */
 
-/** @type {ModuleRegistry} */
-let instance = null;
+let instance: ModuleRegistry|null = null;
 
 export class ModuleRegistry {
-  /** @return {!ModuleRegistry} */
-  static getInstance() {
+  static getInstance(): ModuleRegistry {
     return instance ||
         (instance = new ModuleRegistry(
              loadTimeData.getBoolean('modulesRedesignedEnabled') ?
@@ -26,39 +24,34 @@
                  descriptors));
   }
 
-  /** @param {ModuleRegistry} newInstance */
-  static setInstance(newInstance) {
+  static setInstance(newInstance: ModuleRegistry) {
     instance = newInstance;
   }
 
-  /**
-   * Creates a registry populated with a list of descriptors
-   * @param {!Array<!ModuleDescriptor>} descriptors
-   */
-  constructor(descriptors) {
-    /** @private {!Array<!ModuleDescriptor>} */
+  private descriptors_: ModuleDescriptor[];
+
+  /** Creates a registry populated with a list of descriptors. */
+  constructor(descriptors: ModuleDescriptor[]) {
     this.descriptors_ = descriptors;
   }
 
-  /** @return {!Array<!ModuleDescriptor>} */
-  getDescriptors() {
+  getDescriptors(): ModuleDescriptor[] {
     return this.descriptors_;
   }
 
   /**
    * Initializes enabled modules previously set via |registerModules| and
    * returns the initialized modules.
-   * @param {number} timeout Timeout in milliseconds after which initialization
-   *     of a particular module aborts.
-   * @return {!Promise<!Array<!Module>>}
+   * @param timeout Timeout in milliseconds after which initialization of a
+   *     particular module aborts.
    */
-  async initializeModules(timeout) {
+  async initializeModules(timeout: number): Promise<Module[]> {
     // Capture updateDisabledModules -> setDisabledModules round trip in a
     // promise for convenience.
-    const disabledIds = await new Promise((resolve, _) => {
+    const disabledIds = await new Promise<string[]>((resolve, _) => {
       const callbackRouter = NewTabPageProxy.getInstance().callbackRouter;
-      const listenerId =
-          callbackRouter.setDisabledModules.addListener((all, ids) => {
+      const listenerId = callbackRouter.setDisabledModules.addListener(
+          (all: boolean, ids: string[]) => {
             callbackRouter.removeListener(listenerId);
             resolve(all ? this.descriptors_.map(({id}) => id) : ids);
           });
@@ -95,6 +88,6 @@
     const elements =
         await Promise.all(descriptors.map(d => d.initialize(timeout)));
     return elements.map((e, i) => ({element: e, descriptor: descriptors[i]}))
-        .filter(m => !!m.element);
+               .filter(m => !!m.element) as Module[];
   }
 }
diff --git a/chrome/browser/resources/new_tab_page/modules/module_wrapper.js b/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts
similarity index 88%
rename from chrome/browser/resources/new_tab_page/modules/module_wrapper.js
rename to chrome/browser/resources/new_tab_page/modules/module_wrapper.ts
index 50985917..030f491 100644
--- a/chrome/browser/resources/new_tab_page/modules/module_wrapper.js
+++ b/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts
@@ -12,18 +12,20 @@
 
 /** @fileoverview Element that implements the common module UI. */
 
+export interface ModuleWrapperElement {
+  $: {
+    moduleElement: HTMLElement,
+    impressionProbe: HTMLElement,
+  };
+}
+
 export class ModuleWrapperElement extends PolymerElement {
   static get is() {
     return 'ntp-module-wrapper';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
-      /** @type {!Module} */
       module: {
         observer: 'onModuleChange_',
         type: Object,
@@ -31,12 +33,9 @@
     };
   }
 
-  /**
-   * @param {*} newValue
-   * @param {*} oldValue
-   * @private
-   */
-  onModuleChange_(newValue, oldValue) {
+  module: Module;
+
+  private onModuleChange_(_newValue: Module, oldValue?: Module) {
     assert(!oldValue);
     this.$.moduleElement.appendChild(this.module.element);
     if (this.module.descriptor.height !== ModuleHeight.DYNAMIC) {
@@ -92,10 +91,20 @@
       chrome.metricsPrivate.recordSparseHashable(
           'NewTabPage.Modules.Hover', this.module.descriptor.id);
     }, {
-      useCapture: true,  // So that modules cannot swallow event.
-      once: true,        // Only one log per NTP load.
+      capture: true,  // So that modules cannot swallow event.
+      once: true,     // Only one log per NTP load.
     });
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+}
+
+declare global {
+  interface HTMLElementTagNameMap {
+    'ntp-module-wrapper': ModuleWrapperElement;
+  }
 }
 
 customElements.define(ModuleWrapperElement.is, ModuleWrapperElement);
diff --git a/chrome/browser/resources/new_tab_page/modules/modules.js b/chrome/browser/resources/new_tab_page/modules/modules.ts
similarity index 74%
rename from chrome/browser/resources/new_tab_page/modules/modules.js
rename to chrome/browser/resources/new_tab_page/modules/modules.ts
index 7405f8a..efcd6d2 100644
--- a/chrome/browser/resources/new_tab_page/modules/modules.js
+++ b/chrome/browser/resources/new_tab_page/modules/modules.ts
@@ -6,11 +6,12 @@
 import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
 
+import {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.m.js';
-import {html, mixinBehaviors, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {I18nBehavior, loadTimeData} from '../i18n_setup.js';
+import {I18nMixin, loadTimeData} from '../i18n_setup.js';
 import {NewTabPageProxy} from '../new_tab_page_proxy.js';
 import {$$} from '../utils.js';
 
@@ -18,52 +19,52 @@
 import {ModuleRegistry} from './module_registry.js';
 import {ModuleWrapperElement} from './module_wrapper.js';
 
-/**
- * Container for the NTP modules.
- * @polymer
- * @extends {PolymerElement}
- */
-export class ModulesElement extends mixinBehaviors
-([I18nBehavior], PolymerElement) {
+
+type DismissModuleEvent =
+    CustomEvent<{message: string, restoreCallback: () => void}>;
+type DisableModuleEvent = DismissModuleEvent;
+
+declare global {
+  interface HTMLElementEventMap {
+    'dismiss-module': DismissModuleEvent, 'disable-module': DisableModuleEvent,
+  }
+}
+
+export interface ModulesElement {
+  $: {
+    modules: HTMLElement,
+    removeModuleToast: CrToastElement,
+  };
+}
+
+/** Container for the NTP modules. */
+export class ModulesElement extends I18nMixin
+(PolymerElement) {
   static get is() {
     return 'ntp-modules';
   }
 
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
   static get properties() {
     return {
-      /** @private {!Array<string>} */
       dismissedModules_: {
         type: Array,
         value: () => [],
       },
 
-      /** @private {!{all: boolean, ids: !Array<string>}} */
       disabledModules_: {
         type: Object,
         value: () => ({all: true, ids: []}),
       },
 
-      /**
-       * Data about the most recently removed module.
-       * @type {?{message: string, undo: function()}}
-       * @private
-       */
+      /** Data about the most recently removed module. */
       removedModuleData_: {
         type: Object,
         value: null,
       },
 
-      /** @private */
       modulesLoaded_: Boolean,
-
-      /** @private */
       modulesVisibilityDetermined_: Boolean,
 
-      /** @private */
       modulesLoadedAndVisibilityDetermined_: {
         type: Boolean,
         computed: `computeModulesLoadedAndVisibilityDetermined_(
@@ -72,7 +73,6 @@
         observer: 'onModulesLoadedAndVisibilityDeterminedChange_',
       },
 
-      /** @private */
       dragEnabled_: {
         type: Boolean,
         value: () => loadTimeData.getBoolean('modulesDragAndDropEnabled'),
@@ -92,77 +92,61 @@
     return ['onRemovedModulesChange_(dismissedModules_.*, disabledModules_)'];
   }
 
-  constructor() {
-    super();
-    /** @private {?number} */
-    this.setDisabledModulesListenerId_ = null;
-    /** @private {!EventTracker} */
-    this.eventTracker_ = new EventTracker();
-  }
+  private dismissedModules_: string[];
+  private disabledModules_: {all: boolean, ids: string[]};
+  private removedModuleData_: {message: string, undo: () => void}|null;
+  private modulesLoaded_: boolean;
+  private modulesVisibilityDetermined_: boolean;
+  private modulesLoadedAndVisibilityDetermined_: boolean;
+  private dragEnabled_: boolean;
 
-  /** @override */
+  private setDisabledModulesListenerId_: number|null = null;
+  private eventTracker_: EventTracker = new EventTracker();
+
   connectedCallback() {
     super.connectedCallback();
     this.setDisabledModulesListenerId_ =
         NewTabPageProxy.getInstance()
-            .callbackRouter.setDisabledModules.addListener((all, ids) => {
-              this.disabledModules_ = {all, ids};
-              this.modulesVisibilityDetermined_ = true;
-            });
+            .callbackRouter.setDisabledModules.addListener(
+                (all: boolean, ids: string[]) => {
+                  this.disabledModules_ = {all, ids};
+                  this.modulesVisibilityDetermined_ = true;
+                });
     NewTabPageProxy.getInstance().handler.updateDisabledModules();
-    this.eventTracker_.add(window, 'keydown', e => this.onWindowKeydown_(e));
+    this.eventTracker_.add(window, 'keydown', this.onWindowKeydown_.bind(this));
   }
 
-  /** @override */
   disconnectedCallback() {
     super.disconnectedCallback();
     NewTabPageProxy.getInstance().callbackRouter.removeListener(
-        assert(this.setDisabledModulesListenerId_));
+        assert(this.setDisabledModulesListenerId_!));
     this.eventTracker_.removeAll();
   }
 
-  /** @override */
   ready() {
     super.ready();
     this.renderModules_();
   }
 
-  /**
-   * @return {!Promise<void>}
-   * @private
-   */
-  async renderModules_() {
+  private async renderModules_(): Promise<void> {
     const modules = await ModuleRegistry.getInstance().initializeModules(
         loadTimeData.getInteger('modulesLoadTimeout'));
     if (modules) {
       NewTabPageProxy.getInstance().handler.onModulesLoadedWithData();
-      let shortModuleSiblingsContainer = null;
+      let shortModuleSiblingsContainer: HTMLElement|null = null;
       modules.forEach((module, index) => {
         const moduleWrapper = new ModuleWrapperElement();
         moduleWrapper.module = module;
         if (this.dragEnabled_) {
-          moduleWrapper.addEventListener('mousedown', event => {
-            this.onDragStart_(/** @type {!DragEvent} */ (event));
-          });
+          moduleWrapper.addEventListener(
+              'mousedown', e => this.onDragStart_(e));
         }
         if (!loadTimeData.getBoolean('modulesRedesignedEnabled')) {
-          moduleWrapper.addEventListener('dismiss-module', event => {
-            this.onDismissModule_(
-                /**
-                   @type {!CustomEvent<{message: string, restoreCallback:
-                      function()}>}
-                */
-                (event));
-          });
+          moduleWrapper.addEventListener(
+              'dismiss-module', e => this.onDismissModule_(e));
         }
-        moduleWrapper.addEventListener('disable-module', event => {
-          this.onDisableModule_(
-              /**
-                 @type {!CustomEvent<{message: string, restoreCallback:
-                     ?function()}>}
-               */
-              (event));
-        });
+        moduleWrapper.addEventListener(
+            'disable-module', e => this.onDisableModule_(e));
         let moduleContainerParent = this.$.modules;
         if (loadTimeData.getBoolean('modulesRedesignedLayoutEnabled')) {
           // Wrap pairs of sibling short modules in a container. All other
@@ -199,11 +183,7 @@
     }
   }
 
-  /**
-   * @param {KeyboardEvent} e
-   * @private
-   */
-  onWindowKeydown_(e) {
+  private onWindowKeydown_(e: KeyboardEvent) {
     let ctrlKeyPressed = e.ctrlKey;
     // <if expr="is_macosx">
     ctrlKeyPressed = ctrlKeyPressed || e.metaKey;
@@ -213,16 +193,11 @@
     }
   }
 
-  /** @private */
-  onModulesLoaded_() {
+  private onModulesLoaded_() {
     this.modulesLoaded_ = true;
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  computeModulesLoadedAndVisibilityDetermined_() {
+  private computeModulesLoadedAndVisibilityDetermined_(): boolean {
     return this.modulesLoaded_ && this.modulesVisibilityDetermined_;
   }
 
@@ -242,14 +217,11 @@
   }
 
   /**
-   * @param {!CustomEvent<{message: string, restoreCallback: function()}>} e
-   *     Event notifying a module was dismissed. Contains the message to show in
-   *     the toast.
-   * @private
+   * @param e Event notifying a module was dismissed. Contains the message to
+   *     show in the toast.
    */
-  onDismissModule_(e) {
-    const id =
-        /** @type {ModuleWrapperElement} */ (e.target).module.descriptor.id;
+  private onDismissModule_(e: DismissModuleEvent) {
+    const id = (e.target as ModuleWrapperElement).module.descriptor.id;
     const restoreCallback = e.detail.restoreCallback;
     this.removedModuleData_ = {
       message: e.detail.message,
@@ -264,20 +236,17 @@
     }
 
     // Notify the user.
-    $$(this, '#removeModuleToast').show();
+    this.$.removeModuleToast.show();
     // Notify the backend.
     NewTabPageProxy.getInstance().handler.onDismissModule(id);
   }
 
   /**
-   * @param {!CustomEvent<{message: string, restoreCallback: ?function()}>} e
-   *     Event notifying a module was disabled. Contains the message to show in
-   *     the toast.
-   * @private
+   * @param e Event notifying a module was disabled. Contains the message to
+   *     show in the toast.
    */
-  onDisableModule_(e) {
-    const id =
-        /** @type {ModuleWrapperElement} */ (e.target).module.descriptor.id;
+  private onDisableModule_(e: DisableModuleEvent) {
+    const id = (e.target as ModuleWrapperElement).module.descriptor.id;
     const restoreCallback = e.detail.restoreCallback;
     this.removedModuleData_ = {
       message: e.detail.message,
@@ -294,19 +263,14 @@
     };
 
     NewTabPageProxy.getInstance().handler.setModuleDisabled(id, true);
-    $$(this, '#removeModuleToast').show();
+    this.$.removeModuleToast.show();
     chrome.metricsPrivate.recordSparseHashable(
         'NewTabPage.Modules.Disabled', id);
     chrome.metricsPrivate.recordSparseHashable(
         'NewTabPage.Modules.Disabled.ModuleRequest', id);
   }
 
-  /**
-   * @param {string} id
-   * @return {boolean}
-   * @private
-   */
-  moduleDisabled_(id) {
+  private moduleDisabled_(id: string): boolean {
     return this.disabledModules_.all || this.dismissedModules_.includes(id) ||
         this.disabledModules_.ids.includes(id);
   }
@@ -321,7 +285,7 @@
     this.removedModuleData_.undo();
 
     // Notify the user.
-    $$(this, '#removeModuleToast').hide();
+    this.$.removeModuleToast.hide();
 
     this.removedModuleData_ = null;
   }
@@ -331,9 +295,9 @@
    * @private
    */
   onRemovedModulesChange_() {
-    this.shadowRoot.querySelectorAll('ntp-module-wrapper')
+    this.shadowRoot!.querySelectorAll('ntp-module-wrapper')
         .forEach(moduleWrapper => {
-          moduleWrapper.parentElement.hidden =
+          moduleWrapper.parentElement!.hidden =
               this.moduleDisabled_(moduleWrapper.module.descriptor.id);
         });
   }
@@ -341,13 +305,11 @@
   /**
    * Module is dragged by updating the module position based on the
    * position of the pointer.
-   * @param {!DragEvent} e
-   * @private
    */
-  onDragStart_(e) {
+  private onDragStart_(e: MouseEvent) {
     assert(loadTimeData.getBoolean('modulesDragAndDropEnabled'));
 
-    const dragElement = e.target;
+    const dragElement = e.target as HTMLElement;
     const dragElementRect = dragElement.getBoundingClientRect();
     // This is the offset between the pointer and module so that the
     // module isn't dragged by the top-left corner.
@@ -356,14 +318,14 @@
       y: e.y - dragElementRect.y,
     };
 
-    dragElement.parentElement.style.width = `${dragElementRect.width}px`;
-    dragElement.parentElement.style.height = `${dragElementRect.height}px`;
+    dragElement.parentElement!.style.width = `${dragElementRect.width}px`;
+    dragElement.parentElement!.style.height = `${dragElementRect.height}px`;
 
     const undraggedModuleWrappers =
-        [...this.shadowRoot.querySelectorAll('ntp-module-wrapper')].filter(
+        [...this.shadowRoot!.querySelectorAll('ntp-module-wrapper')].filter(
             moduleWrapper => moduleWrapper !== dragElement);
 
-    const dragOver = e => {
+    const dragOver = (e: MouseEvent) => {
       e.preventDefault();
 
       dragElement.setAttribute('dragging', '');
@@ -371,10 +333,11 @@
       dragElement.style.top = `${e.y - dragOffset.y}px`;
     };
 
-    const dragEnter = e => {
+    const dragEnter = (e: MouseEvent) => {
       const moduleContainers = [...this.$.modules.childNodes];
-      const dragIndex = moduleContainers.indexOf(dragElement.parentElement);
-      const dropIndex = moduleContainers.indexOf(e.target.parentElement);
+      const dragIndex = moduleContainers.indexOf(dragElement.parentElement!);
+      const dropIndex =
+          moduleContainers.indexOf((e.target as HTMLElement).parentElement!);
 
       const positionType = dragIndex > dropIndex ? 'beforebegin' : 'afterend';
       const dragContainer = moduleContainers[dragIndex];
@@ -391,7 +354,8 @@
       });
 
       dragContainer.remove();
-      previousContainer.insertAdjacentElement(positionType, dragContainer);
+      (previousContainer as Element)
+          .insertAdjacentElement(positionType, dragContainer as Element);
 
       undraggedModuleWrappers.forEach((moduleWrapper, i) => {
         const lastRect = moduleWrapper.getBoundingClientRect();
@@ -445,11 +409,15 @@
           {duration: 800, easing: 'ease'});
 
       const moduleIds =
-          [...this.shadowRoot.querySelectorAll('ntp-module-wrapper')].map(
+          [...this.shadowRoot!.querySelectorAll('ntp-module-wrapper')].map(
               moduleWrapper => moduleWrapper.module.descriptor.id);
       NewTabPageProxy.getInstance().handler.setModulesOrder(moduleIds);
     }, {once: true});
   }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
 }
 
 customElements.define(ModulesElement.is, ModulesElement);
diff --git a/chrome/browser/resources/new_tab_page/new_tab_page.js b/chrome/browser/resources/new_tab_page/new_tab_page.ts
similarity index 100%
rename from chrome/browser/resources/new_tab_page/new_tab_page.js
rename to chrome/browser/resources/new_tab_page/new_tab_page.ts
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.ts
similarity index 67%
rename from chrome/browser/resources/new_tab_page/new_tab_page_proxy.js
rename to chrome/browser/resources/new_tab_page/new_tab_page_proxy.ts
index 9a61cdb..eec1c00 100644
--- a/chrome/browser/resources/new_tab_page/new_tab_page_proxy.js
+++ b/chrome/browser/resources/new_tab_page/new_tab_page_proxy.ts
@@ -4,13 +4,11 @@
 
 import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote} from './new_tab_page.mojom-webui.js';
 
-/** @type {?NewTabPageProxy} */
-let instance = null;
+let instance: NewTabPageProxy|null = null;
 
 /** Holds Mojo interfaces for communication with the browser process. */
 export class NewTabPageProxy {
-  /** @return {!NewTabPageProxy} */
-  static getInstance() {
+  static getInstance(): NewTabPageProxy {
     if (!instance) {
       const handler = new PageHandlerRemote();
       const callbackRouter = new PageCallbackRouter();
@@ -22,20 +20,16 @@
     return instance;
   }
 
-  /**
-   * @param {!PageHandlerRemote} handler
-   * @param {!PageCallbackRouter} callbackRouter
-   */
-  static setInstance(handler, callbackRouter) {
+  static setInstance(
+      handler: PageHandlerRemote, callbackRouter: PageCallbackRouter) {
     instance = new NewTabPageProxy(handler, callbackRouter);
   }
 
-  /**
-   * @param {!PageHandlerRemote} handler
-   * @param {!PageCallbackRouter} callbackRouter
-   * @private
-   */
-  constructor(handler, callbackRouter) {
+  handler: PageHandlerRemote;
+  callbackRouter: PageCallbackRouter;
+
+  private constructor(
+      handler: PageHandlerRemote, callbackRouter: PageCallbackRouter) {
     this.handler = handler;
     this.callbackRouter = callbackRouter;
   }
diff --git a/chrome/browser/resources/new_tab_page/utils.js b/chrome/browser/resources/new_tab_page/utils.ts
similarity index 65%
rename from chrome/browser/resources/new_tab_page/utils.js
rename to chrome/browser/resources/new_tab_page/utils.ts
index 3338213..ae4f211 100644
--- a/chrome/browser/resources/new_tab_page/utils.js
+++ b/chrome/browser/resources/new_tab_page/utils.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from 'chrome://resources/js/assert.m.js';
+import {assert} from 'chrome://resources/js/assert_ts.js';
 
 import {String16} from 'chrome://resources/mojo/mojo/public/mojom/base/string16.mojom-webui.js';
 import {TimeDelta, TimeTicks} from 'chrome://resources/mojo/mojo/public/mojom/base/time.mojom-webui.js';
@@ -16,14 +16,10 @@
  *
  * Returns an |IntersectionObserver| so the caller can disconnect the observer
  * when needed.
- * @param {!Element} container
- * @param {!Element} topBorder
- * @param {!Element} bottomBorder
- * @param {string} showAttribute
- * @return {!IntersectionObserver}
  */
 export function createScrollBorders(
-    container, topBorder, bottomBorder, showAttribute) {
+    container: Element, topBorder: Element, bottomBorder: Element,
+    showAttribute: string): IntersectionObserver {
   const topProbe = document.createElement('div');
   container.prepend(topProbe);
   const bottomProbe = document.createElement('div');
@@ -43,21 +39,13 @@
   return observer;
 }
 
-/**
- * Converts a String16 to a JavaScript String.
- * @param {?String16} str
- * @return {string}
- */
-export function decodeString16(str) {
+/** Converts a String16 to a JavaScript String. */
+export function decodeString16(str: String16|null): string {
   return str ? str.data.map(ch => String.fromCodePoint(ch)).join('') : '';
 }
 
-/**
- * Converts a JavaScript String to a String16.
- * @param {string} str
- * @return {!String16}
- */
-export function mojoString16(str) {
+/** Converts a JavaScript String to a String16. */
+export function mojoString16(str: string): String16 {
   const array = new Array(str.length);
   for (let i = 0; i < str.length; ++i) {
     array[i] = str.charCodeAt(i);
@@ -67,44 +55,43 @@
 
 /**
  * Converts a time delta in milliseconds to TimeDelta.
- * @param {number} timeDelta time delta in milliseconds
- * @returns {!TimeDelta}
+ * @param timeDelta time delta in milliseconds
  */
-export function mojoTimeDelta(timeDelta) {
+export function mojoTimeDelta(timeDelta: number): TimeDelta {
   return {microseconds: BigInt(Math.floor(timeDelta * 1000))};
 }
 
 /**
  * Converts a time ticks in milliseconds to TimeTicks.
- * @param {number} timeTicks time ticks in milliseconds
- * @returns {!TimeTicks}
+ * @param timeTicks time ticks in milliseconds
  */
-export function mojoTimeTicks(timeTicks) {
+export function mojoTimeTicks(timeTicks: number): TimeTicks {
   return {internalValue: BigInt(Math.floor(timeTicks * 1000))};
 }
 
 /**
  * Queries |selector| on |element|'s shadow root and returns the resulting
  * element if there is any.
- * @param {!Element} element
- * @param {string} selector
- * @return {Element}
  */
-export function $$(element, selector) {
-  return element.shadowRoot.querySelector(selector);
+export function $$<K extends keyof HTMLElementTagNameMap>(
+    element: Element, selector: K): HTMLElementTagNameMap[K]|null;
+export function $$<K extends keyof SVGElementTagNameMap>(
+    element: Element, selector: K): SVGElementTagNameMap[K]|null;
+export function $$<E extends Element = Element>(
+    element: Element, selector: string): E|null;
+export function $$(element: Element, selector: string) {
+  return element.shadowRoot!.querySelector(selector);
 }
 
+type Constructor<T> = new (...args: any[]) => T;
+
 /**
  * Queries |selector| on |root| and returns the resulting element. Throws
  * exception if there is no resulting element or if element is not of type
  * |type|.
- * @param {!Element|!ShadowRoot} root
- * @param {string} selector
- * @param {!function(new:T)} type
- * @return {!T}
- * @template T
  */
-export function strictQuery(root, selector, type) {
+export function strictQuery<T>(
+    root: Element|ShadowRoot, selector: string, type: Constructor<T>): T {
   const element = root.querySelector(selector);
   assert(element && element instanceof type);
   return element;
diff --git a/chrome/browser/resources/new_tab_page/utils_ts.ts b/chrome/browser/resources/new_tab_page/utils_ts.ts
deleted file mode 100644
index 231db9a..0000000
--- a/chrome/browser/resources/new_tab_page/utils_ts.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2022 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.
-
-/**
- * Queries |selector| on |element|'s shadow root and returns the resulting
- * element if there is any.
- * TODO(crbug.com/1273590): Delete when utils.js is converted to TypeScript.
- */
-export function $$<K extends keyof HTMLElementTagNameMap>(
-    element: Element, selector: K): HTMLElementTagNameMap[K]|null;
-export function $$<K extends keyof SVGElementTagNameMap>(
-    element: Element, selector: K): SVGElementTagNameMap[K]|null;
-export function $$<E extends Element = Element>(
-    element: Element, selector: string): E|null;
-export function $$(element: Element, selector: string) {
-  return element.shadowRoot!.querySelector(selector);
-}
diff --git a/chrome/browser/resources/new_tab_page/window_proxy.js b/chrome/browser/resources/new_tab_page/window_proxy.js
deleted file mode 100644
index 2fb3aa6..0000000
--- a/chrome/browser/resources/new_tab_page/window_proxy.js
+++ /dev/null
@@ -1,97 +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.
-
-/** @type {?WindowProxy} */
-let instance = null;
-
-/** Abstracts some builtin JS functions to mock them in tests. */
-export class WindowProxy {
-  /** @return {!WindowProxy} */
-  static getInstance() {
-    return instance || (instance = new WindowProxy());
-  }
-
-  /** @param {?WindowProxy} newInstance */
-  static setInstance(newInstance) {
-    instance = newInstance;
-  }
-
-  /** @param {string} href */
-  navigate(href) {
-    window.location.href = href;
-  }
-
-  /** @param {string} url */
-  open(url) {
-    window.open(url, '_blank');
-  }
-
-  /**
-   * @param {function()} callback
-   * @param {number} duration
-   * @return {number}
-   */
-  setTimeout(callback, duration) {
-    return window.setTimeout(callback, duration);
-  }
-
-  /** @param {?number} id */
-  clearTimeout(id) {
-    window.clearTimeout(id);
-  }
-
-  /** @return {number} */
-  random() {
-    return Math.random();
-  }
-
-  /**
-   * @param {string} src
-   * @return {string}
-   */
-  createIframeSrc(src) {
-    return src;
-  }
-
-  /**
-   * @param {string} query
-   * @return {!MediaQueryList}
-   */
-  matchMedia(query) {
-    return window.matchMedia(query);
-  }
-
-  /** @return {number} */
-  now() {
-    return Date.now();
-  }
-
-  /**
-   * Returns promise that resolves when lazy rendering should be started.
-   * @return {!Promise}
-   */
-  waitForLazyRender() {
-    return new Promise((resolve, reject) => {
-      requestIdleCallback(resolve, {timeout: 500});
-    });
-  }
-
-  /**
-   * Posts |message| on the content window of |iframe| at |targetOrigin|.
-   * @param {!HTMLIFrameElement} iframe
-   * @param {*} message
-   * @param {string} targetOrigin
-   */
-  postMessage(iframe, message, targetOrigin) {
-    iframe.contentWindow.postMessage(message, targetOrigin);
-  }
-
-  /**
-   * Returns `window.location.href` wrapped in a URL object.
-   * @return {!URL}
-   */
-  get url() {
-    return new URL(window.location.href);
-  }
-}
diff --git a/chrome/browser/resources/new_tab_page/window_proxy.ts b/chrome/browser/resources/new_tab_page/window_proxy.ts
new file mode 100644
index 0000000..017e724
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/window_proxy.ts
@@ -0,0 +1,73 @@
+// 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.
+
+let instance: WindowProxy|null = null;
+
+declare global {
+  interface Window {
+    // https://github.com/microsoft/TypeScript/issues/40807
+    requestIdleCallback(callback: () => void, options?: {timeout: number}):
+        void;
+  }
+}
+
+/** Abstracts some builtin JS functions to mock them in tests. */
+export class WindowProxy {
+  static getInstance(): WindowProxy {
+    return instance || (instance = new WindowProxy());
+  }
+
+  static setInstance(newInstance: WindowProxy) {
+    instance = newInstance;
+  }
+
+  navigate(href: string) {
+    window.location.href = href;
+  }
+
+  open(url: string) {
+    window.open(url, '_blank');
+  }
+
+  setTimeout(callback: () => void, duration: number): number {
+    return window.setTimeout(callback, duration);
+  }
+
+  clearTimeout(id: number|null) {
+    window.clearTimeout(id !== null ? id : undefined);
+  }
+
+  random(): number {
+    return Math.random();
+  }
+
+  createIframeSrc(src: string): string {
+    return src;
+  }
+
+  matchMedia(query: string): MediaQueryList {
+    return window.matchMedia(query);
+  }
+
+  now(): number {
+    return Date.now();
+  }
+
+  /** Returns promise that resolves when lazy rendering should be started. */
+  waitForLazyRender(): Promise<void> {
+    return new Promise<void>(resolve => {
+      window.requestIdleCallback(resolve, {timeout: 500});
+    });
+  }
+
+  /** Posts |message| on the content window of |iframe| at |targetOrigin|. */
+  postMessage(iframe: HTMLIFrameElement, message: any, targetOrigin: string) {
+    iframe.contentWindow!.postMessage(message, targetOrigin);
+  }
+
+  /** Returns `window.location.href` wrapped in a URL object. */
+  get url(): URL {
+    return new URL(window.location.href);
+  }
+}
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 05db5350..c90a40b 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -154,6 +154,7 @@
   input_files = [
     "os_settings_v3.html",
     "images/computer_and_bluetooth_switch.svg",
+    "images/computer_and_bluetooth_switch_dark.svg",
     "images/error_badge.svg",
     "images/error_badge_dark.svg",
     "images/icon_add_circle.svg",
@@ -178,7 +179,9 @@
     "images/switch_access_setup_guide_choose_two_switches.svg",
     "images/switch_access_setup_guide_choose_three_switches.svg",
     "images/switch_access_setup_guide_auto_scan_enabled.svg",
+    "images/switch_access_setup_guide_auto_scan_enabled_dark.svg",
     "images/switch_access_setup_guide_closing.svg",
+    "images/switch_access_setup_guide_closing_dark.svg",
     "../../nearby_share/shared/nearby_share_progress_bar_dark.json",
     "../../nearby_share/shared/nearby_share_progress_bar_light.json",
     "../../nearby_share/shared/nearby_share_pulse_animation_dark.json",
diff --git a/chrome/browser/resources/settings/chromeos/images/computer_and_bluetooth_switch.svg b/chrome/browser/resources/settings/chromeos/images/computer_and_bluetooth_switch.svg
index 3c905f5..befe4b8 100644
--- a/chrome/browser/resources/settings/chromeos/images/computer_and_bluetooth_switch.svg
+++ b/chrome/browser/resources/settings/chromeos/images/computer_and_bluetooth_switch.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216 216"><defs><clipPath id="a"><circle cx="62" cy="75.9" r="11.4" fill="none"/></clipPath><clipPath id="b"><path fill="none" d="M27.7 129.7h38.7v17.15H27.7z"/></clipPath><clipPath id="c"><path fill="none" d="M27.7 129.7h38.7v17.15H27.7z"/></clipPath></defs><g data-name="Layer 2"><g data-name="Start Setup R4"><path fill="none" stroke="#2a84fc" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M5.7 147.4h208.4"/><path d="M139.1 74l3.8-1.1a2.1 2.1 0 0 0 1.4-1.5l.9-3.8a1.8 1.8 0 0 0-.5-1.9l-2.9-2.8a1.9 1.9 0 0 0-2-.4l-3.8 1.1a2 2 0 0 0-1.4 1.4l-.9 3.9a2 2 0 0 0 .6 1.9l2.9 2.8a2.1 2.1 0 0 0 1.9.4z" fill="#5ef1f2"/><circle cx="43.9" cy="96" r="3.4" fill="#f882ff"/><path d="M78.2 56.3a7.6 7.6 0 0 0-.9 1.8l-1 1.9a8.3 8.3 0 0 1-3.9 3.5c-2.4.9-4.9 1.2-6.3 3.6a6.3 6.3 0 0 0-.1 6.1l.2.3a6.1 6.1 0 0 0 9.2 1.1 23.9 23.9 0 0 0 2.2-2.3 7.4 7.4 0 0 1 5-2.4h2.2a6.7 6.7 0 0 0 2 0 9 9 0 0 0 4-1.8A8.6 8.6 0 0 0 93 57a8.7 8.7 0 0 0-12.3-3.2 8.5 8.5 0 0 0-2.5 2.5z" fill="none" stroke="#fbbc05" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><circle cx="62" cy="75.9" r="11.4" fill="#34a853"/><g clip-path="url(#a)"><path d="M78.2 56.3a7.6 7.6 0 0 0-.9 1.8l-1 1.9a8.3 8.3 0 0 1-3.9 3.5c-2.4.9-4.9 1.2-6.3 3.6a6.3 6.3 0 0 0-.1 6.1l.2.3a6.1 6.1 0 0 0 9.2 1.1 23.9 23.9 0 0 0 2.2-2.3 7.4 7.4 0 0 1 5-2.4h2.2a6.7 6.7 0 0 0 2 0 9 9 0 0 0 4-1.8A8.6 8.6 0 0 0 93 57a8.7 8.7 0 0 0-12.3-3.2 8.5 8.5 0 0 0-2.5 2.5z" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></g><g clip-path="url(#b)"><g clip-path="url(#c)"><path d="M57 142.7H37.2a2.6 2.6 0 0 1-2.4-2.7v-7.6a2.6 2.6 0 0 1 2.4-2.7H57a2.6 2.6 0 0 1 2.4 2.7v7.6a2.6 2.6 0 0 1-2.4 2.7" fill="#7cb1f2"/><path d="M61.1 145.1h-28a2.6 2.6 0 0 1-2.7-2.4v-6.5a2.5 2.5 0 0 1 2.7-2.3l3.4 1c9.7 2.5 18.7.8 24.6-1a3.2 3.2 0 0 1 1.9.6 2.5 2.5 0 0 1 .8 1.7v6.5a2.6 2.6 0 0 1-2.7 2.4" fill="#d2e3fc"/><path d="M61.1 145.1h-28a2.6 2.6 0 0 1-2.7-2.4v-6.5a2.5 2.5 0 0 1 2.7-2.3l3.4 1c9.7 2.5 18.7.8 24.6-1a3.2 3.2 0 0 1 1.9.6 2.5 2.5 0 0 1 .8 1.7v6.5a2.6 2.6 0 0 1-2.7 2.4z" fill="none" stroke="#2a84fc" stroke-linecap="round" stroke-miterlimit="10" stroke-width="1.41"/><path fill="#4285f4" d="M28.4 139.8h37.4v6.28H28.4z"/><path fill="none" stroke="#2a84fc" stroke-linecap="round" stroke-miterlimit="10" stroke-width="1.41" d="M28.4 139.8h37.4v6.28H28.4z"/></g></g><g data-name="BG LP"><path d="M202.4 79.8H118a4.2 4.2 0 0 0-4.1 3.7l-5 60.1h91.9l5-60.1a3.3 3.3 0 0 0-3.4-3.7z" fill="#fff" stroke="#2a84fc" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M198.6 146.7H86.2a2.6 2.6 0 0 1-2.6-2.6h0a1.3 1.3 0 0 1 1.3-1.3h115a1.3 1.3 0 0 1 1.3 1.3h0a2.6 2.6 0 0 1-2.6 2.6z" fill="#fff" stroke="#2a84fc" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M200.1 83.9h-80.3a1.7 1.7 0 0 0-1.5 1.4l-4.9 54.2h83l5-54.2a1.3 1.3 0 0 0-1.3-1.4z" fill="#d2e3fc" stroke="#2a84fc" stroke-miterlimit="10" stroke-width="1.5"/><path fill="none" stroke="#2a84fc" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M170.3 142.7v4"/><path fill="none" stroke="#2a84fc" stroke-linecap="round" stroke-linejoin="round" d="M196.8 144.7h-3.7M189.9 144.7h-.5"/><circle cx="158" cy="82" r=".6" fill="#2a84fc"/><ellipse cx="156.4" cy="110.2" rx="12.6" ry="11.9" transform="rotate(-75.2 156.473 110.157)" fill="#2a84fc"/></g><path d="M127 59.5a5.9 5.9 0 0 1-8.3 1.1l-11.1-8.4a6 6 0 0 1-1.1-8.4 5.9 5.9 0 0 1 8.3-1.1l11.1 8.4a6 6 0 0 1 1.1 8.4z" fill="#f882ff"/><path d="M37.7 121.1l6.9.7a1.8 1.8 0 0 0 1.8-.8l4-5.3a1.7 1.7 0 0 0 .2-2l-2.7-6.1a2.3 2.3 0 0 0-1.7-1.1l-6.9-.8a2.3 2.3 0 0 0-1.8.8l-4 5.3a2.2 2.2 0 0 0-.2 2.1l2.8 6a2.2 2.2 0 0 0 1.6 1.2z" fill="none" stroke="#fe2c25" stroke-miterlimit="10" stroke-width="1.5"/><path d="M149.7 107.7l4.6 5.9a.8.8 0 0 0 1.2.2l13.4-10.4" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M152.2 92.6l-.4-6a1 1 0 0 0-1.5-.7l-5 3.4a1 1 0 0 0 .1 1.7l5.4 2.6a1 1 0 0 0 1.4-1z" fill="#7cb1f2"/><path fill="none" d="M0 0h216v216H0z"/></g></g></svg>
\ No newline at end of file
+<svg width="217" height="216" viewBox="0 0 217 216" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 149.598h208.4" stroke="#4285F4" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round"/><path d="M135.544 76.898l3.8-1.1a1.996 1.996 0 0 0 1.4-1.4l.9-3.9a1.902 1.902 0 0 0-.5-1.9l-2.9-2.8a2 2 0 0 0-2-.4l-3.8 1.1a2.1 2.1 0 0 0-1.4 1.5l-.9 3.8a2 2 0 0 0 .6 1.9l2.9 2.8a1.895 1.895 0 0 0 1.9.4z" fill="#2FE2EA"/><path d="M40.344 102.299a3.4 3.4 0 1 0-.001-6.801 3.4 3.4 0 0 0 0 6.801z" fill="#EE5FFA"/><path d="M74.644 59.198a7.598 7.598 0 0 0-.9 1.8l-1 1.9a7.8 7.8 0 0 1-3.9 3.5c-2.4.9-4.9 1.2-6.3 3.6a6.3 6.3 0 0 0-.1 6.1l.2.3a6.1 6.1 0 0 0 9.2 1.2l2.2-2.4a7.3 7.3 0 0 1 5-2.3h4.2a9 9 0 0 0 4-1.8 8.7 8.7 0 0 0-10.1-14.1 9.498 9.498 0 0 0-2.5 2.2v0z" stroke="#FBBC04" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M58.444 90.198c6.296 0 11.4-5.103 11.4-11.4 0-6.296-5.104-11.4-11.4-11.4s-11.4 5.104-11.4 11.4 5.104 11.4 11.4 11.4z" fill="#34A853"/><path d="M69.794 79.872a7.1 7.1 0 0 1-7.985-2.923l-.197-.296a1.012 1.012 0 0 1-.05-.085 7.3 7.3 0 0 1 .115-7.068l.003-.005a6.27 6.27 0 0 1 .989-1.288c.673.269 1.314.6 1.916.986-.46.337-.854.755-1.176 1.307a5.3 5.3 0 0 0-.104 5.087l.177.266a5.099 5.099 0 0 0 6.314 1.892 11.574 11.574 0 0 1-.002 2.127z" fill="#fff"/><path d="M198.844 82.698h-84.4a4.302 4.302 0 0 0-4.1 3.7l-5 60.1h91.9l5.1-60.1c.113-1.981-1.515-3.703-3.5-3.7z" stroke="#4285F4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M195.044 149.598h-112.4c-.69 0-1.351-.364-1.839-1.012-.487-.648-.761-1.527-.761-2.444 0-.458.137-.897.38-1.221.244-.324.575-.507.92-.507h115c.345 0 .675.183.919.507s.381.763.381 1.221c0 .917-.274 1.796-.762 2.444-.487.648-1.148 1.012-1.838 1.012z" fill="#fff"/><path d="M198.061 87.335h-83.495c-.773-.055-1.506.516-1.562 1.301l-4.393 52.23h86.161l4.642-52.23c.069-.755-.615-1.363-1.353-1.3z" fill="#D2E3FC"/><path d="M154.847 86.603a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" fill="#8AB4F8"/><path d="M188.544 146.959a1 1 0 0 1 1-1h3.7a1 1 0 1 1 0 2h-3.7a1 1 0 0 1-1-1zM187.653 146.959a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" fill="#4285F4"/><path fill-rule="evenodd" clip-rule="evenodd" d="M82.644 150.598c-1.062 0-1.999-.561-2.638-1.411-.634-.843-.962-1.941-.962-3.044 0-.646.19-1.304.582-1.823.394-.525 1-.905 1.718-.905h115c.717 0 1.323.38 1.718.905.391.519.582 1.177.582 1.823 0 1.103-.328 2.201-.962 3.044-.639.85-1.577 1.411-2.638 1.411h-112.4zm113.439-2.613c-.336.447-.721.613-1.039.613h-27.3v-3a.996.996 0 0 0-.017-.183h28.615c.002 0 .008.002.017.007.02.011.059.039.105.1.097.128.18.35.18.621 0 .729-.22 1.389-.561 1.842zm-30.322-2.57H81.346l-.004.001a.357.357 0 0 0-.119.106c-.096.128-.18.35-.18.621 0 .729.221 1.389.561 1.842.337.447.722.613 1.04.613h83.1v-3c0-.063.006-.124.017-.183z" fill="#4285F4"/><path d="M164.613 116.502c1.778-6.728-1.932-13.543-8.286-15.221-6.355-1.68-12.947 2.414-14.724 9.142-1.778 6.728 1.932 13.543 8.286 15.222 6.354 1.678 12.946-2.415 14.724-9.143z" fill="#4285F4"/><path d="M123.544 62.398a6 6 0 0 1-8.4 1.1l-11.1-8.4a5.998 5.998 0 0 1-1.1-8.4 5.898 5.898 0 0 1 8.3-1.1l11.1 8.4a6.014 6.014 0 0 1 2.34 3.952 6 6 0 0 1-1.14 4.448z" fill="#EE5FFA"/><path d="M34.144 123.998l6.9.7a1.8 1.8 0 0 0 1.8-.8l4-5.3a1.9 1.9 0 0 0 .3-2l-2.8-6.1a2.11 2.11 0 0 0-1.6-1.1l-7-.7a2.003 2.003 0 0 0-1.8.7l-4 5.3a2.204 2.204 0 0 0-.2 2.1l2.8 6a2.004 2.004 0 0 0 1.6 1.2z" stroke="#EA4335" stroke-width="2" stroke-miterlimit="10"/><path d="M146.144 110.598l4.6 5.9a.796.796 0 0 0 1.2.2l13.4-10.4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M149.048 97.8l-.401-6a1.002 1.002 0 0 0-1.015-.935c-.171.003-.337.05-.485.135l-5 3.4a1.003 1.003 0 0 0-.269 1.357.998.998 0 0 0 .37.343l5.399 2.6a.904.904 0 0 0 1.002.019.901.901 0 0 0 .399-.919zM50.257 131.104H25.852a2.89 2.89 0 0 0-2.89 2.89v8.116a2.89 2.89 0 0 0 2.89 2.89h24.405a2.89 2.89 0 0 0 2.89-2.89v-8.116a2.89 2.89 0 0 0-2.89-2.89z" fill="#8AB4F8"/><path d="M55.315 147.4h-34.36c-1.846 0-3.291-1.124-3.291-2.489v-6.904c0-1.364 1.445-2.488 3.291-2.488 1.847 0 2.65.802 4.175 1.124 11.881 2.569 22.88.802 30.185-1.124a3.934 3.934 0 0 1 2.328.722 2.33 2.33 0 0 1 .963 1.766v6.904c0 1.365-1.525 2.489-3.291 2.489z" fill="#fff"/><path d="M55.315 147.4h-34.36c-1.846 0-3.291-1.124-3.291-2.489v-6.904c0-1.364 1.445-2.488 3.291-2.488 1.847 0 2.65.802 4.175 1.124 11.881 2.569 22.88.802 30.185-1.124a3.934 3.934 0 0 1 2.328.722 2.33 2.33 0 0 1 .963 1.766v6.904c0 1.365-1.525 2.489-3.291 2.489z" fill="#D2E3FC"/><path fill-rule="evenodd" clip-rule="evenodd" d="M16.861 142.406v-4.399c0-1.996 2.02-3.291 4.094-3.291 1.058 0 1.823.234 2.498.496.166.064.32.127.468.187.462.188.86.35 1.374.458l.004.001c11.712 2.532 22.572.794 29.811-1.115l.095-.026.098-.001a4.74 4.74 0 0 1 2.804.87l.005.004a3.131 3.131 0 0 1 1.296 2.375l.001.021v4.42h1.726v6.68h-46v-6.68h1.726zm8.101-4.978c11.996 2.593 23.084.827 30.457-1.106a3.13 3.13 0 0 1 1.757.572 1.537 1.537 0 0 1 .627 1.137v4.375H18.467v-4.399c0-.733.87-1.685 2.488-1.685.79 0 1.349.167 1.918.387.117.045.24.095.369.148.484.198 1.052.43 1.72.571z" fill="#4285F4"/></svg>
diff --git a/chrome/browser/resources/settings/chromeos/images/computer_and_bluetooth_switch_dark.svg b/chrome/browser/resources/settings/chromeos/images/computer_and_bluetooth_switch_dark.svg
new file mode 100644
index 0000000..e5c4a45b
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/images/computer_and_bluetooth_switch_dark.svg
@@ -0,0 +1 @@
+<svg width="217" height="216" viewBox="0 0 217 216" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4.044 149.598h208.4" stroke="#669DF6" stroke-width="2" stroke-miterlimit="10" stroke-linecap="round"/><path d="M135.544 76.898l3.8-1.1a1.996 1.996 0 0 0 1.4-1.4l.9-3.9a1.902 1.902 0 0 0-.5-1.9l-2.9-2.8a2 2 0 0 0-2-.4l-3.8 1.1a2.1 2.1 0 0 0-1.4 1.5l-.9 3.8a2 2 0 0 0 .6 1.9l2.9 2.8a1.895 1.895 0 0 0 1.9.4z" fill="#5DF1F2"/><path d="M40.344 102.299a3.4 3.4 0 1 0-.001-6.801 3.4 3.4 0 0 0 0 6.801z" fill="#F882FE"/><path d="M74.644 59.198a7.598 7.598 0 0 0-.9 1.8l-1 1.9a7.8 7.8 0 0 1-3.9 3.5c-2.4.9-4.9 1.2-6.3 3.6a6.3 6.3 0 0 0-.1 6.1l.2.3a6.1 6.1 0 0 0 9.2 1.2l2.2-2.4a7.3 7.3 0 0 1 5-2.3h4.2a9 9 0 0 0 4-1.8 8.7 8.7 0 0 0-10.1-14.1 9.498 9.498 0 0 0-2.5 2.2v0z" stroke="#FCC934" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M58.444 90.198c6.296 0 11.4-5.103 11.4-11.4 0-6.296-5.104-11.4-11.4-11.4s-11.4 5.104-11.4 11.4 5.104 11.4 11.4 11.4z" fill="#5BB974"/><path d="M69.794 79.872a7.1 7.1 0 0 1-7.985-2.923l-.197-.296a1.012 1.012 0 0 1-.05-.085 7.3 7.3 0 0 1 .115-7.068l.003-.005a6.27 6.27 0 0 1 .989-1.288c.673.269 1.314.6 1.916.986-.46.337-.854.755-1.176 1.307a5.3 5.3 0 0 0-.104 5.087l.177.266a5.099 5.099 0 0 0 6.314 1.892 11.574 11.574 0 0 1-.002 2.127z" fill="#323336"/><path d="M198.844 82.698h-84.4a4.302 4.302 0 0 0-4.1 3.7l-5 60.1h91.9l5.1-60.1c.113-1.981-1.515-3.703-3.5-3.7z" stroke="#669DF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M195.044 149.598h-112.4c-.69 0-1.351-.364-1.839-1.012-.487-.648-.761-1.527-.761-2.444 0-.458.137-.897.38-1.221.244-.324.575-.507.92-.507h115c.345 0 .675.183.919.507s.381.763.381 1.221c0 .917-.274 1.796-.762 2.444-.487.648-1.148 1.012-1.838 1.012z" fill="#323336"/><path d="M198.061 87.335h-83.495c-.773-.055-1.506.516-1.562 1.301l-4.393 52.23h86.161l4.642-52.23c.069-.755-.615-1.363-1.353-1.3z" fill="#8AB4F8" fill-opacity=".4"/><path d="M154.847 86.603a1 1 0 1 0 0-2 1 1 0 0 0 0 2z" fill="#1A73E8" fill-opacity=".4"/><path d="M188.544 146.959a1 1 0 0 1 1-1h3.7a1 1 0 1 1 0 2h-3.7a1 1 0 0 1-1-1zM187.653 146.959a1 1 0 1 1-2 0 1 1 0 0 1 2 0z" fill="#669DF6"/><path fill-rule="evenodd" clip-rule="evenodd" d="M82.644 150.598c-1.062 0-1.999-.561-2.638-1.411-.634-.843-.962-1.941-.962-3.044 0-.646.19-1.304.582-1.823.394-.525 1-.905 1.718-.905h115c.717 0 1.323.38 1.718.905.391.519.582 1.177.582 1.823 0 1.103-.328 2.201-.962 3.044-.639.85-1.577 1.411-2.638 1.411h-112.4zm113.439-2.613c-.336.447-.721.613-1.039.613h-27.3v-3a.996.996 0 0 0-.017-.183h28.615c.002 0 .008.002.017.007.02.011.059.039.105.1.097.128.18.35.18.621 0 .729-.22 1.389-.561 1.842zm-30.322-2.57H81.346l-.004.001a.357.357 0 0 0-.119.106c-.096.128-.18.35-.18.621 0 .729.221 1.389.561 1.842.337.447.722.613 1.04.613h83.1v-3c0-.063.006-.124.017-.183z" fill="#669DF6"/><path d="M164.613 116.502c1.778-6.728-1.932-13.543-8.286-15.221-6.355-1.68-12.947 2.414-14.724 9.142-1.778 6.728 1.932 13.543 8.286 15.222 6.354 1.678 12.946-2.415 14.724-9.143z" fill="#669DF6"/><path d="M123.544 62.398a6 6 0 0 1-8.4 1.1l-11.1-8.4a5.998 5.998 0 0 1-1.1-8.4 5.898 5.898 0 0 1 8.3-1.1l11.1 8.4a6.014 6.014 0 0 1 2.34 3.952 6 6 0 0 1-1.14 4.448z" fill="#F882FE"/><path d="M34.144 123.998l6.9.7a1.8 1.8 0 0 0 1.8-.8l4-5.3a1.9 1.9 0 0 0 .3-2l-2.8-6.1a2.11 2.11 0 0 0-1.6-1.1l-7-.7a2.003 2.003 0 0 0-1.8.7l-4 5.3a2.204 2.204 0 0 0-.2 2.1l2.8 6a2.004 2.004 0 0 0 1.6 1.2z" stroke="#EA4335" stroke-width="2" stroke-miterlimit="10"/><path d="M146.144 110.598l4.6 5.9a.796.796 0 0 0 1.2.2l13.4-10.4" stroke="#323336" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M149.048 97.8l-.401-6a1.002 1.002 0 0 0-1.015-.935c-.171.003-.337.05-.485.135l-5 3.4a1.003 1.003 0 0 0-.269 1.357.998.998 0 0 0 .37.343l5.399 2.6a.904.904 0 0 0 1.002.019.901.901 0 0 0 .399-.919zM50.257 131.104H25.852a2.89 2.89 0 0 0-2.89 2.89v8.116a2.89 2.89 0 0 0 2.89 2.89h24.405a2.89 2.89 0 0 0 2.89-2.89v-8.116a2.89 2.89 0 0 0-2.89-2.89z" fill="#1A73E8" fill-opacity=".4"/><path d="M55.315 147.4h-34.36c-1.846 0-3.291-1.124-3.291-2.489v-6.904c0-1.364 1.445-2.488 3.291-2.488 1.847 0 2.65.802 4.175 1.124 11.881 2.569 22.88.802 30.185-1.124a3.934 3.934 0 0 1 2.328.722 2.33 2.33 0 0 1 .963 1.766v6.904c0 1.365-1.525 2.489-3.291 2.489z" fill="#323336"/><path d="M55.315 147.4h-34.36c-1.846 0-3.291-1.124-3.291-2.489v-6.904c0-1.364 1.445-2.488 3.291-2.488 1.847 0 2.65.802 4.175 1.124 11.881 2.569 22.88.802 30.185-1.124a3.934 3.934 0 0 1 2.328.722 2.33 2.33 0 0 1 .963 1.766v6.904c0 1.365-1.525 2.489-3.291 2.489z" fill="#8AB4F8" fill-opacity=".4"/><path fill-rule="evenodd" clip-rule="evenodd" d="M16.861 142.406v-4.399c0-1.996 2.02-3.291 4.094-3.291 1.058 0 1.823.234 2.498.496.166.064.32.127.468.187.462.188.86.35 1.374.458l.004.001c11.712 2.532 22.572.794 29.811-1.115l.095-.026.098-.001a4.74 4.74 0 0 1 2.804.87l.005.004a3.131 3.131 0 0 1 1.296 2.375l.001.021v4.42h1.726v6.68h-46v-6.68h1.726zm8.101-4.978c11.996 2.593 23.084.827 30.457-1.106a3.13 3.13 0 0 1 1.757.572 1.537 1.537 0 0 1 .627 1.137v4.375H18.467v-4.399c0-.733.87-1.685 2.488-1.685.79 0 1.349.167 1.918.387.117.045.24.095.369.148.484.198 1.052.43 1.72.571z" fill="#669DF6"/></svg>
diff --git a/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_auto_scan_enabled.svg b/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_auto_scan_enabled.svg
index 1439107..c283902 100644
--- a/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_auto_scan_enabled.svg
+++ b/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_auto_scan_enabled.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216 216"><g data-name="Layer 2"><g data-name="Auto Scan"><g data-name="Auto Scan Pattern - Diagram"><rect x="93.8" y="160.7" width="28.5" height="28.26" rx="4.5" transform="rotate(-49 107.956 174.81)" fill="#39b54a"/><path d="M31 128.6l-11.9-11.9a2.4 2.4 0 0 1-.6-2.2l4.4-16.2a2.2 2.2 0 0 1 1.6-1.6l16.2-4.4a2.4 2.4 0 0 1 2.3.6l11.8 11.9a2.3 2.3 0 0 1 .6 2.2l-4.3 16.2a2.9 2.9 0 0 1-1.7 1.7l-16.2 4.3a2.3 2.3 0 0 1-2.2-.6z" fill="#fbbc05"/><rect x="159.3" y="166.9" width="39.2" height="13.96" rx="7" transform="rotate(-144.2 178.814 173.932)" fill="#2a84fc"/><path d="M105 98.7a14.3 14.3 0 0 0-.9 2.7l-1 3a10.4 10.4 0 0 1-4.9 5.6c-3.1 1.8-6.7 2.6-8.1 6.3a9 9 0 0 0 1.3 8.9l.3.4a8.8 8.8 0 0 0 13.5.2 25.5 25.5 0 0 0 2.6-3.8 10.2 10.2 0 0 1 6.7-4.2l3-.4a13.5 13.5 0 0 0 2.9-.5 12 12 0 0 0 5.3-3.1 12.8 12.8 0 0 0 .7-16.5c-4.5-6-12.9-7.2-18.4-2.6a11.7 11.7 0 0 0-3 4z" fill="#7cb1f2"/><path d="M57.3 175.6a2.1 2.1 0 0 1 .9 2.9 20.5 20.5 0 0 1-36.7-17.7 2 2 0 0 1 2.8-1.1z" fill="none" stroke="#f882ff" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><rect x="161.3" y="32.2" width="29.8" height="29.83" rx="1.4" fill="#d2e3fc"/><circle cx="37.8" cy="47.9" r="18.2" fill="#ea4335"/><path d="M125.3 46.5L97.8 30.7a.6.6 0 0 0-1 .6V63a.6.6 0 0 0 1 .6l27.5-15.8a.8.8 0 0 0 0-1.3z" fill="#f882ff"/><path fill="none" d="M0 0h216v216H0z"/><rect x="160.2" y="93.5" width="34.5" height="34.18" rx="17.1" transform="rotate(-26.9 177.52 110.752)" fill="none" stroke="#5ef1f2" stroke-miterlimit="10" stroke-width="3"/></g><circle data-name="Auto Scan Pattern - Select Circle" cx="37.2" cy="110.5" r="28.4" fill="none" stroke="#d2e3fc" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/></g></g></svg>
\ No newline at end of file
+<svg width="217" height="216" viewBox="0 0 217 216" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M104.07 157.894L91.276 172.61a4.5 4.5 0 0 0 .444 6.349l14.536 12.636a4.5 4.5 0 0 0 6.348-.444l12.794-14.717a4.502 4.502 0 0 0-.444-6.349l-14.536-12.635a4.499 4.499 0 0 0-6.348.444z" fill="#34A853"/><path d="M31.144 128.6l-11.8-11.8a2.398 2.398 0 0 1-.6-2.3l4.3-16.2a2.3 2.3 0 0 1 1.7-1.6l16.1-4.3a2.2 2.2 0 0 1 2.3.6l11.8 11.8a2.2 2.2 0 0 1 .6 2.3l-4.3 16.1a2.301 2.301 0 0 1-1.6 1.7l-16.2 4.3a2.406 2.406 0 0 1-2.3-.6z" fill="#FBBC04"/><path d="M164.325 172.227l20.439 14.741c3.135 2.261 7.505 1.56 9.76-1.567 2.255-3.126 1.541-7.494-1.594-9.756l-20.439-14.741c-3.136-2.261-7.506-1.56-9.761 1.567-2.255 3.127-1.541 7.494 1.595 9.756z" fill="#4285F4"/><path d="M105.144 98.7c-.816 1.81-1.252 3.821-1.9 5.7a10.4 10.4 0 0 1-4.8 5.7c-3.1 1.7-6.7 2.6-8.2 6.2a9.503 9.503 0 0 0-.47 4.614c.437 2.993 2.323 5.713 5.055 7.042a8.909 8.909 0 0 0 3.696.895 8.964 8.964 0 0 0 6.819-2.951c1-1.2 1.8-2.6 2.7-3.8a9.495 9.495 0 0 1 2.848-2.811c2.066-1.315 4.492-1.508 6.852-1.889a9.178 9.178 0 0 0 2.8-.5 11.497 11.497 0 0 0 5.3-3.1c4.2-4.3 4.5-11.4.7-16.5s-12.9-7.1-18.3-2.5a12.998 12.998 0 0 0-3.1 3.9z" fill="#8AB4F8"/><path fill-rule="evenodd" clip-rule="evenodd" d="M23.152 161.417a18.9 18.9 0 0 0 33.909 16.306c.163-.269.133-.626-.17-.772l-32.998-15.9-.016-.008c-.339-.168-.613.07-.725.374zm-.876-3.143a3.497 3.497 0 0 1 2.926.078l32.995 15.897a3.5 3.5 0 0 1 1.417 5.049 21.9 21.9 0 0 1-39.29-18.883 3.502 3.502 0 0 1 1.952-2.141z" fill="#EE5FFA"/><path d="M189.944 32.2h-27a1.4 1.4 0 0 0-1.4 1.4v27.03a1.4 1.4 0 0 0 1.4 1.4h27a1.4 1.4 0 0 0 1.4-1.4V33.6a1.4 1.4 0 0 0-1.4-1.4z" fill="#D2E3FC"/><path d="M38.044 66.1c10.052 0 18.2-8.148 18.2-18.2s-8.148-18.2-18.2-18.2-18.2 8.148-18.2 18.2 8.148 18.2 18.2 18.2z" fill="#EA4335"/><path d="M125.544 46.6l-27.5-15.9a.7.7 0 0 0-1.1.6V63a.7.7 0 0 0 1.1.6l27.5-15.8a.705.705 0 0 0 0-1.2z" fill="#EE5FFA"/><path fill-rule="evenodd" clip-rule="evenodd" d="M170.444 97.26c-7.684 3.898-10.754 13.283-6.859 20.961 3.895 7.677 13.282 10.743 20.966 6.845l.267-.136c7.685-3.898 10.754-13.283 6.859-20.961-3.895-7.678-13.281-10.743-20.966-6.845l-.267.136zm-9.534 22.318c-4.646-9.157-.984-20.347 8.177-24.994l.267-.136c9.16-4.647 20.353-.993 24.998 8.164 4.646 9.157.984 20.346-8.176 24.994l-.268.135c-9.16 4.648-20.353.994-24.998-8.163z" fill="#2FE2EA"/><path fill-rule="evenodd" clip-rule="evenodd" d="M37.444 83.6c-14.857 0-26.9 12.044-26.9 26.9 0 14.857 12.043 26.9 26.9 26.9 14.856 0 26.9-12.043 26.9-26.9 0-14.856-12.044-26.9-26.9-26.9zm-29.9 26.9c0-16.513 13.387-29.9 29.9-29.9 16.513 0 29.9 13.387 29.9 29.9 0 16.513-13.387 29.9-29.9 29.9-16.513 0-29.9-13.387-29.9-29.9z" fill="#D2E3FC"/></svg>
diff --git a/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_auto_scan_enabled_dark.svg b/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_auto_scan_enabled_dark.svg
new file mode 100644
index 0000000..51b3f2c
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_auto_scan_enabled_dark.svg
@@ -0,0 +1 @@
+<svg width="217" height="216" viewBox="0 0 217 216" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M107.16 157.272l-12.793 14.717a4.5 4.5 0 0 0 .444 6.348l14.535 12.636a4.502 4.502 0 0 0 6.349-.444l12.793-14.717a4.499 4.499 0 0 0-.444-6.348l-14.536-12.636a4.499 4.499 0 0 0-6.348.444z" fill="#5BB974"/><path d="M34.234 127.978l-11.8-11.8a2.398 2.398 0 0 1-.6-2.3l4.3-16.2a2.3 2.3 0 0 1 1.7-1.6l16.1-4.3a2.2 2.2 0 0 1 2.3.6l11.8 11.8a2.205 2.205 0 0 1 .6 2.3l-4.3 16.1a2.296 2.296 0 0 1-1.6 1.7l-16.2 4.3a2.395 2.395 0 0 1-2.3-.6z" fill="#FCC934"/><path d="M167.415 171.605l20.439 14.741c3.135 2.261 7.505 1.56 9.76-1.567 2.255-3.126 1.542-7.494-1.594-9.755l-20.439-14.741c-3.135-2.262-7.505-1.56-9.76 1.566-2.255 3.127-1.541 7.495 1.594 9.756z" fill="#669DF6"/><path d="M108.234 98.078c-.815 1.81-1.252 3.821-1.9 5.7a10.4 10.4 0 0 1-4.8 5.7c-3.1 1.7-6.7 2.6-8.2 6.2a9.503 9.503 0 0 0-.47 4.614c.438 2.993 2.323 5.713 5.055 7.042a8.907 8.907 0 0 0 3.696.895 8.961 8.961 0 0 0 6.819-2.951c1-1.2 1.8-2.6 2.7-3.8a9.507 9.507 0 0 1 2.848-2.811c2.066-1.315 4.493-1.508 6.852-1.889a9.172 9.172 0 0 0 2.8-.5 11.497 11.497 0 0 0 5.3-3.1c4.2-4.3 4.5-11.4.7-16.5s-12.9-7.1-18.3-2.5a13.013 13.013 0 0 0-3.1 3.9z" fill="#1A73E8" fill-opacity=".4"/><path fill-rule="evenodd" clip-rule="evenodd" d="M26.242 160.795a18.9 18.9 0 0 0 33.91 16.306c.162-.269.132-.626-.17-.772l-32.999-15.9-.016-.007c-.338-.168-.613.069-.725.373zm-.876-3.143a3.513 3.513 0 0 1 2.926.078l32.995 15.898a3.496 3.496 0 0 1 1.934 3.696 3.5 3.5 0 0 1-.516 1.352 21.9 21.9 0 0 1-39.29-18.883 3.502 3.502 0 0 1 1.951-2.141z" fill="#F882FE"/><path d="M193.034 31.578h-27a1.4 1.4 0 0 0-1.4 1.4v27.03a1.4 1.4 0 0 0 1.4 1.4h27a1.4 1.4 0 0 0 1.4-1.4v-27.03a1.4 1.4 0 0 0-1.4-1.4z" fill="#8AB4F8" fill-opacity=".4"/><path d="M41.134 65.478c10.052 0 18.2-8.148 18.2-18.2 0-10.051-8.148-18.2-18.2-18.2-10.051 0-18.2 8.149-18.2 18.2 0 10.052 8.149 18.2 18.2 18.2z" fill="#EA4335"/><path d="M128.634 45.978l-27.5-15.9a.698.698 0 0 0-1.009.228.702.702 0 0 0-.091.372v31.7a.702.702 0 0 0 .738.725.697.697 0 0 0 .362-.125l27.5-15.8a.693.693 0 0 0 .34-.6.695.695 0 0 0-.34-.6z" fill="#F882FE"/><path fill-rule="evenodd" clip-rule="evenodd" d="M173.534 96.638c-7.684 3.898-10.753 13.283-6.858 20.961 3.895 7.678 13.281 10.743 20.965 6.845l.268-.136c7.684-3.898 10.753-13.283 6.858-20.961-3.895-7.677-13.281-10.743-20.965-6.845l-.268.136zM164 118.956c-4.645-9.157-.983-20.346 8.177-24.994l.268-.135c9.16-4.648 20.352-.994 24.998 8.163 4.645 9.157.983 20.346-8.177 24.994l-.267.135c-9.161 4.648-20.353.994-24.999-8.163z" fill="#5DF1F2"/><path fill-rule="evenodd" clip-rule="evenodd" d="M40.534 82.978c-14.856 0-26.9 12.044-26.9 26.9 0 14.857 12.044 26.9 26.9 26.9 14.857 0 26.9-12.043 26.9-26.9 0-14.856-12.043-26.9-26.9-26.9zm-29.9 26.9c0-16.513 13.387-29.9 29.9-29.9 16.514 0 29.9 13.387 29.9 29.9 0 16.514-13.386 29.9-29.9 29.9-16.513 0-29.9-13.386-29.9-29.9z" fill="#8AB4F8" fill-opacity=".4"/></svg>
diff --git a/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_closing.svg b/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_closing.svg
index 2673f48b..fce25f7 100644
--- a/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_closing.svg
+++ b/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_closing.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216 216"><defs><clipPath id="a"><path d="M28.8 116.3a67.9 67.9 0 0 0 4.6 25.7c10 25.8 37.3 49.5 70.4 49.3 38.4-.2 75-32.1 75-75a75 75 0 0 0-150 0z" fill="none"/></clipPath></defs><g data-name="Layer 2"><g data-name="Success R2"><path d="M51.8 84.7h0a9.2 9.2 0 0 0-3.6-12.4L29.6 62a9.2 9.2 0 0 0-12.4 3.6h0a9.1 9.1 0 0 0 3.6 12.3l18.6 10.4a9.2 9.2 0 0 0 12.4-3.6z" fill="none" stroke="#fbbc05" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M165.3 73.3a75.1 75.1 0 1 0 13.5 43 75.1 75.1 0 0 0-13.5-43z" fill="#d2e3fc"/><rect x="126.1" y="31.2" width="9.2" height="9.18" rx="1.4" transform="rotate(143.2 130.745 35.83)" fill="#fe2c25"/><path d="M120.1 24.1l2.7-2.7a2.4 2.4 0 0 0 .6-2.3l-1-3.7a2.5 2.5 0 0 0-1.6-1.7l-3.8-1a2.3 2.3 0 0 0-2.2.7l-2.7 2.7a2.3 2.3 0 0 0-.7 2.2l1 3.7a2.4 2.4 0 0 0 1.7 1.7l3.7 1a2.4 2.4 0 0 0 2.3-.6z" fill="#f882ff"/><path d="M133 57.9l-7.4 6.1a1.7 1.7 0 0 1-2.7-1l-1.5-9.5a1.6 1.6 0 0 1 2.2-1.8l9 3.4a1.6 1.6 0 0 1 .4 2.8z" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><g clip-path="url(#a)"><path d="M56.3 86.9h0a9.1 9.1 0 0 0-3.7-12.3L33.8 64.5a9.1 9.1 0 0 0-12.3 3.8h0a9.1 9.1 0 0 0 3.8 12.3L44 90.7a9.2 9.2 0 0 0 12.3-3.8z" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></g><circle cx="99.8" cy="70.7" r="11.3" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M152.9 126.7a49.7 49.7 0 0 0-12.5-22.1c-12.1-12.3-27-14.3-31.5-14.7-.1.6-1.3 4.9-5 6.4s-9.7-.8-12.3-6.4M107.3 69.9a3.7 3.7 0 0 1-5.4 3.2M108.9 89.9l-2.1-9" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M101.4 60.1a18.4 18.4 0 0 1-13.6 12.3l.6-3.5a9.4 9.4 0 0 1 .7-3 8.6 8.6 0 0 1 3.4-4.3 12.8 12.8 0 0 1 5-2 7.4 7.4 0 0 1 3.9.5z" fill="#4285f4"/><circle cx="81.5" cy="57.7" r="11.2" fill="#4285f4"/><path d="M126.9 117.4a28.4 28.4 0 0 0-.8 3.9c-.7 4.7.3 7 .1 11.7a33.3 33.3 0 0 1-1 6.2M89.9 124.1c1.1 5.2 2.1 10.4 3.1 15.6" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><path d="M93.6 139.5l31-.5c4 9.7 7.9 19.5 11.9 29.2-30.1 16-63.1 7.4-75.1-13.1-7.6-12.9-8.5-33.5 3-48.6C74 93.9 87.9 90.7 92 89.9l3.6-7.7" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><g fill="none" stroke="#2a84fc" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M179.5 89.6s.5 2.8 1.1 7.1M161 74.2a12.8 12.8 0 0 0-3.2 3.7c-4.2 7.1 1 16.3 1.5 17.3M176.6 72.9c3.4-1.5 12.1-5.7 14.7-10 .4-.7 1.2-2.1.7-3.1a2.3 2.3 0 0 0-3.1-.8 8.5 8.5 0 0 0-2.7 2.3 28.5 28.5 0 0 1-9.8 6.9 20.5 20.5 0 0 1-5.1 1.6"/><path d="M191.6 62.6a2.4 2.4 0 0 1 4.1-.2c.9 1.5 0 3.4-1.1 4.9-3.7 5.3-9.3 8.5-15.7 10.3"/><path d="M194.7 67.4a2.5 2.5 0 0 1 4.1-.3c.8 1.5-.1 3.3-1.2 4.7-3.7 5.1-9.3 8.2-15.6 10"/><path d="M179.6 89.2c6.7-1.7 14.4-6.1 18.9-11.4a7 7 0 0 0 1.5-2.5 3 3 0 0 0-.7-2.7 2.4 2.4 0 0 0-1.3-.6M161 74.2a41.5 41.5 0 0 0 6-5.6c4.4-4.9 5.9-9.3 8.6-9.1a2.6 2.6 0 0 1 2.2 1.3c1 1.7-.2 4.6-3.3 7.4"/></g><path d="M194.3 96.7l-60.5-.2a4 4 0 0 0-3.6 2.5l-13.7 41.4h66.4l13.3-41.1a1.9 1.9 0 0 0-1.9-2.6z" fill="#2a84fc" stroke="#2a84fc" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.89"/><path fill="none" stroke="#2a84fc" stroke-linecap="round" stroke-linejoin="round" stroke-width="4" d="M182.4 141.1h-72.8"/><path d="M172.9 140.5c1.9-.1 2.9 2 2.6 3.4a5.8 5.8 0 0 1-2.4 3.7c-5.3 4.6-11.4 6.5-18.6 9.6M151.6 153.3c2.7-1.2 5.5-2.3 8.2-3.8a65.2 65.2 0 0 0 9.5-6.3 22.1 22.1 0 0 0 3.2-2.9c1.2-1.3 2.2-3 1.8-4.7a3 3 0 0 0-3.1-2.1 6.1 6.1 0 0 0-2.2 1.2 25.4 25.4 0 0 0-2.6 2.1" fill="#d2e3fc"/><path d="M144.8 141.8c1.7-2 4.8-5.3 6.5-7.2s1.7-2.2.9-3.5a3 3 0 0 0-2.1-1.3 3.6 3.6 0 0 0-2.3.3 19.5 19.5 0 0 0-3.5 1.9c-5 3.1-9.3 5.4-12.8 10h.1c-1.3.7-1.9.4-3.3 1.2l8.6 23.7 6.7-2.9c10.7-3.3 11.3-2.7 22.9-7.6a14.2 14.2 0 0 0 4.8-2.7c1.5-1.6 1.3-4 1.3-5.1-.1-7-6.9-14.5-8.1-15.8z" fill="#d2e3fc"/><path d="M145.1 141.6a55.6 55.6 0 0 0 16.9-10.7 12.9 12.9 0 0 1 4.2-2.8c1.5-.5 3.4-.2 4.1 1.2a3.1 3.1 0 0 1-.2 2.8 10.5 10.5 0 0 1-1.8 2.5c-6.3 7.1-10.9 10.6-19.6 14" fill="#d2e3fc"/><path d="M172.4 149.7c.7 1.8-.2 3.7-1.6 5a13.8 13.8 0 0 1-4.8 2.5c-11.8 4.3-12.5 3.7-23.3 6.4l-6.9 2.5M153.6 156.5c7.3-2.7 13.4-4.3 19-8.6a6.4 6.4 0 0 0 2.6-3.5c.3-1.5-.6-3.6-2.4-3.6M150.8 152.6c2.8-1.2 5.7-2.1 8.4-3.4a74.2 74.2 0 0 0 9.8-5.8 31.7 31.7 0 0 0 3.4-2.8c1.3-1.3 2.3-2.9 2-4.6a2.8 2.8 0 0 0-3-2.2 4.6 4.6 0 0 0-2.3 1 27.1 27.1 0 0 0-2.7 2M144.6 140.7l6.9-6.9c.9-.8 1.8-2.1 1.1-3.4a3.2 3.2 0 0 0-2.1-1.5 4.5 4.5 0 0 0-2.3.3 14.9 14.9 0 0 0-3.6 1.7c-5.1 2.8-9.6 5-13.3 9.3M131.5 140.1l-4.2 2.1" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.83"/><path d="M144.9 140.5a58.2 58.2 0 0 0 17.5-9.8 10.9 10.9 0 0 1 4.2-2.6c1.6-.5 3.5-.1 4.1 1.4a3.1 3.1 0 0 1-.3 2.8 11.2 11.2 0 0 1-1.9 2.4c-6.7 6.7-11.5 10-20.3 13" fill="none" stroke="#4285f4" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.83"/><path d="M164.7 44.5h-.2a1.1 1.1 0 0 1-.8-.3l-7.2-5.6a1.1 1.1 0 0 1-.1-1.5 1 1 0 0 1 1.5-.2l6.3 4.9 8.2-11.2a12.9 12.9 0 0 0-17.5-.3h-.2a13 13 0 0 0 17.2 19.4h.1a13 13 0 0 0 1.9-17.4L165.3 44c-.2.3-.4.5-.6.5z" fill="#34a853"/><path d="M176.8 26.6a1.2 1.2 0 0 0-1.6.3l-2.8 3.8.7.8.8.9 3.1-4.2a1.2 1.2 0 0 0-.2-1.6z" fill="#7cb1f2"/><path d="M161.7 75a11.9 11.9 0 0 0-3 3.4c-3.8 6.6 1 15.3 1.5 16.3a1 1 0 0 1-.3 1.4h-.5a1.1 1.1 0 0 1-.9-.5c-.6-1.1-5.9-10.8-1.5-18.3a15.8 15.8 0 0 1 3.4-4h.1l2.9-2.4a73.5 73.5 0 1 0 1.2 1.6z" fill="none"/><path d="M160.5 73.4h-.1a15.8 15.8 0 0 0-3.4 4c-4.4 7.5.9 17.2 1.5 18.3a1.1 1.1 0 0 0 .9.5h.5a1 1 0 0 0 .3-1.4c-.5-1-5.3-9.7-1.5-16.3a11.9 11.9 0 0 1 3-3.4l2.9-2.4-1.2-1.7-2.9 2.4z" fill="#2a84fc"/><path fill="none" d="M0 0h216v216H0z"/></g></g></svg>
\ No newline at end of file
+<svg width="217" height="216" viewBox="0 0 217 216" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M164.962 79.312a75.104 75.104 0 0 0-94.711-24.448 75.1 75.1 0 1 0 108.211 67.448 74.812 74.812 0 0 0-13.5-43z" fill="#D2E3FC"/><path d="M130.747 46.332l5.125-3.834a1.4 1.4 0 0 0 .282-1.96l-3.822-5.108a1.4 1.4 0 0 0-1.959-.283l-5.125 3.834a1.4 1.4 0 0 0-.282 1.96l3.821 5.108a1.4 1.4 0 0 0 1.96.283z" fill="#EA4335"/><path d="M119.762 30.012l2.7-2.7a2.397 2.397 0 0 0 .599-2.3l-1-3.7a2.19 2.19 0 0 0-1.599-1.6l-3.7-1a2.402 2.402 0 0 0-2.3.6l-2.7 2.7a2.204 2.204 0 0 0-.6 2.3l.899 3.7a2.506 2.506 0 0 0 1.701 1.6l3.7 1a2.398 2.398 0 0 0 2.3-.6z" fill="#EE5FFA"/><path d="M132.762 63.912l-7.5 6a1.703 1.703 0 0 1-2.701-1l-1.5-9.5a1.71 1.71 0 0 1 .615-1.494 1.697 1.697 0 0 1 1.586-.306l9 3.4a1.81 1.81 0 0 1 1.002 1.32 1.796 1.796 0 0 1-.502 1.58zM51.61 91.497a9.1 9.1 0 0 0-3.7-12.3l-18.8-10a9.1 9.1 0 0 0-12.3 3.7v0a9.1 9.1 0 0 0 3.8 12.3l18.7 10.1a9.099 9.099 0 0 0 12.3-3.8z" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M51.61 91.497a9.1 9.1 0 0 0-3.7-12.3l-18.8-10a9.1 9.1 0 0 0-12.3 3.7v0a9.1 9.1 0 0 0 3.8 12.3l18.7 10.1a9.099 9.099 0 0 0 12.3-3.8z" stroke="#FBBC04" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M107.062 75.912a3.995 3.995 0 0 1-1.6 2.8 3.302 3.302 0 0 1-3.8.3M126.562 123.312a28.303 28.303 0 0 0-.8 3.9c-.7 4.7.3 7.1.1 11.7a32.128 32.128 0 0 1-1 6.2M90.117 129.512c1.1 5.2 2.1 10.4 3.1 15.6" stroke="#4285F4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M160.662 80.112a14.482 14.482 0 0 0-3.2 3.7c-4.2 7.1 1 16.4 1.5 17.3" stroke="#8AB4F8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M160.662 80.112a32.915 32.915 0 0 0 6-5.6c4.4-4.9 5.9-9.3 8.6-9.1a2.837 2.837 0 0 1 2.2 1.3c1 1.7-.2 4.7-3.3 7.4" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M192.494 65.157c-.541-.81-1.251-1.307-2.08-1.459-.794-.146-1.563.049-2.215.345-1.274.58-2.418 1.694-3.095 2.515a26.207 26.207 0 0 1-8.153 6.065 10.465 10.465 0 0 0 1.316-2.19c.626-1.445.785-2.99.056-4.228a3.836 3.836 0 0 0-2.995-1.79c-.987-.072-1.824.292-2.547.858-.7.548-1.344 1.324-1.983 2.177-.369.493-.744 1.023-1.141 1.582-1.021 1.44-2.183 3.078-3.739 4.812a31.907 31.907 0 0 1-5.842 5.458 1 1 0 0 0 1.171 1.62 33.885 33.885 0 0 0 6.171-5.756c1.634-1.821 2.9-3.606 3.934-5.064.378-.533.725-1.022 1.048-1.454.624-.834 1.136-1.426 1.614-1.8.453-.355.814-.466 1.174-.439l.014.001c.56.034 1.101.35 1.405.822.265.463.319 1.265-.176 2.408-.489 1.13-1.458 2.44-2.926 3.718a.997.997 0 0 0-.342.798c-.778.24-1.571.43-2.374.57a.999.999 0 1 0 .345 1.97 21.503 21.503 0 0 0 5.329-1.668 28.204 28.204 0 0 0 10.172-7.182c.586-.715 1.506-1.58 2.392-1.982.434-.198.774-.245 1.026-.199.206.038.461.157.733.538.054.151.06.422-.088.874-.131.403-.327.788-.517 1.162l-.093.183c-1.172 1.866-3.79 3.847-6.693 5.596-2.875 1.732-5.858 3.138-7.537 3.879a1 1 0 0 0 .807 1.83c1.721-.76 4.788-2.204 7.763-3.996 2.789-1.68 6.113-3.787 7.713-6.743.115-.212.282-.39.485-.519a1.425 1.425 0 0 1 1.884.37c.263.493.286 1.065.101 1.729-.194.692-.596 1.409-1.062 2.062-3.631 5.063-9.006 8.16-15.258 9.92a1 1 0 1 0 .541 1.925c6.41-1.803 12.128-5.006 16.09-10.334a.992.992 0 0 0 .287-.299c.527-.841 1.781-.921 2.416-.174.182.361.198.826-.001 1.432-.212.646-.636 1.342-1.149 1.994l-.023.03c-3.525 4.86-8.896 7.864-15.065 9.626a1 1 0 1 0 .549 1.923c6.174-1.763 11.792-4.779 15.66-9.742.246.081.473.216.664.394.485.503.562 1.048.389 1.692-.19.71-.68 1.47-1.253 2.151l-.004.005c-4.233 5.099-11.823 9.304-18.364 10.967a.994.994 0 0 0-.388.188c-.743.59-.152 2.162-.04 2.845.198 1.217.472 2.984.771 5.174a1 1 0 1 0 1.981-.27 202.91 202.91 0 0 0-.947-6.237c6.644-1.88 14.157-6.128 18.524-11.387.646-.767 1.35-1.794 1.651-2.916.318-1.183.188-2.51-.904-3.625l-.019-.018-.006-.006a3.8 3.8 0 0 0-.862-.617c.299-.487.559-1.01.735-1.547.315-.96.394-2.078-.194-3.108a1.028 1.028 0 0 0-.076-.114 3.464 3.464 0 0 0-2.731-1.346l.014-.05c.285-1.02.314-2.186-.304-3.284a.706.706 0 0 0-.043-.07c-.757-1.122-2.1-1.65-3.372-1.448a2.55 2.55 0 0 0-.262-1.04 1.078 1.078 0 0 0-.062-.107z" fill="#8AB4F8"/><path d="M134.667 100.112l61.277.202a1.931 1.931 0 0 1 1.691.784 1.927 1.927 0 0 1 .234 1.85l-13.364 41.295a2 2 0 0 1-1.995 1.869h-72.8a2 2 0 0 1 0-4h8.252l13.058-39.367a4.055 4.055 0 0 1 3.647-2.633z" fill="#4285F4"/><path d="M144.231 146.627c1.7-2 5.16-5.042 6.96-7.042 2.761-3.068-.429-5.272-3.729-4.138-7.393 3.242-18.898 11.589-20.332 13.79-1.434 2.201 8.671 22.526 8.671 22.526 11.661-4.69 40.397-7.481 36.461-17.151 0-1.481 2.517-2.394 2.517-5.375-.172-1.189-1.201-2.382-1.295-3.555.861-1.938.632-3.199 0-4.495-.582-1.193-1.837-.899-2.926-1.893-.237-2.415 1.183-5.066-3.084-5.328-4.267-.262-9.133 6.765-11.839 7.684-2.706.918-11.404 4.977-11.404 4.977z" fill="#fff"/><path d="M144.231 146.627c1.7-2 5.16-5.042 6.96-7.042 2.761-3.068-.429-5.272-3.729-4.138-7.393 3.242-18.898 11.589-20.332 13.79-1.434 2.201 8.671 22.526 8.671 22.526 11.661-4.69 40.397-7.481 36.461-17.151 0-1.481 2.517-2.394 2.517-5.375-.172-1.189-1.201-2.382-1.295-3.555.861-1.938.632-3.199 0-4.495-.582-1.193-1.837-.899-2.926-1.893-.237-2.415 1.183-5.066-3.084-5.328-4.267-.262-9.133 6.765-11.839 7.684-2.706.918-11.404 4.977-11.404 4.977z" fill="#D2E3FC"/><path d="M162.662 137.303a14.469 14.469 0 0 1 3.885-2.419c1.049-.24 2.067.084 2.638.64.283.276.443.594.48.926.037.329-.038.753-.37 1.262l-.022.035a10.297 10.297 0 0 1-1.744 2.204l-.014.014c-6.619 6.618-11.303 9.833-19.949 12.781a.914.914 0 1 0 .591 1.732c7.861-2.68 12.61-5.616 18.241-10.882a.902.902 0 0 0 .321-.159l2.59-1.992c.745-.537 1.672-.797 2.414-.698.36.048.659.177.89.379.227.198.437.508.559 1 .277 1.189-.487 2.565-1.731 3.713l-.015.014-.011.011c-1 .999-2.093 1.899-3.265 2.689l-.013.009a73.26 73.26 0 0 1-9.676 5.727c-1.311.631-2.679 1.169-4.087 1.705l-.712.27c-1.176.445-2.379.9-3.561 1.407a.914.914 0 1 0 .721 1.682c1.143-.49 2.301-.928 3.477-1.373l.725-.275c1.416-.539 2.847-1.101 4.235-1.769l.007-.003a75.2 75.2 0 0 0 9.914-5.867 23.95 23.95 0 0 0 3.072-2.463.901.901 0 0 0 .21.024c.491 0 .9.272 1.195.781.299.516.407 1.171.314 1.689a5.686 5.686 0 0 1-2.228 3.061l-.03.022c-4.493 3.369-9.292 5.079-14.967 7.101-1.224.437-2.49.887-3.802 1.373a.915.915 0 1 0 .635 1.716c1.261-.466 2.493-.905 3.7-1.334 5.07-1.804 9.676-3.442 13.968-6.289a.95.95 0 0 0 .025.181 3.98 3.98 0 0 1-1.293 3.978 12.49 12.49 0 0 1-4.557 2.323 1.048 1.048 0 0 0-.074.023c-8.135 2.965-10.915 3.574-15.475 4.574-2.021.443-4.392.963-7.733 1.798a.848.848 0 0 0-.101.032l-6.9 2.6a.915.915 0 1 0 .645 1.712l6.851-2.581a245.685 245.685 0 0 1 7.568-1.759c4.609-1.011 7.511-1.647 15.736-4.643a14.313 14.313 0 0 0 5.197-2.661l.016-.013a5.817 5.817 0 0 0 1.899-5.815.915.915 0 0 0-.54-.63l.285-.211a7.516 7.516 0 0 0 2.944-4.088.65.65 0 0 0 .019-.076c.195-.977.001-2.103-.518-3-.341-.59-.853-1.122-1.523-1.427.932-1.193 1.619-2.726 1.234-4.362l-.002-.009c-.201-.814-.589-1.47-1.134-1.946-.542-.473-1.191-.725-1.851-.814a4.593 4.593 0 0 0-1.282.018l.157-.257c.519-.803.734-1.64.644-2.446-.09-.808-.478-1.503-1.021-2.033-1.069-1.043-2.781-1.501-4.423-1.091a.849.849 0 0 0-.129.043 16.301 16.301 0 0 0-4.438 2.744 59.867 59.867 0 0 1-13.44 8.217l.319-.313.011-.011c1.267-1.242 2.551-2.501 3.463-3.463.96-1.013 1.551-2.358 1.389-3.63a2.918 2.918 0 0 0-.883-1.764c-.511-.485-1.197-.797-2.015-.937a5.423 5.423 0 0 0-2.766.241l-.009.004a24.642 24.642 0 0 0-3.736 1.763c-5.111 2.907-12.85 7.648-18.223 12.019a.915.915 0 1 0 1.155 1.419c5.252-4.272 12.887-8.955 17.976-11.849l.006-.004a22.808 22.808 0 0 1 3.456-1.632 3.588 3.588 0 0 1 1.828-.158l.004.001c.543.092.87.276 1.062.459.187.178.295.4.33.67.073.572-.197 1.395-.903 2.14-.885.934-2.146 2.171-3.426 3.426l-.011.01c-1.267 1.242-2.551 2.501-3.463 3.464a.915.915 0 0 0 1.1 1.434.938.938 0 0 0 .177-.045 61.739 61.739 0 0 0 17.788-10.069z" fill="#4285F4"/><path fill-rule="evenodd" clip-rule="evenodd" d="M87.826 72.614a11.15 11.15 0 0 1-6.664 2.198c-6.186 0-11.2-5.014-11.2-11.2 0-6.186 5.014-11.2 11.2-11.2 6.185 0 11.2 5.014 11.2 11.2 0 1.16-.177 2.279-.504 3.33a12.248 12.248 0 0 1 7.604-2.63c6.793 0 12.3 5.507 12.3 12.3 0 3.738-1.668 7.087-4.3 9.342l2.018 9.033c5.22.55 19.544 2.884 31.295 14.924a50.004 50.004 0 0 1 12.754 22.448.999.999 0 1 1-1.935.506 47.991 47.991 0 0 0-12.245-21.551l-.003-.004c-11.21-11.488-24.887-13.778-29.989-14.325a13.615 13.615 0 0 1-1.073 2.288c-.851 1.432-2.228 3.064-4.369 3.874-2.163.819-4.872.554-7.337-.591a12.905 12.905 0 0 1-5.82-5.438c-5.013 1.087-17.254 4.55-25.901 15.9-11.226 14.74-10.352 34.895-2.934 47.486l.002.003c11.556 19.742 43.434 28.482 72.969 13.24l-11.301-27.824-30.315.489a1 1 0 0 1-.032-2l30.999-.5a1 1 0 0 1 .943.624l11.9 29.3a1 1 0 0 1-.457 1.259c-30.443 16.182-64.092 7.502-76.432-13.577-7.781-13.209-8.706-34.253 3.067-49.712 9.293-12.197 22.5-15.688 27.43-16.72a1.008 1.008 0 0 1 .31-.141l3.305-7.16c-4.22-1.95-7.15-6.22-7.15-11.173 0-1.4.234-2.744.665-3.998zm1.394 5.102c.551 5.17 4.926 9.196 10.242 9.196 5.688 0 10.3-4.612 10.3-10.3 0-5.18-3.824-9.467-8.802-10.192a18.104 18.104 0 0 1-5.046 7.634c-2.096 1.88-4.11 2.984-6.694 3.662zm8.2 23.026a10.901 10.901 0 0 1-4.832-4.45l3.582-7.761.024-.058c1.04.286 2.136.439 3.268.439 2.275 0 4.405-.618 6.233-1.695l1.931 8.647a11.758 11.758 0 0 1-1.062 2.387c-.723 1.218-1.797 2.436-3.356 3.026-1.537.581-3.678.446-5.788-.535z" fill="#4285F4"/><path d="M160.099 79.165a14.476 14.476 0 0 0-3.569 4c-4.487 7.6.917 17.3 1.529 18.3a.916.916 0 0 0 .379.391c.894.487 1.926-.327 1.457-1.291-.612-.9-5.507-9.7-1.632-16.2a12.476 12.476 0 0 1 3.06-3.5l2.957-2.4-1.224-1.6-2.957 2.3z" fill="#4285F4"/><path d="M33.97 93.55l4.865 2.627.008.004a10.1 10.1 0 0 0 13.65-4.215 10.1 10.1 0 0 0-4.108-13.649l-4.23-2.25c-.415.533-.823 1.07-1.224 1.615l4.504 2.395.003.002a8.1 8.1 0 0 1 3.29 10.947l-.002.005a8.101 8.101 0 0 1-10.945 3.384l-5.018-2.71a76.35 76.35 0 0 0-.792 1.845z" fill="#fff"/><path d="M166.114 49.216h-.139a.761.761 0 0 1-.763-.208l-6.656-5.2a1.042 1.042 0 0 1 .477-1.862 1.04 1.04 0 0 1 .771.198l5.824 4.645 7.696-10.469a12.064 12.064 0 0 0-16.362-.277l-.139.138a12.07 12.07 0 0 0-3.734 13.142 12.054 12.054 0 0 0 6.26 7.046 12.064 12.064 0 0 0 13.49-2.161l.139-.139a11.994 11.994 0 0 0 1.733-16.154L166.668 48.8c-.135.194-.33.34-.554.416z" fill="#34A853"/><path d="M177.415 32.507a1.11 1.11 0 0 0-1.456.208l-2.635 3.605.694.763.693.831 2.912-3.951a1.112 1.112 0 0 0-.208-1.456z" fill="#8AB4F8"/></svg>
diff --git a/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_closing_dark.svg b/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_closing_dark.svg
new file mode 100644
index 0000000..62106251
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/images/switch_access_setup_guide_closing_dark.svg
@@ -0,0 +1 @@
+<svg width="217" height="216" viewBox="0 0 217 216" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M164.962 79.312a75.104 75.104 0 0 0-94.711-24.448 75.1 75.1 0 1 0 108.211 67.448 74.812 74.812 0 0 0-13.5-43z" fill="#8AB4F8" fill-opacity=".4"/><path d="M130.747 46.332l5.125-3.834a1.4 1.4 0 0 0 .282-1.96l-3.822-5.108a1.4 1.4 0 0 0-1.959-.283l-5.125 3.834a1.4 1.4 0 0 0-.282 1.96l3.821 5.108a1.4 1.4 0 0 0 1.96.283z" fill="#EA4335"/><path d="M119.762 30.012l2.7-2.7a2.397 2.397 0 0 0 .599-2.3l-1-3.7a2.19 2.19 0 0 0-1.599-1.6l-3.7-1a2.402 2.402 0 0 0-2.3.6l-2.7 2.7a2.204 2.204 0 0 0-.6 2.3l.899 3.7a2.506 2.506 0 0 0 1.701 1.6l3.7 1a2.398 2.398 0 0 0 2.3-.6z" fill="#F882FE"/><path d="M132.762 63.912l-7.5 6a1.703 1.703 0 0 1-2.701-1l-1.5-9.5a1.71 1.71 0 0 1 .615-1.494 1.697 1.697 0 0 1 1.586-.306l9 3.4a1.81 1.81 0 0 1 1.002 1.32 1.796 1.796 0 0 1-.502 1.58zM51.61 91.497a9.1 9.1 0 0 0-3.7-12.3l-18.8-10a9.1 9.1 0 0 0-12.3 3.7v0a9.1 9.1 0 0 0 3.8 12.3l18.7 10.1a9.099 9.099 0 0 0 12.3-3.8z" stroke="#323336" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M51.61 91.497a9.1 9.1 0 0 0-3.7-12.3l-18.8-10a9.1 9.1 0 0 0-12.3 3.7v0a9.1 9.1 0 0 0 3.8 12.3l18.7 10.1a9.099 9.099 0 0 0 12.3-3.8z" stroke="#FCC934" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M107.062 75.912a3.995 3.995 0 0 1-1.6 2.8 3.302 3.302 0 0 1-3.8.3M126.562 123.312a28.303 28.303 0 0 0-.8 3.9c-.7 4.7.3 7.1.1 11.7a32.128 32.128 0 0 1-1 6.2M90.117 129.512c1.1 5.2 2.1 10.4 3.1 15.6" stroke="#669DF6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M192.494 65.157c-.541-.81-1.251-1.307-2.08-1.459-.794-.146-1.563.049-2.215.345-1.274.58-2.418 1.694-3.095 2.515a26.207 26.207 0 0 1-8.153 6.065 10.465 10.465 0 0 0 1.316-2.19c.626-1.445.785-2.99.056-4.228a3.836 3.836 0 0 0-2.995-1.79c-.987-.072-1.824.292-2.547.858-.7.548-1.344 1.324-1.983 2.177-.369.493-.744 1.023-1.141 1.582-1.021 1.44-2.183 3.078-3.739 4.812a31.907 31.907 0 0 1-5.842 5.458 1 1 0 0 0 1.171 1.62 33.885 33.885 0 0 0 6.171-5.756c1.634-1.821 2.9-3.606 3.934-5.064.378-.533.725-1.022 1.048-1.454.624-.834 1.136-1.426 1.614-1.8.453-.355.814-.466 1.174-.439l.014.001c.56.034 1.101.35 1.405.822.265.463.319 1.265-.176 2.408-.489 1.13-1.458 2.44-2.926 3.718a.997.997 0 0 0-.342.798c-.778.24-1.571.43-2.374.57a.999.999 0 1 0 .345 1.97 21.503 21.503 0 0 0 5.329-1.668 28.204 28.204 0 0 0 10.172-7.182c.586-.715 1.506-1.58 2.392-1.982.434-.198.774-.245 1.026-.199.206.038.461.157.733.538.054.151.06.422-.088.874-.131.403-.327.788-.517 1.162l-.093.183c-1.172 1.866-3.79 3.847-6.693 5.596-2.875 1.732-5.858 3.138-7.537 3.879a1 1 0 0 0 .807 1.83c1.721-.76 4.788-2.204 7.763-3.996 2.789-1.68 6.113-3.787 7.713-6.743.115-.212.282-.39.485-.519a1.425 1.425 0 0 1 1.884.37c.263.493.286 1.065.101 1.729-.194.692-.596 1.409-1.062 2.062-3.631 5.063-9.006 8.16-15.258 9.92a1 1 0 1 0 .541 1.925c6.41-1.803 12.128-5.006 16.09-10.334a.992.992 0 0 0 .287-.299c.527-.841 1.781-.921 2.416-.174.182.361.198.826-.001 1.432-.212.646-.636 1.342-1.149 1.994l-.023.03c-3.525 4.86-8.896 7.864-15.065 9.626a1 1 0 1 0 .549 1.923c6.174-1.763 11.792-4.779 15.66-9.742.246.081.473.216.664.394.485.503.562 1.048.389 1.692-.19.71-.68 1.47-1.253 2.151l-.004.005c-4.233 5.099-11.823 9.304-18.364 10.967a.994.994 0 0 0-.388.188c-.743.59-.152 2.162-.04 2.845.198 1.217.472 2.984.771 5.174a1 1 0 1 0 1.981-.27 202.91 202.91 0 0 0-.947-6.237c6.644-1.88 14.157-6.128 18.524-11.387.646-.767 1.35-1.794 1.651-2.916.318-1.183.188-2.51-.904-3.625l-.019-.018-.006-.006a3.8 3.8 0 0 0-.862-.617c.299-.487.559-1.01.735-1.547.315-.96.394-2.078-.194-3.108a1.028 1.028 0 0 0-.076-.114 3.464 3.464 0 0 0-2.731-1.346l.014-.05c.285-1.02.314-2.186-.304-3.284a.706.706 0 0 0-.043-.07c-.757-1.122-2.1-1.65-3.372-1.448a2.55 2.55 0 0 0-.262-1.04 1.078 1.078 0 0 0-.062-.107z" fill="#1A73E8" fill-opacity=".4"/><path d="M160.662 80.112a14.482 14.482 0 0 0-3.2 3.7c-4.2 7.1 1 16.4 1.5 17.3" stroke="#1A73E8" stroke-opacity=".4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M134.667 100.112l61.277.202a1.931 1.931 0 0 1 1.691.784 1.927 1.927 0 0 1 .234 1.85l-13.364 41.295a2 2 0 0 1-1.995 1.869h-72.8a2 2 0 0 1 0-4h8.252l13.058-39.367a4.055 4.055 0 0 1 3.647-2.633z" fill="#669DF6"/><path d="M144.231 146.627c1.7-2 5.16-5.042 6.96-7.042 2.761-3.068-.429-5.272-3.729-4.138-7.393 3.242-18.898 11.589-20.332 13.79-1.434 2.201 8.671 22.526 8.671 22.526 11.661-4.69 40.397-7.481 36.461-17.151 0-1.481 2.517-2.394 2.517-5.375-.172-1.189-1.201-2.382-1.295-3.555.861-1.938.632-3.199 0-4.495-.582-1.193-1.837-.899-2.926-1.893-.237-2.415 1.183-5.066-3.084-5.328-4.267-.262-9.133 6.765-11.839 7.684-2.706.918-11.404 4.977-11.404 4.977z" fill="#323336"/><path d="M144.231 146.627c1.7-2 5.16-5.042 6.96-7.042 2.761-3.068-.429-5.272-3.729-4.138-7.393 3.242-18.898 11.589-20.332 13.79-1.434 2.201 8.671 22.526 8.671 22.526 11.661-4.69 40.397-7.481 36.461-17.151 0-1.481 2.517-2.394 2.517-5.375-.172-1.189-1.201-2.382-1.295-3.555.861-1.938.632-3.199 0-4.495-.582-1.193-1.837-.899-2.926-1.893-.237-2.415 1.183-5.066-3.084-5.328-4.267-.262-9.133 6.765-11.839 7.684-2.706.918-11.404 4.977-11.404 4.977z" fill="#8AB4F8" fill-opacity=".4"/><path d="M162.662 137.303a14.469 14.469 0 0 1 3.885-2.419c1.049-.24 2.067.084 2.638.64.283.276.443.594.48.926.037.329-.038.753-.37 1.262l-.022.035a10.297 10.297 0 0 1-1.744 2.204l-.014.014c-6.619 6.618-11.303 9.833-19.949 12.781a.914.914 0 1 0 .591 1.732c7.861-2.68 12.61-5.616 18.241-10.882a.902.902 0 0 0 .321-.159l2.59-1.992c.745-.537 1.672-.797 2.414-.698.36.048.659.177.89.379.227.198.437.508.559 1 .277 1.189-.487 2.565-1.731 3.713l-.015.014-.011.011c-1 .999-2.093 1.899-3.265 2.689l-.013.009a73.26 73.26 0 0 1-9.676 5.727c-1.311.631-2.679 1.169-4.087 1.705l-.712.27c-1.176.445-2.379.9-3.561 1.407a.914.914 0 1 0 .721 1.682c1.143-.49 2.301-.928 3.477-1.373l.725-.275c1.416-.539 2.847-1.101 4.235-1.769l.007-.003a75.2 75.2 0 0 0 9.914-5.867 23.95 23.95 0 0 0 3.072-2.463.901.901 0 0 0 .21.024c.491 0 .9.272 1.195.781.299.516.407 1.171.314 1.689a5.686 5.686 0 0 1-2.228 3.061l-.03.022c-4.493 3.369-9.292 5.079-14.967 7.101-1.224.437-2.49.887-3.802 1.373a.915.915 0 1 0 .635 1.716c1.261-.466 2.493-.905 3.7-1.334 5.07-1.804 9.676-3.442 13.968-6.289a.95.95 0 0 0 .025.181 3.98 3.98 0 0 1-1.293 3.978 12.49 12.49 0 0 1-4.557 2.323 1.048 1.048 0 0 0-.074.023c-8.135 2.965-10.915 3.574-15.475 4.574-2.021.443-4.392.963-7.733 1.798a.848.848 0 0 0-.101.032l-6.9 2.6a.915.915 0 1 0 .645 1.712l6.851-2.581a245.685 245.685 0 0 1 7.568-1.759c4.609-1.011 7.511-1.647 15.736-4.643a14.313 14.313 0 0 0 5.197-2.661l.016-.013a5.817 5.817 0 0 0 1.899-5.815.915.915 0 0 0-.54-.63l.285-.211a7.516 7.516 0 0 0 2.944-4.088.65.65 0 0 0 .019-.076c.195-.977.001-2.103-.518-3-.341-.59-.853-1.122-1.523-1.427.932-1.193 1.619-2.726 1.234-4.362l-.002-.009c-.201-.814-.589-1.47-1.134-1.946-.542-.473-1.191-.725-1.851-.814a4.593 4.593 0 0 0-1.282.018l.157-.257c.519-.803.734-1.64.644-2.446-.09-.808-.478-1.503-1.021-2.033-1.069-1.043-2.781-1.501-4.423-1.091a.849.849 0 0 0-.129.043 16.301 16.301 0 0 0-4.438 2.744 59.867 59.867 0 0 1-13.44 8.217l.319-.313.011-.011c1.267-1.242 2.551-2.501 3.463-3.463.96-1.013 1.551-2.358 1.389-3.63a2.918 2.918 0 0 0-.883-1.764c-.511-.485-1.197-.797-2.015-.937a5.423 5.423 0 0 0-2.766.241l-.009.004a24.642 24.642 0 0 0-3.736 1.763c-5.111 2.907-12.85 7.648-18.223 12.019a.915.915 0 1 0 1.155 1.419c5.252-4.272 12.887-8.955 17.976-11.849l.006-.004a22.808 22.808 0 0 1 3.456-1.632 3.588 3.588 0 0 1 1.828-.158l.004.001c.543.092.87.276 1.062.459.187.178.295.4.33.67.073.572-.197 1.395-.903 2.14-.885.934-2.146 2.171-3.426 3.426l-.011.01c-1.267 1.242-2.551 2.501-3.463 3.464a.915.915 0 0 0 1.1 1.434.938.938 0 0 0 .177-.045 61.739 61.739 0 0 0 17.788-10.069z" fill="#669DF6"/><path fill-rule="evenodd" clip-rule="evenodd" d="M87.826 72.614a11.15 11.15 0 0 1-6.664 2.198c-6.186 0-11.2-5.014-11.2-11.2 0-6.186 5.014-11.2 11.2-11.2 6.185 0 11.2 5.014 11.2 11.2 0 1.16-.177 2.279-.504 3.33a12.248 12.248 0 0 1 7.604-2.63c6.793 0 12.3 5.507 12.3 12.3 0 3.738-1.668 7.087-4.3 9.342l2.018 9.033c5.22.55 19.544 2.884 31.295 14.924a50.004 50.004 0 0 1 12.754 22.448.999.999 0 1 1-1.935.506 47.991 47.991 0 0 0-12.245-21.551l-.003-.004c-11.21-11.488-24.887-13.778-29.989-14.325a13.615 13.615 0 0 1-1.073 2.288c-.851 1.432-2.228 3.064-4.369 3.874-2.163.819-4.872.554-7.337-.591a12.905 12.905 0 0 1-5.82-5.438c-5.013 1.087-17.254 4.55-25.901 15.9-11.226 14.74-10.352 34.895-2.934 47.486l.002.003c11.556 19.742 43.434 28.482 72.969 13.24l-11.301-27.824-30.315.489a1 1 0 0 1-.032-2l30.999-.5a1 1 0 0 1 .943.624l11.9 29.3a1 1 0 0 1-.457 1.259c-30.443 16.182-64.092 7.502-76.432-13.577-7.781-13.209-8.706-34.253 3.067-49.712 9.293-12.197 22.5-15.688 27.43-16.72a1.008 1.008 0 0 1 .31-.141l3.305-7.16c-4.22-1.95-7.15-6.22-7.15-11.173 0-1.4.234-2.744.665-3.998zm1.394 5.102c.551 5.17 4.926 9.196 10.242 9.196 5.688 0 10.3-4.612 10.3-10.3 0-5.18-3.824-9.467-8.802-10.192a18.104 18.104 0 0 1-5.046 7.634c-2.096 1.88-4.11 2.984-6.694 3.662zm8.2 23.026a10.901 10.901 0 0 1-4.832-4.45l3.582-7.761.024-.058c1.04.286 2.136.439 3.268.439 2.275 0 4.405-.618 6.233-1.695l1.931 8.647a11.758 11.758 0 0 1-1.062 2.387c-.723 1.218-1.797 2.436-3.356 3.026-1.537.581-3.678.446-5.788-.535z" fill="#669DF6"/><path d="M160.099 79.165a14.476 14.476 0 0 0-3.569 4c-4.487 7.6.917 17.3 1.529 18.3a.916.916 0 0 0 .379.391c.894.487 1.926-.327 1.457-1.291-.612-.9-5.507-9.7-1.632-16.2a12.476 12.476 0 0 1 3.06-3.5l2.957-2.4-1.224-1.6-2.957 2.3z" fill="#669DF6"/><path d="M33.97 93.55l4.865 2.627.008.004a10.1 10.1 0 0 0 13.65-4.215 10.1 10.1 0 0 0-4.108-13.649l-4.23-2.25c-.415.533-.823 1.07-1.224 1.615l4.504 2.395.003.002a8.1 8.1 0 0 1 3.29 10.947l-.002.005a8.101 8.101 0 0 1-10.945 3.384l-5.018-2.71a76.35 76.35 0 0 0-.792 1.845z" fill="#323336"/><path d="M166.114 49.216h-.139a.761.761 0 0 1-.763-.208l-6.656-5.2a1.042 1.042 0 0 1 .477-1.862 1.04 1.04 0 0 1 .771.198l5.824 4.645 7.696-10.469a12.064 12.064 0 0 0-16.362-.277l-.139.138a12.07 12.07 0 0 0-3.734 13.142 12.054 12.054 0 0 0 6.26 7.046 12.064 12.064 0 0 0 13.49-2.161l.139-.139a11.994 11.994 0 0 0 1.733-16.154L166.668 48.8c-.135.194-.33.34-.554.416z" fill="#5BB974"/><path d="M177.415 32.507a1.11 1.11 0 0 0-1.456.208l-2.635 3.605.694.763.693.831 2.912-3.951a1.112 1.112 0 0 0-.208-1.456z" fill="#1A73E8" fill-opacity=".4"/></svg>
diff --git a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.html b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.html
index 64ca971..a4b9153 100644
--- a/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_setup_guide_dialog.html
@@ -79,6 +79,23 @@
       url(chrome://os-settings/images/switch_access_setup_guide_closing.svg);
   }
 
+  @media(prefers-color-scheme: dark) {
+    #intro .illustration {
+       background-image:
+          url(chrome://os-settings/images/computer_and_bluetooth_switch_dark.svg);
+    }
+
+    #auto-scan-enabled .illustration {
+      background-image:
+        url(chrome://os-settings/images/switch_access_setup_guide_auto_scan_enabled_dark.svg);
+    }
+
+    #closing .illustration {
+      background-image:
+        url(chrome://os-settings/images/switch_access_setup_guide_closing_dark.svg);
+    }
+  }
+
   #button-container {
     padding: 24px;
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
index 05e746b4..b279ea8 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
@@ -54,6 +54,21 @@
     text-overflow: ellipsis;
   }
 
+  .middle.two-line-or-more {
+    min-height: calc(var(--settings-row-two-line-min-height)
+                      - 2*var(--account-item-padding-size));
+    padding-bottom: var(--account-item-padding-size);
+    padding-top: var(--account-item-padding-size);
+  }
+
+  .middle.two-line-or-more>.flex {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    min-height: calc(var(--settings-row-two-line-min-height)
+                      - 2*var(--account-item-padding-size));
+  }
+
   .secondary-accounts-policy-indicator {
     margin-inline-end: 12px;
   }
@@ -306,7 +321,7 @@
           <span class="error-badge"></span>
         </template>
       </div>
-      <div class="middle two-line no-min-width">
+      <div class="middle two-line-or-more no-min-width">
         <div class="flex text-elide">
           <!-- If account is signed in, display the full name -->
           <template is="dom-if" if="[[item.isSignedIn]]">
@@ -319,17 +334,18 @@
               [[getAccountManagerSignedOutName_(item.unmigrated)]]
             </span>
           </template>
+          <!-- Display email -->
           <div class="secondary" id="email-[[index]]"
               aria-hidden="true">[[item.email]]</div>
+          <!-- Display ARC status -->
+          <template is="dom-if" if="[[isArcAccountRestrictionsEnabled_]]">
+            <span class="arc-availability secondary" id="arc-status-[[index]]"
+                aria-hidden="true" hidden$="[[item.isAvailableInArc]]">
+              $i18n{accountNotUsedInArcLabel}
+            </span>
+          </template>
         </div>
       </div>
-      <!-- Display ARC status -->
-      <template is="dom-if" if="[[isArcAccountRestrictionsEnabled_]]">
-        <span class="arc-availability secondary" id="arc-status-[[index]]"
-            aria-hidden="true" hidden$="[[item.isAvailableInArc]]">
-          $i18n{accountNotUsedInArcLabel}
-        </span>
-      </template>
       <template is="dom-if"
           if="[[shouldShowReauthenticationButton_(item)]]">
         <cr-button title="[[getAccountManagerSignedOutTitle_(item)]]"
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_cookies_fragment.html b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_cookies_fragment.html
index 2ab77a6..05c1c41 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_cookies_fragment.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_cookies_fragment.html
@@ -16,7 +16,8 @@
       name="[[cookiePrimarySettingEnum_.BLOCK_THIRD_PARTY_INCOGNITO]]"
       label="$i18n{privacyReviewCookiesCardBlockTpcIncognitoSubheader}"
       expand-aria-label=
-          "$i18n{cookiePageBlockThirdIncognitoExpandA11yLabel}">
+          "$i18n{cookiePageBlockThirdIncognitoExpandA11yLabel}"
+      on-click="onCookies3PIncognitoClick_">
     <div slot="collapse" class="description-wrapper-radio two-column">
       <div class="description-column description-column-first">
         <div class=description-header>
@@ -50,7 +51,8 @@
       pref="[[prefs.generated.cookie_primary_setting]]"
       name="[[cookiePrimarySettingEnum_.BLOCK_THIRD_PARTY]]"
       label="$i18n{privacyReviewCookiesCardBlockTpcSubheader}"
-      expand-aria-label="$i18n{cookiePageBlockThirdExpandA11yLabel}">
+      expand-aria-label="$i18n{cookiePageBlockThirdExpandA11yLabel}"
+      on-click="onCookies3PClick_">
     <div slot="collapse" class="description-wrapper-radio two-column">
       <div class="description-column description-column-first">
         <div class=description-header>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_cookies_fragment.ts b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_cookies_fragment.ts
index 51292e3..6f8f0f77 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_cookies_fragment.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_cookies_fragment.ts
@@ -84,6 +84,16 @@
     }
     this.metricsBrowserProxy_.recordPrivacyGuideSettingsStatesHistogram(state!);
   }
+
+  private onCookies3PIncognitoClick_() {
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.PrivacyGuide.ChangeCookiesBlock3PIncognito');
+  }
+
+  private onCookies3PClick_() {
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.PrivacyGuide.ChangeCookiesBlock3P');
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_history_sync_fragment.ts b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_history_sync_fragment.ts
index 2cf43d4..d02b842 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_history_sync_fragment.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_history_sync_fragment.ts
@@ -146,6 +146,13 @@
     this.syncPrefs_.typedUrlsSynced = this.historySyncVirtualPref_.value;
     this.syncPrefs_.syncAllDataTypes = this.shouldSyncAllBeOn_();
     this.syncBrowserProxy_.setSyncDatatypes(this.syncPrefs_);
+    if (this.syncPrefs_.typedUrlsSynced) {
+      this.metricsBrowserProxy_.recordAction(
+          'Settings.PrivacyGuide.ChangeHistorySyncOn');
+    } else {
+      this.metricsBrowserProxy_.recordAction(
+          'Settings.PrivacyGuide.ChangeHistorySyncOff');
+    }
   }
 
   /**
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_msbb_fragment.html b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_msbb_fragment.html
index 282f3c7..13fccb8a 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_msbb_fragment.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_msbb_fragment.html
@@ -11,7 +11,8 @@
 <div class="embedded-setting-wrapper">
   <settings-toggle-button id="urlCollectionToggle"
       pref="{{prefs.url_keyed_anonymized_data_collection.enabled}}"
-      label="$i18n{urlKeyedAnonymizedDataCollection}">
+      label="$i18n{urlKeyedAnonymizedDataCollection}"
+      on-change="onMsbbToggleClick_">
   </settings-toggle-button>
 </div>
 <div class="description-wrapper two-column">
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_msbb_fragment.ts b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_msbb_fragment.ts
index c4fc1e9..63169902 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_msbb_fragment.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_msbb_fragment.ts
@@ -70,6 +70,16 @@
     }
     this.metricsBrowserProxy_.recordPrivacyGuideSettingsStatesHistogram(state!);
   }
+
+  private onMsbbToggleClick_() {
+    if (this.getPref('url_keyed_anonymized_data_collection.enabled').value) {
+      this.metricsBrowserProxy_.recordAction(
+          'Settings.PrivacyGuide.ChangeMSBBOn');
+    } else {
+      this.metricsBrowserProxy_.recordAction(
+          'Settings.PrivacyGuide.ChangeMSBBOff');
+    }
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_safe_browsing_fragment.html b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_safe_browsing_fragment.html
index a78da34b..ef2d18a3 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_safe_browsing_fragment.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_safe_browsing_fragment.html
@@ -16,7 +16,8 @@
       name="[[safeBrowsingSettingEnum_.ENHANCED]]"
       label="$i18n{safeBrowsingEnhanced}"
       sub-label="$i18n{safeBrowsingEnhancedDesc}"
-      expand-aria-label="$i18n{safeBrowsingEnhancedExpandA11yLabel}">
+      expand-aria-label="$i18n{safeBrowsingEnhancedExpandA11yLabel}"
+      on-click="onSafeBrowsingEnhancedClick_">
       <div slot="collapse" class="description-wrapper-radio two-column">
         <div class="description-column description-column-first">
           <div class=description-header>
@@ -63,7 +64,8 @@
       name="[[safeBrowsingSettingEnum_.STANDARD]]"
       label="$i18n{safeBrowsingStandard}"
       sub-label="$i18n{safeBrowsingStandardDesc}"
-      expand-aria-label="$i18n{safeBrowsingStandardExpandA11yLabel}">
+      expand-aria-label="$i18n{safeBrowsingStandardExpandA11yLabel}"
+      on-click="onSafeBrowsingStandardClick_">
       <div slot="collapse" class="description-wrapper-radio two-column">
         <div class="description-column description-column-first">
           <div class=description-header>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_safe_browsing_fragment.ts b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_safe_browsing_fragment.ts
index a9cc5049..75c5101b 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_safe_browsing_fragment.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_review/privacy_review_safe_browsing_fragment.ts
@@ -82,6 +82,16 @@
     }
     this.metricsBrowserProxy_.recordPrivacyGuideSettingsStatesHistogram(state!);
   }
+
+  private onSafeBrowsingEnhancedClick_() {
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.PrivacyGuide.ChangeSafeBrowsingEnhanced');
+  }
+
+  private onSafeBrowsingStandardClick_() {
+    this.metricsBrowserProxy_.recordAction(
+        'Settings.PrivacyGuide.ChangeSafeBrowsingStandard');
+  }
 }
 
 customElements.define(
diff --git a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.html b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.html
index ce44c6b..9bde6ca9 100644
--- a/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.html
+++ b/chrome/browser/resources/signin/sync_confirmation/sync_confirmation_app.html
@@ -260,7 +260,7 @@
     <div id="old-footer" class="message-container secondary">
       $i18n{syncConfirmationSettingsInfo}
     </div>
-    <div class$="action-container [[isSyncForcedClass_]]">
+    <div class$="action-container [[syncOptionalClass_]]">
       <cr-button class="action-button" id="confirmButton"
           on-click="onConfirm_" consent-confirmation autofocus>
         $i18n{syncConfirmationConfirmLabel}
diff --git a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
index 665ded1..956da8b 100644
--- a/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
+++ b/chrome/browser/signin/dice_web_signin_interceptor_browsertest.cc
@@ -14,7 +14,6 @@
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/enterprise/util/managed_browser_utils.h"
 #include "chrome/browser/profiles/profile_attributes_entry.h"
@@ -1132,15 +1131,9 @@
   EXPECT_EQ(source_interceptor_delegate->fre_browser(), nullptr);
 }
 
-// Failed run on MAC CI builder. https://crbug.com/1245200
-#if BUILDFLAG(IS_MAC)
-#define MAYBE_EnterpriseSwitchAlreadyOpen DISABLED_EnterpriseSwitchAlreadyOpen
-#else
-#define MAYBE_EnterpriseSwitchAlreadyOpen EnterpriseSwitchAlreadyOpen
-#endif
 // Tests the complete profile switch flow when the profile is already loaded.
 IN_PROC_BROWSER_TEST_F(DiceWebSigninInterceptorEnterpriseBrowserTest,
-                       MAYBE_EnterpriseSwitchAlreadyOpen) {
+                       EnterpriseSwitchAlreadyOpen) {
   base::HistogramTester histogram_tester;
   // Enforce enterprise profile separation.
   profile()->GetPrefs()->SetString(prefs::kManagedAccountsSigninRestriction,
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d8a7080c..654cb1808 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1973,6 +1973,8 @@
       "app_list/search/files/zero_state_file_provider.h",
       "app_list/search/help_app_provider.cc",
       "app_list/search/help_app_provider.h",
+      "app_list/search/keyboard_shortcut_data.cc",
+      "app_list/search/keyboard_shortcut_data.h",
       "app_list/search/keyboard_shortcut_provider.cc",
       "app_list/search/keyboard_shortcut_provider.h",
       "app_list/search/keyboard_shortcut_result.cc",
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.cc b/chrome/browser/ui/app_list/search/chrome_search_result.cc
index d58789c..a1ba684 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.cc
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.cc
@@ -26,11 +26,6 @@
   SetSearchResultMetadata();
 }
 
-void ChromeSearchResult::SetIsInstalling(bool is_installing) {
-  metadata_->is_installing = is_installing;
-  SetSearchResultMetadata();
-}
-
 void ChromeSearchResult::SetTitle(const std::u16string& title) {
   metadata_->title = title;
   MaybeUpdateTitleVector();
@@ -157,13 +152,6 @@
   SetSearchResultMetadata();
 }
 
-void ChromeSearchResult::SetQueryUrl(const GURL& url) {
-  metadata_->query_url = url;
-  auto* updater = model_updater();
-  if (updater)
-    updater->SetSearchResultMetadata(id(), CloneMetadata());
-}
-
 void ChromeSearchResult::SetEquivalentResultId(
     const std::string& equivalent_result_id) {
   metadata_->equivalent_result_id = equivalent_result_id;
@@ -195,11 +183,6 @@
   SetSearchResultMetadata();
 }
 
-void ChromeSearchResult::SetNotifyVisibilityChange(
-    bool notify_visibility_change) {
-  metadata_->notify_visibility_change = notify_visibility_change;
-}
-
 void ChromeSearchResult::SetSearchResultMetadata() {
   AppListModelUpdater* updater = model_updater();
   if (updater)
diff --git a/chrome/browser/ui/app_list/search/chrome_search_result.h b/chrome/browser/ui/app_list/search/chrome_search_result.h
index 36c54e9..51c0736 100644
--- a/chrome/browser/ui/app_list/search/chrome_search_result.h
+++ b/chrome/browser/ui/app_list/search/chrome_search_result.h
@@ -88,7 +88,6 @@
   float position_priority() const { return metadata_->position_priority; }
   const Actions& actions() const { return metadata_->actions; }
   double display_score() const { return metadata_->display_score; }
-  bool is_installing() const { return metadata_->is_installing; }
   bool is_recommendation() const { return metadata_->is_recommendation; }
   const absl::optional<GURL>& query_url() const { return metadata_->query_url; }
   const absl::optional<std::string>& equivalent_result_id() const {
@@ -98,10 +97,6 @@
   const gfx::ImageSkia& chip_icon() const { return metadata_->chip_icon; }
   const ui::ImageModel& badge_icon() const { return metadata_->badge_icon; }
 
-  bool notify_visibility_change() const {
-    return metadata_->notify_visibility_change;
-  }
-
   // The following methods set Chrome side data here, and call model updater
   // interface to update Ash.
   void SetTitle(const std::u16string& title);
@@ -127,14 +122,11 @@
   void SetActions(const Actions& actions);
   void SetIsOmniboxSearch(bool is_omnibox_search);
   void SetIsRecommendation(bool is_recommendation);
-  void SetIsInstalling(bool is_installing);
-  void SetQueryUrl(const GURL& url);
   void SetEquivalentResultId(const std::string& equivalent_result_id);
   void SetIcon(const IconInfo& icon);
   void SetChipIcon(const gfx::ImageSkia& icon);
   void SetBadgeIcon(const ui::ImageModel& badge_icon);
   void SetUseBadgeIconBackground(bool use_badge_icon_background);
-  void SetNotifyVisibilityChange(bool notify_visibility_change);
 
   void SetSearchResultMetadata();
 
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
index 8693642..ed032ae 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.cc
@@ -37,11 +37,7 @@
 
 using ThrottleInterval = ZeroStateDriveProvider::ThrottleInterval;
 
-// Schemas of result IDs for the results list and suggestion chips.
-// TODO(crbug.com/1258415): kChipSchema can be removed once the new launcher is
-// launched.
-constexpr char kListSchema[] = "zero_state_drive://";
-constexpr char kChipSchema[] = "drive_chip://";
+constexpr char kSchema[] = "zero_state_drive://";
 
 // How long to wait before making the first request for results from the
 // ItemSuggestCache.
@@ -89,11 +85,6 @@
                           latency);
 }
 
-bool IsSuggestedContentEnabled(Profile* profile) {
-  return profile->GetPrefs()->GetBoolean(
-      chromeos::prefs::kSuggestedContentEnabled);
-}
-
 // Given an absolute path representing a file in the user's Drive, returns a
 // reparented version of the path within the user's drive fs mount.
 base::FilePath ReparentToDriveMount(
@@ -176,6 +167,8 @@
       !gate_on_use || launcher_used || productivity_launcher;
   LogShouldWarm(should_warm);
 
+  // TODO(crbug.com/1258415): Remove the IsSuggestedFilesEnabled dependency for
+  // cache warming.
   if (have_warmed_up_cache_ || !suggested_files_enabled_ || !should_warm)
     return;
   have_warmed_up_cache_ = true;
@@ -283,10 +276,6 @@
 
     provider_results.emplace_back(MakeListResult(
         path_or_error->get_path(), cache_results[i].prediction_reason, score));
-    if (suggested_files_enabled_ && IsSuggestedContentEnabled(profile_)) {
-      provider_results.emplace_back(
-          MakeChipResult(path_or_error->get_path(), score));
-    }
   }
 
   // We expect some files to error sometimes, but we're mainly interested in
@@ -310,7 +299,7 @@
     const absl::optional<std::string>& prediction_reason,
     const float relevance) {
   auto result = std::make_unique<FileResult>(
-      kListSchema, ReparentToDriveMount(filepath, drive_service_),
+      kSchema, ReparentToDriveMount(filepath, drive_service_),
       ash::AppListSearchResultType::kZeroStateDrive, GetDisplayType(),
       relevance, std::u16string(), FileResult::Type::kFile, profile_);
   // If it exists, override the details text with the prediction reason in the
@@ -320,16 +309,6 @@
   return result;
 }
 
-std::unique_ptr<FileResult> ZeroStateDriveProvider::MakeChipResult(
-    const base::FilePath& filepath,
-    const float relevance) {
-  return std::make_unique<FileResult>(
-      kChipSchema, ReparentToDriveMount(filepath, drive_service_),
-      ash::AppListSearchResultType::kDriveChip,
-      ash::SearchResultDisplayType::kChip, relevance, std::u16string(),
-      FileResult::Type::kFile, profile_);
-}
-
 void ZeroStateDriveProvider::OnCacheUpdated() {
   StartZeroState();
 }
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h
index 22944f1..361f830b 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h
+++ b/chrome/browser/ui/app_list/search/files/zero_state_drive_provider.h
@@ -83,10 +83,6 @@
       const base::FilePath& filepath,
       const absl::optional<std::string>& prediction_reason,
       const float relevance);
-  // TODO(crbug.com/1258415): Chip results don't exist in the new launcher.
-  // MakeChipResult can be removed after launch.
-  std::unique_ptr<FileResult> MakeChipResult(const base::FilePath& filepath,
-                                             const float relevance);
 
   // Callback for when the ItemSuggestCache updates its results.
   void OnCacheUpdated();
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
index 4974608..4e424d5 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_file_provider.cc
@@ -32,10 +32,7 @@
 namespace app_list {
 namespace {
 
-// TODO(crbug.com/1258415): kFileChipSchema can be removed once the new
-// launcher is launched.
-constexpr char kFileChipSchema[] = "file_chip://";
-constexpr char kZeroStateFileSchema[] = "zero_state_file://";
+constexpr char kSchema[] = "zero_state_file://";
 
 constexpr base::TimeDelta kSaveDelay = base::Seconds(3);
 
@@ -61,11 +58,6 @@
   return {valid, invalid};
 }
 
-bool IsSuggestedContentEnabled(Profile* profile) {
-  return profile->GetPrefs()->GetBoolean(
-      chromeos::prefs::kSuggestedContentEnabled);
-}
-
 // TODO(crbug.com/1258415): This exists to reroute results depending on which
 // launcher is enabled, and should be removed after the new launcher launch.
 ash::SearchResultDisplayType GetDisplayType() {
@@ -124,6 +116,7 @@
 void ZeroStateFileProvider::StartZeroState() {
   query_start_time_ = base::TimeTicks::Now();
   ClearResultsSilently();
+
   if (!files_ranker_) {
     return;
   }
@@ -150,7 +143,7 @@
     const auto& filepath_score = valid_results[i];
     double score = filepath_score.second;
     auto result = std::make_unique<FileResult>(
-        kZeroStateFileSchema, filepath_score.first,
+        kSchema, filepath_score.first,
         ash::AppListSearchResultType::kZeroStateFile, GetDisplayType(), score,
         std::u16string(), FileResult::Type::kFile, profile_);
     // TODO(crbug.com/1258415): Only generate thumbnails if the old launcher is
@@ -160,18 +153,6 @@
       result->RequestThumbnail(&thumbnail_loader_);
     }
     new_results.push_back(std::move(result));
-
-    // Add suggestion chip file results.
-    // TODO(crbug.com/1258415): This can be removed once the new launcher is
-    // launched.
-    if (app_list_features::IsSuggestedLocalFilesEnabled() &&
-        IsSuggestedContentEnabled(profile_)) {
-      new_results.emplace_back(std::make_unique<FileResult>(
-          kFileChipSchema, filepath_score.first,
-          ash::AppListSearchResultType::kFileChip,
-          ash::SearchResultDisplayType::kChip, filepath_score.second,
-          std::u16string(), FileResult::Type::kFile, profile_));
-    }
   }
 
   if (app_list_features::IsForceShowContinueSectionEnabled())
@@ -211,10 +192,10 @@
   constexpr int kTotalFakeFiles = 3;
   for (int i = 0; i < kTotalFakeFiles; ++i) {
     results->emplace_back(std::make_unique<FileResult>(
-        kFileChipSchema,
+        kSchema,
         base::FilePath(FILE_PATH_LITERAL(
             base::StrCat({"Fake-file-", base::NumberToString(i), ".png"}))),
-        ash::AppListSearchResultType::kFileChip,
+        ash::AppListSearchResultType::kZeroStateFile,
         ash::SearchResultDisplayType::kContinue, 0.1f, std::u16string(),
         FileResult::Type::kFile, profile_));
   }
diff --git a/chrome/browser/ui/app_list/search/files/zero_state_file_provider_unittest.cc b/chrome/browser/ui/app_list/search/files/zero_state_file_provider_unittest.cc
index 006fd4d4..73be192b 100644
--- a/chrome/browser/ui/app_list/search/files/zero_state_file_provider_unittest.cc
+++ b/chrome/browser/ui/app_list/search/files/zero_state_file_provider_unittest.cc
@@ -101,27 +101,4 @@
       UnorderedElementsAre(Title("exists_1.txt"), Title("exists_2.png")));
 }
 
-TEST_F(ZeroStateFileProviderTest, ResultsProvidedWithChips) {
-  // Enable flag - with flag enabled, we expect to receive the chip
-  // results for each file as well, so each file should be listed twice.
-  scoped_feature_list_.InitWithFeatures(
-      {app_list_features::kEnableSuggestedLocalFiles}, {});
-
-  WriteFile("exists_1.txt");
-  WriteFile("exists_2.png");
-  WriteFile("exists_3.pdf");
-
-  provider_->OnFilesOpened(
-      {OpenEvent("exists_1.txt"), OpenEvent("exists_2.png")});
-  provider_->OnFilesOpened({OpenEvent("nonexistant.txt")});
-
-  provider_->StartZeroState();
-  Wait();
-
-  EXPECT_THAT(
-      provider_->results(),
-      UnorderedElementsAre(Title("exists_1.txt"), Title("exists_2.png"),
-                           Title("exists_1.txt"), Title("exists_2.png")));
-}
-
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_data.cc b/chrome/browser/ui/app_list/search/keyboard_shortcut_data.cc
new file mode 100644
index 0000000..52c1a60a
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_data.cc
@@ -0,0 +1,17 @@
+// Copyright 2022 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/app_list/search/keyboard_shortcut_data.h"
+
+namespace app_list {
+
+// TODO(crbug.com/1290682): Continue implementation.
+KeyboardShortcutData::KeyboardShortcutData(
+    const ash::KeyboardShortcutItem& item)
+    : description_message(
+          l10n_util::GetStringUTF16(item.description_message_id)) {}
+
+KeyboardShortcutData::~KeyboardShortcutData() = default;
+
+}  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_data.h b/chrome/browser/ui/app_list/search/keyboard_shortcut_data.h
new file mode 100644
index 0000000..5038bb0
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_data.h
@@ -0,0 +1,41 @@
+// Copyright 2022 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_APP_LIST_SEARCH_KEYBOARD_SHORTCUT_DATA_H_
+#define CHROME_BROWSER_UI_APP_LIST_SEARCH_KEYBOARD_SHORTCUT_DATA_H_
+
+#include "ash/public/cpp/keyboard_shortcut_item.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace app_list {
+
+// An intermediary struct between keyboard shortcut data classes in //ash and
+// the launcher backend. Hierarchy:
+//
+//   1) ash::KeyboardShortcutItem.
+//   2) app_list::KeyboardShortcutData [this].
+//   3) app_list::KeyboardShortcutResult. A subclass of ChromeSearchResult.
+//
+// Data needed for the launcher is extracted from a hard-coded list of (1)
+// objects, and this data is stored in the form of (2). (2) is then later used
+// to create (3) - the benefits of this are that (2) is more lightweight than
+// (3), and can provide encapsulation of processing tasks.
+//
+// To be used in the productivity launcher, and not in the old launcher.
+//
+// TODO(crbug.com/1290682): Complete implementation.
+struct KeyboardShortcutData {
+  explicit KeyboardShortcutData(const ash::KeyboardShortcutItem& item);
+  KeyboardShortcutData(const KeyboardShortcutData&) = delete;
+  KeyboardShortcutData& operator=(const KeyboardShortcutData&) = delete;
+
+  ~KeyboardShortcutData();
+
+  // The description of the shortcut action e.g. "Dock a window on the right".
+  const std::u16string description_message;
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_KEYBOARD_SHORTCUT_DATA_H_
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_result.cc b/chrome/browser/ui/app_list/search/keyboard_shortcut_result.cc
index 2225403..99256e2 100644
--- a/chrome/browser/ui/app_list/search/keyboard_shortcut_result.cc
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_result.cc
@@ -7,7 +7,8 @@
 namespace app_list {
 
 // TODO(crbug.com/1290682): Implement.
-KeyboardShortcutResult::KeyboardShortcutResult() = default;
+KeyboardShortcutResult::KeyboardShortcutResult(
+    const KeyboardShortcutData& data) {}
 
 // TODO(crbug.com/1290682): Implement.
 KeyboardShortcutResult::~KeyboardShortcutResult() = default;
diff --git a/chrome/browser/ui/app_list/search/keyboard_shortcut_result.h b/chrome/browser/ui/app_list/search/keyboard_shortcut_result.h
index 1054831..844d937 100644
--- a/chrome/browser/ui/app_list/search/keyboard_shortcut_result.h
+++ b/chrome/browser/ui/app_list/search/keyboard_shortcut_result.h
@@ -6,13 +6,14 @@
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_KEYBOARD_SHORTCUT_RESULT_H_
 
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+#include "chrome/browser/ui/app_list/search/keyboard_shortcut_data.h"
 
 namespace app_list {
 
 // TODO(crbug.com/1290682): Implement.
 class KeyboardShortcutResult : public ChromeSearchResult {
  public:
-  KeyboardShortcutResult();
+  explicit KeyboardShortcutResult(const KeyboardShortcutData& data);
   KeyboardShortcutResult(const KeyboardShortcutResult&) = delete;
   KeyboardShortcutResult& operator=(const KeyboardShortcutResult&) = delete;
 
diff --git a/chrome/browser/ui/app_list/search/ranking/answer_ranker.cc b/chrome/browser/ui/app_list/search/ranking/answer_ranker.cc
index 7f44d2c9e..950cb44f 100644
--- a/chrome/browser/ui/app_list/search/ranking/answer_ranker.cc
+++ b/chrome/browser/ui/app_list/search/ranking/answer_ranker.cc
@@ -8,41 +8,136 @@
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 
 namespace app_list {
+namespace {
+
+// Returns the provider type's priority. A higher value indicates higher
+// priority. Providers that are never used for answers will have 0 priority.
+bool GetPriority(ProviderType type) {
+  switch (type) {
+    case ProviderType::kOmnibox:
+      return 2;
+    case ProviderType::kKeyboardShortcut:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+// If there are any Omnibox answers, returns the highest scoring one. If not,
+// returns nullptr.
+ChromeSearchResult* GetOmniboxCandidate(Results& results) {
+  ChromeSearchResult* top_answer = nullptr;
+  double top_score = 0.0;
+  for (const auto& result : results) {
+    if (result->display_type() != DisplayType::kAnswerCard)
+      continue;
+
+    const double score = result->relevance();
+    if (!top_answer || score > top_score) {
+      top_answer = result.get();
+      top_score = score;
+    }
+  }
+  return top_answer;
+}
+
+// Returns the Shortcut best match as long as there is only one. Otherwise,
+// returns nullptr.
+ChromeSearchResult* GetShortcutCandidate(Results& results) {
+  ChromeSearchResult* best_shortcut = nullptr;
+  for (auto& result : results) {
+    if (!result->best_match())
+      continue;
+
+    if (best_shortcut) {
+      // A best match shortcut has already been found, so there are at least
+      // two and neither should be promoted to Answer Card.
+      return nullptr;
+    }
+    best_shortcut = result.get();
+  }
+  return best_shortcut;
+}
+
+}  // namespace
 
 AnswerRanker::AnswerRanker() = default;
+
 AnswerRanker::~AnswerRanker() = default;
 
+void AnswerRanker::Start(const std::u16string& query,
+                         ResultsMap& results,
+                         CategoriesList& categories) {
+  burn_in_elapsed_ = false;
+  chosen_answer_ = nullptr;
+}
+
 void AnswerRanker::UpdateResultRanks(ResultsMap& results,
                                      ProviderType provider) {
-  if (provider != ProviderType::kOmnibox)
+  if (GetPriority(provider) == 0)
     return;
 
   const auto it = results.find(provider);
   DCHECK(it != results.end());
+  auto& new_results = it->second;
 
-  ChromeSearchResult* top_answer = nullptr;
-  double top_score;
-  for (const auto& result : it->second) {
-    if (result->display_type() != ash::SearchResultDisplayType::kAnswerCard)
-      continue;
-
-    // Compare this result to the existing answer, if any, and hide the one with
-    // lower score.
-    // TODO(crbug.com/1275408): If we ever expect there to be more than one
-    // answer candidate, then it should instead be properly demoted into a list
-    // result.
-    const double score = result->relevance();
-    if (!top_answer) {
-      top_answer = result.get();
-      top_score = score;
-    } else if (score > top_score) {
-      top_answer->scoring().filter = true;
-      top_answer = result.get();
-      top_score = score;
-    } else {
-      result->scoring().filter = true;
+  // Omnibox answers should not be displayed unless they are selected, so filter
+  // them out by default. If an Omnibox answer is selected later, it will be
+  // un-filtered then.
+  if (provider == ProviderType::kOmnibox) {
+    for (auto& result : new_results) {
+      if (result->display_type() == DisplayType::kAnswerCard) {
+        result->scoring().filter = true;
+      }
     }
   }
+
+  // Don't change a selected answer after the burn-in period has elapsed. This
+  // includes ensuring that the answer is re-selected.
+  if (burn_in_elapsed_ && chosen_answer_) {
+    if (chosen_answer_->result_type() == provider) {
+      PromoteChosenAnswer();
+    }
+    return;
+  }
+
+  // Don't make any changes if the chosen answer has higher priority than the
+  // current provider.
+  if (chosen_answer_ &&
+      GetPriority(provider) < GetPriority(chosen_answer_->result_type())) {
+    return;
+  }
+
+  // Finally, choose a new candidate from the current provider if one exists.
+  ChromeSearchResult* new_answer = nullptr;
+  switch (provider) {
+    case ProviderType::kOmnibox:
+      new_answer = GetOmniboxCandidate(new_results);
+      break;
+    case ProviderType::kKeyboardShortcut:
+      new_answer = GetShortcutCandidate(new_results);
+      break;
+    default:
+      return;
+  }
+  if (new_answer)
+    chosen_answer_ = new_answer->GetWeakPtr();
+
+  if (burn_in_elapsed_)
+    PromoteChosenAnswer();
+}
+
+void AnswerRanker::OnBurnInPeriodElapsed() {
+  burn_in_elapsed_ = true;
+  PromoteChosenAnswer();
+}
+
+void AnswerRanker::PromoteChosenAnswer() {
+  if (!chosen_answer_)
+    return;
+
+  chosen_answer_->SetDisplayType(DisplayType::kAnswerCard);
+  chosen_answer_->scoring().filter = false;
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/ranking/answer_ranker.h b/chrome/browser/ui/app_list/search/ranking/answer_ranker.h
index 95f50c2..a0fce30 100644
--- a/chrome/browser/ui/app_list/search/ranking/answer_ranker.h
+++ b/chrome/browser/ui/app_list/search/ranking/answer_ranker.h
@@ -5,13 +5,15 @@
 #ifndef CHROME_BROWSER_UI_APP_LIST_SEARCH_RANKING_ANSWER_RANKER_H_
 #define CHROME_BROWSER_UI_APP_LIST_SEARCH_RANKING_ANSWER_RANKER_H_
 
+#include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/app_list/search/ranking/ranker.h"
 #include "chrome/browser/ui/app_list/search/ranking/types.h"
 
 namespace app_list {
 
-// A ranker that selects at most one result to be an Answer card. This ranker
-// also hides any Omnibox answers that are not selected.
+// A ranker that selects at most one result to be an Answer card by settings its
+// DisplayType to kAnswerCard.
+// This ranker also hides any Omnibox answers that are not selected.
 class AnswerRanker : public Ranker {
  public:
   AnswerRanker();
@@ -21,7 +23,20 @@
   AnswerRanker& operator=(const AnswerRanker&) = delete;
 
   // Ranker:
+  void Start(const std::u16string& query,
+             ResultsMap& results,
+             CategoriesList& categories) override;
   void UpdateResultRanks(ResultsMap& results, ProviderType provider) override;
+  void OnBurnInPeriodElapsed() override;
+
+ private:
+  // Officially promotes the current answer candidate if there is one.
+  void PromoteChosenAnswer();
+
+  // The currently selected answer. A nullptr value indicates that no answer
+  // card has been chosen.
+  base::WeakPtr<ChromeSearchResult> chosen_answer_;
+  bool burn_in_elapsed_ = false;
 };
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/ranking/answer_ranker_unittest.cc b/chrome/browser/ui/app_list/search/ranking/answer_ranker_unittest.cc
index 6759a47b..b31053b 100644
--- a/chrome/browser/ui/app_list/search/ranking/answer_ranker_unittest.cc
+++ b/chrome/browser/ui/app_list/search/ranking/answer_ranker_unittest.cc
@@ -12,44 +12,152 @@
 namespace app_list {
 namespace {
 
-class TestAnswerResult : public ChromeSearchResult {
+class TestResult : public ChromeSearchResult {
  public:
-  explicit TestAnswerResult(double relevance) {
-    SetDisplayType(DisplayType::kAnswerCard);
+  TestResult(double relevance, DisplayType display_type, bool best_match) {
     set_relevance(relevance);
+    SetDisplayType(display_type);
+    SetBestMatch(best_match);
   }
-  ~TestAnswerResult() override {}
+  ~TestResult() override {}
 
   // ChromeSearchResult overrides:
   void Open(int event_flags) override {}
 };
 
-Results make_answers(std::vector<double> relevances) {
+Results make_omnibox_candidates(std::vector<double> relevances) {
   Results results;
   for (const double relevance : relevances) {
-    results.push_back(std::make_unique<TestAnswerResult>(relevance));
+    results.push_back(std::make_unique<TestResult>(
+        relevance, ash::SearchResultDisplayType::kAnswerCard, false));
+  }
+  return results;
+}
+
+Results make_shortcut_candidates(std::vector<bool> best_matches) {
+  Results results;
+  for (const double best_match : best_matches) {
+    results.push_back(std::make_unique<TestResult>(
+        1, ash::SearchResultDisplayType::kList, best_match));
   }
   return results;
 }
 
 }  // namespace
 
-// Tests that all but one answer is filtered out.
-TEST(AnswerRankerTest, FilterUnsuccessfulCandidates) {
+// Tests that the best Omnibox answer is selected and all others are filtered
+// out.
+TEST(AnswerRankerTest, SelectAndFilterOmnibox) {
   ResultsMap results_map;
-  results_map[ResultType::kOmnibox] = make_answers({0.3, 0.5, 0.4});
+  results_map[ResultType::kOmnibox] = make_omnibox_candidates({0.3, 0.5, 0.4});
 
   AnswerRanker ranker;
   ranker.UpdateResultRanks(results_map, ProviderType::kOmnibox);
+  ranker.OnBurnInPeriodElapsed();
 
-  // Results should still be in the same order, with all except the highest
-  // scoring answer filtered out.
   const auto& results = results_map[ResultType::kOmnibox];
   ASSERT_EQ(results.size(), 3);
 
-  EXPECT_TRUE(results[0]->scoring().filter);
+  // The highest scoring Omnibox answer is selected.
+  EXPECT_EQ(results[1]->display_type(),
+            ash::SearchResultDisplayType::kAnswerCard);
   EXPECT_FALSE(results[1]->scoring().filter);
+
+  // Others are filtered out.
+  EXPECT_TRUE(results[0]->scoring().filter);
   EXPECT_TRUE(results[2]->scoring().filter);
 }
 
+// Tests that a best match shortcut is selected.
+TEST(AnswerRankerTest, SelectBestShortcut) {
+  ResultsMap results_map;
+  results_map[ResultType::kKeyboardShortcut] =
+      make_shortcut_candidates({false, true});
+
+  AnswerRanker ranker;
+  ranker.UpdateResultRanks(results_map, ProviderType::kKeyboardShortcut);
+  ranker.OnBurnInPeriodElapsed();
+
+  const auto& results = results_map[ResultType::kKeyboardShortcut];
+  ASSERT_EQ(results.size(), 2);
+
+  // The best match shortcut is selected.
+  EXPECT_EQ(results[1]->display_type(),
+            ash::SearchResultDisplayType::kAnswerCard);
+  EXPECT_FALSE(results[1]->scoring().filter);
+
+  EXPECT_NE(results[0]->display_type(),
+            ash::SearchResultDisplayType::kAnswerCard);
+}
+
+// Tests that no shortcut answers are selected if there are multiple best
+// matches.
+TEST(AnswerRankerTest, OnlySelectIfOneBestShortcut) {
+  ResultsMap results_map;
+  results_map[ResultType::kKeyboardShortcut] =
+      make_shortcut_candidates({true, true});
+
+  AnswerRanker ranker;
+  ranker.UpdateResultRanks(results_map, ProviderType::kKeyboardShortcut);
+  ranker.OnBurnInPeriodElapsed();
+
+  const auto& results = results_map[ResultType::kKeyboardShortcut];
+  ASSERT_EQ(results.size(), 2);
+
+  // No shortcuts should be selected.
+  for (const auto& result : results) {
+    EXPECT_NE(result->display_type(),
+              ash::SearchResultDisplayType::kAnswerCard);
+  }
+}
+
+// Tests that Omnibox answers take priority over Shortcuts.
+TEST(AnswerRankerTest, OmniboxOverShortcuts) {
+  ResultsMap results_map;
+  results_map[ResultType::kOmnibox] = make_omnibox_candidates({0.4});
+  results_map[ResultType::kKeyboardShortcut] = make_shortcut_candidates({true});
+
+  AnswerRanker ranker;
+  ranker.UpdateResultRanks(results_map, ProviderType::kKeyboardShortcut);
+  ranker.UpdateResultRanks(results_map, ProviderType::kOmnibox);
+  ranker.OnBurnInPeriodElapsed();
+
+  // Shortcut candidate should not be selected.
+  const auto& shortcut_results = results_map[ResultType::kKeyboardShortcut];
+  ASSERT_EQ(shortcut_results.size(), 1);
+  EXPECT_NE(shortcut_results[0]->display_type(),
+            ash::SearchResultDisplayType::kAnswerCard);
+
+  // Omnibox candidate should be selected.
+  const auto& omnibox_results = results_map[ResultType::kOmnibox];
+  ASSERT_EQ(omnibox_results.size(), 1);
+  EXPECT_EQ(omnibox_results[0]->display_type(),
+            ash::SearchResultDisplayType::kAnswerCard);
+  EXPECT_FALSE(omnibox_results[0]->scoring().filter);
+}
+
+// Tests that a chosen answer is not changed after burn-in.
+TEST(AnswerRankerTest, SelectedAnswerNotChangedAfterBurnIn) {
+  ResultsMap results_map;
+  results_map[ResultType::kKeyboardShortcut] = make_shortcut_candidates({true});
+
+  AnswerRanker ranker;
+  ranker.UpdateResultRanks(results_map, ProviderType::kKeyboardShortcut);
+  ranker.OnBurnInPeriodElapsed();
+
+  // The shortcut answer is selected.
+  const auto& shortcut_results = results_map[ResultType::kKeyboardShortcut];
+  ASSERT_EQ(shortcut_results.size(), 1);
+  EXPECT_EQ(shortcut_results[0]->display_type(),
+            ash::SearchResultDisplayType::kAnswerCard);
+
+  // New Omnibox candidates should still be filtered out.
+  results_map[ResultType::kOmnibox] = make_omnibox_candidates({0.5});
+  ranker.UpdateResultRanks(results_map, ProviderType::kOmnibox);
+
+  const auto& omnibox_results = results_map[ResultType::kOmnibox];
+  ASSERT_EQ(omnibox_results.size(), 1);
+  EXPECT_TRUE(omnibox_results[0]->scoring().filter);
+}
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc b/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc
index 629c0c8..e86828c 100644
--- a/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc
+++ b/chrome/browser/ui/app_list/search/ranking/best_match_ranker.cc
@@ -44,7 +44,9 @@
 bool ShouldIgnoreResult(const ChromeSearchResult* result) {
   // TODO(crbug.com/1199206): We should have a more robust way of determining
   // omnibox subtypes than using the metrics type.
-  return result->metrics_type() == ash::OMNIBOX_WEB_QUERY;
+  return result->metrics_type() == ash::OMNIBOX_WEB_QUERY ||
+         result->metrics_type() == ash::OMNIBOX_ANSWER ||
+         result->metrics_type() == ash::OMNIBOX_CALCULATOR;
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/app_list/search/ranking/ranker_delegate.cc b/chrome/browser/ui/app_list/search/ranking/ranker_delegate.cc
index bf9fb7a..a8367c94 100644
--- a/chrome/browser/ui/app_list/search/ranking/ranker_delegate.cc
+++ b/chrome/browser/ui/app_list/search/ranking/ranker_delegate.cc
@@ -68,7 +68,6 @@
   // 1. Result pre-processing. These filter or modify search results but don't
   // change their scores.
   AddRanker(std::make_unique<QueryHighlighter>());
-  AddRanker(std::make_unique<AnswerRanker>());
   AddRanker(std::make_unique<ContinueRanker>());
   AddRanker(std::make_unique<FilteringRanker>());
   AddRanker(std::make_unique<RemovedResultsRanker>(
@@ -106,8 +105,10 @@
   AddRanker(std::move(category_ranker));
 
   // 5. Result post-processing.
-  // Nb. the top-match ranker relies on score normalization.
+  // Nb. the best match ranker relies on score normalization, and the answer
+  // ranker relies on the best match ranker.
   AddRanker(std::make_unique<BestMatchRanker>());
+  AddRanker(std::make_unique<AnswerRanker>());
 }
 
 RankerDelegate::~RankerDelegate() {}
diff --git a/chrome/browser/ui/app_list/search/ranking/types.h b/chrome/browser/ui/app_list/search/ranking/types.h
index a8c6427..29774275 100644
--- a/chrome/browser/ui/app_list/search/ranking/types.h
+++ b/chrome/browser/ui/app_list/search/ranking/types.h
@@ -15,6 +15,9 @@
 // The type of a search provider as a whole. This is currently just the 'main'
 // ResultType returned by the provider.
 using ProviderType = ash::AppListSearchResultType;
+// The display type of a search result, indicating which UI section it belongs
+// to.
+using DisplayType = ash::SearchResultDisplayType;
 
 // Note: Results and ResultsMap should be defined here, but are defined in
 // SearchController to avoid an include loop.
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index 8181fce..b972707 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -131,9 +131,11 @@
             kMaxAppShortcutResults, profile, list_controller));
   }
 
-  // Enable the Continue section providers only in the productivity launcher,
-  // and only if the enable_continue parameter is true (the default).
-  if (ash::features::IsProductivityLauncherEnabled() &&
+  // Enable zero-state files aka. the Continue section if:
+  // - unconditionally in the old launcher.
+  // - in the productivity launcher only if the enable_continue parameter is
+  //   true (the default).
+  if (!ash::features::IsProductivityLauncherEnabled() ||
       base::GetFieldTrialParamByFeatureAsBool(
           ash::features::kProductivityLauncher, "enable_continue", true)) {
     size_t zero_state_files_group_id =
diff --git a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
index 9fbb558..0f1a85e 100644
--- a/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/local_card_migration_browsertest.cc
@@ -1083,9 +1083,9 @@
   EXPECT_TRUE(GetLocalCardMigrationIconView()->GetVisible());
   EXPECT_TRUE(GetLocalCardMigrationOfferBubbleViews()->GetVisible());
 
-  AddTabAtIndexToBrowser(GetBrowser(0), 1, GURL("http://example.com/"),
-                         ui::PAGE_TRANSITION_TYPED,
-                         /*check_navigation_success=*/true);
+  ASSERT_TRUE(AddTabAtIndexToBrowser(GetBrowser(0), 1, GURL("about:blank"),
+                                     ui::PAGE_TRANSITION_TYPED,
+                                     /*check_navigation_success=*/true));
   TabStripModel* tab_model = GetBrowser(0)->tab_strip_model();
   tab_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther});
   WaitForAnimationToComplete();
diff --git a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
index 4c6afb7..de4a944 100644
--- a/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_card_bubble_views_browsertest.cc
@@ -2150,9 +2150,9 @@
   EXPECT_TRUE(GetSaveCardIconView()->GetVisible());
   EXPECT_TRUE(GetSaveCardBubbleViews()->GetVisible());
 
-  AddTabAtIndexToBrowser(GetBrowser(0), 1, GURL("http://example.com/"),
-                         ui::PAGE_TRANSITION_TYPED,
-                         /*check_navigation_success=*/true);
+  ASSERT_TRUE(AddTabAtIndexToBrowser(GetBrowser(0), 1, GURL("about:blank"),
+                                     ui::PAGE_TRANSITION_TYPED,
+                                     /*check_navigation_success=*/true));
   TabStripModel* tab_model = GetBrowser(0)->tab_strip_model();
   tab_model->ActivateTabAt(1, {TabStripModel::GestureType::kOther});
   WaitForAnimationToEnd();
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views.cc b/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views.cc
index 3a81036..d97eaa7 100644
--- a/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views.cc
@@ -47,16 +47,9 @@
   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_BUBBLE_PREFERRED_WIDTH));
 
-  // TODO(crbug.com/1281695): Add issuer legal message as well to the footnote.
-  const LegalMessageLines google_legal_message =
-      controller_->GetVirtualCardEnrollmentFields()->google_legal_message;
-  if (!google_legal_message.empty()) {
-    legal_message_view_ = SetFootnoteView(std::make_unique<LegalMessageView>(
-        google_legal_message,
-        base::BindRepeating(&VirtualCardEnrollBubbleViews::LegalMessageClicked,
-                            base::Unretained(this))));
-    legal_message_view_->SetID(DialogViewId::FOOTNOTE_VIEW);
-  }
+  raw_ptr<views::View> legal_message_view =
+      SetFootnoteView(CreateLegalMessageView());
+  legal_message_view->SetID(DialogViewId::FOOTNOTE_VIEW);
 }
 
 void VirtualCardEnrollBubbleViews::Show(DisplayReason reason) {
@@ -195,6 +188,34 @@
   AddChildView(CreateMainContentView());
 }
 
+std::unique_ptr<views::View>
+VirtualCardEnrollBubbleViews::CreateLegalMessageView() {
+  auto legal_message_view = std::make_unique<views::BoxLayoutView>();
+  legal_message_view->SetOrientation(views::BoxLayout::Orientation::kVertical);
+  legal_message_view->SetBetweenChildSpacing(
+      ChromeLayoutProvider::Get()->GetDistanceMetric(
+          DISTANCE_RELATED_CONTROL_VERTICAL_SMALL));
+
+  const LegalMessageLines google_legal_message =
+      controller_->GetVirtualCardEnrollmentFields()->google_legal_message;
+  const LegalMessageLines issuser_legal_message =
+      controller_->GetVirtualCardEnrollmentFields()->issuer_legal_message;
+
+  DCHECK(!google_legal_message.empty());
+  legal_message_view->AddChildView(std::make_unique<LegalMessageView>(
+      google_legal_message,
+      base::BindRepeating(&VirtualCardEnrollBubbleViews::LegalMessageClicked,
+                          base::Unretained(this))));
+
+  if (!issuser_legal_message.empty()) {
+    legal_message_view->AddChildView(std::make_unique<LegalMessageView>(
+        issuser_legal_message,
+        base::BindRepeating(&VirtualCardEnrollBubbleViews::LegalMessageClicked,
+                            base::Unretained(this))));
+  }
+  return legal_message_view;
+}
+
 void VirtualCardEnrollBubbleViews::LearnMoreLinkClicked() {
   if (controller()) {
     controller()->OnLinkClicked(
diff --git a/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views.h b/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views.h
index 0057e5f..560b58c 100644
--- a/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views.h
+++ b/chrome/browser/ui/views/autofill/payments/virtual_card_enroll_bubble_views.h
@@ -61,14 +61,14 @@
   ~VirtualCardEnrollBubbleViews() override;
 
  private:
+  std::unique_ptr<views::View> CreateLegalMessageView();
+
   void LearnMoreLinkClicked();
 
   void LegalMessageClicked(const GURL& url);
 
   raw_ptr<VirtualCardEnrollBubbleController> controller_;
 
-  raw_ptr<LegalMessageView> legal_message_view_ = nullptr;
-
   PaymentsBubbleClosedReason closed_reason_ =
       PaymentsBubbleClosedReason::kUnknown;
 
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 dd5af4c..8c5617e8 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
@@ -196,6 +196,24 @@
   }
 }
 
+#if !BUILDFLAG(IS_CHROMEOS_LACROS)
+apps::WindowMode GetDisplayMode(blink::mojom::DisplayMode display_mode) {
+  switch (display_mode) {
+    case blink::mojom::DisplayMode::kUndefined:
+      return apps::WindowMode::kUnknown;
+    case blink::mojom::DisplayMode::kBrowser:
+      return apps::WindowMode::kBrowser;
+    case blink::mojom::DisplayMode::kTabbed:
+      return apps::WindowMode::kTabbedWindow;
+    case blink::mojom::DisplayMode::kMinimalUi:
+    case blink::mojom::DisplayMode::kStandalone:
+    case blink::mojom::DisplayMode::kFullscreen:
+    case blink::mojom::DisplayMode::kWindowControlsOverlay:
+      return apps::WindowMode::kWindow;
+  }
+}
+#endif
+
 bool IsNoteTakingWebApp(const web_app::WebApp& web_app) {
   return web_app.note_taking_new_note_url().is_valid();
 }
@@ -548,6 +566,9 @@
   }
 #endif
 
+  app->window_mode =
+      GetDisplayMode(registrar().GetAppUserDisplayMode(web_app->app_id()));
+
   return app;
 }
 #endif
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index ddb0fb1..2cfb944 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1643845195-bd30320c0cfe68a85278a2f735b57354f6948944.profdata
+chrome-linux-main-1643867790-3021a2d4a4bad68e35ef0b967e0caeecaf644c81.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 6487df1..6ed3957 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1643845195-08861c60ea4e81363d03d9e7a69cdd11c87c46a3.profdata
+chrome-mac-arm-main-1643889420-e67b913baca764a9adf8a7925faa2d4b65cd7eae.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 35490598..2a93123 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1643845195-08ea4f71ded9cbb0aa7f5b78231811af74e56967.profdata
+chrome-mac-main-1643889420-a983f30b82058d9c756b23284a79b8366e17eff5.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index f9fa4f05..b223c0a4 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1643845195-5ab655d3a865275c6322bfe1b1ee6e7fe47312ac.profdata
+chrome-win32-main-1643878699-66659f2cde6b446c259c94b9eed26ff4b9accdb1.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index cbcb43c..1f93aa6f 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1643845195-d570ff41cfaca55c277a1bc8787242054eef387d.profdata
+chrome-win64-main-1643878699-1d81345be58e2f963fa5f0051361c9550878716d.profdata
diff --git a/chrome/chrome_paks.gni b/chrome/chrome_paks.gni
index b1118483..9904bf50 100644
--- a/chrome/chrome_paks.gni
+++ b/chrome/chrome_paks.gni
@@ -204,26 +204,26 @@
     }
     if (is_chromeos_ash) {
       sources += [
+        "$root_gen_dir/ash/ash_camera_app_resources.pak",
+        "$root_gen_dir/ash/ash_color_internals_resources.pak",
+        "$root_gen_dir/ash/ash_diagnostics_app_resources.pak",
+        "$root_gen_dir/ash/ash_eche_app_resources.pak",
+        "$root_gen_dir/ash/ash_eche_bundle_resources.pak",
+        "$root_gen_dir/ash/ash_firmware_update_app_resources.pak",
+        "$root_gen_dir/ash/ash_help_app_resources.pak",
+        "$root_gen_dir/ash/ash_media_app_resources.pak",
+        "$root_gen_dir/ash/ash_multidevice_debug_resources.pak",
+        "$root_gen_dir/ash/ash_os_feedback_resources.pak",
+        "$root_gen_dir/ash/ash_personalization_app_resources.pak",
+        "$root_gen_dir/ash/ash_print_management_resources.pak",
+        "$root_gen_dir/ash/ash_projector_app_trusted_resources.pak",
+        "$root_gen_dir/ash/ash_projector_app_untrusted_resources.pak",
+        "$root_gen_dir/ash/ash_scanning_app_resources.pak",
+        "$root_gen_dir/ash/ash_shimless_rma_resources.pak",
+        "$root_gen_dir/ash/ash_shortcut_customization_app_resources.pak",
+        "$root_gen_dir/ash/ash_system_extensions_internals_resources.pak",
+        "$root_gen_dir/ash/connectivity_diagnostics_resources.pak",
         "$root_gen_dir/ash/public/cpp/resources/ash_public_unscaled_resources.pak",
-        "$root_gen_dir/ash/webui/ash_camera_app_resources.pak",
-        "$root_gen_dir/ash/webui/ash_color_internals_resources.pak",
-        "$root_gen_dir/ash/webui/ash_diagnostics_app_resources.pak",
-        "$root_gen_dir/ash/webui/ash_eche_app_resources.pak",
-        "$root_gen_dir/ash/webui/ash_eche_bundle_resources.pak",
-        "$root_gen_dir/ash/webui/ash_firmware_update_app_resources.pak",
-        "$root_gen_dir/ash/webui/ash_help_app_resources.pak",
-        "$root_gen_dir/ash/webui/ash_media_app_resources.pak",
-        "$root_gen_dir/ash/webui/ash_multidevice_debug_resources.pak",
-        "$root_gen_dir/ash/webui/ash_os_feedback_resources.pak",
-        "$root_gen_dir/ash/webui/ash_personalization_app_resources.pak",
-        "$root_gen_dir/ash/webui/ash_print_management_resources.pak",
-        "$root_gen_dir/ash/webui/ash_projector_app_trusted_resources.pak",
-        "$root_gen_dir/ash/webui/ash_projector_app_untrusted_resources.pak",
-        "$root_gen_dir/ash/webui/ash_scanning_app_resources.pak",
-        "$root_gen_dir/ash/webui/ash_shimless_rma_resources.pak",
-        "$root_gen_dir/ash/webui/ash_shortcut_customization_app_resources.pak",
-        "$root_gen_dir/ash/webui/ash_system_extensions_internals_resources.pak",
-        "$root_gen_dir/ash/webui/connectivity_diagnostics_resources.pak",
         "$root_gen_dir/ash/webui/file_manager/resources/file_manager_swa_resources.pak",
         "$root_gen_dir/ash/webui/file_manager/untrusted_resources/file_manager_untrusted_resources.pak",
         "$root_gen_dir/chrome/audio_resources.pak",
@@ -303,9 +303,9 @@
 
       if (!is_official_build) {
         sources += [
-          "$root_gen_dir/ash/webui/ash_demo_mode_app_resources.pak",
-          "$root_gen_dir/ash/webui/ash_sample_system_web_app_resources.pak",
-          "$root_gen_dir/ash/webui/ash_sample_system_web_app_untrusted_resources.pak",
+          "$root_gen_dir/ash/ash_demo_mode_app_resources.pak",
+          "$root_gen_dir/ash/ash_sample_system_web_app_resources.pak",
+          "$root_gen_dir/ash/ash_sample_system_web_app_untrusted_resources.pak",
         ]
         deps += [
           "//ash/webui/resources:demo_mode_app_resources",
diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc
index e4d6fa635..1eb99cf3 100644
--- a/chrome/test/base/in_process_browser_test.cc
+++ b/chrome/test/base/in_process_browser_test.cc
@@ -557,6 +557,14 @@
     const GURL& url,
     ui::PageTransition transition,
     bool check_navigation_success) {
+  return AddTabAtIndexToBrowser(browser, index, url, transition);
+}
+
+bool InProcessBrowserTest::AddTabAtIndexToBrowser(
+    Browser* browser,
+    int index,
+    const GURL& url,
+    ui::PageTransition transition) {
   NavigateParams params(browser, url, transition);
   params.tabstrip_index = index;
   params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
diff --git a/chrome/test/base/in_process_browser_test.h b/chrome/test/base/in_process_browser_test.h
index 9f44fd4..b9ac30b 100644
--- a/chrome/test/base/in_process_browser_test.h
+++ b/chrome/test/base/in_process_browser_test.h
@@ -196,12 +196,19 @@
   // Convenience methods for adding tabs to a Browser. Returns true if the
   // navigation succeeded. |check_navigation_success| is ignored and will be
   // removed as part of check_navigation_success http://crbug.com/1014186.
-  bool AddTabAtIndexToBrowser(Browser* browser,
-                              int index,
-                              const GURL& url,
-                              ui::PageTransition transition,
-                              bool check_navigation_success);
-  bool AddTabAtIndex(int index, const GURL& url, ui::PageTransition transition);
+  // Do not add new usages of the version with |check_navigation_success|.
+  [[nodiscard]] bool AddTabAtIndexToBrowser(Browser* browser,
+                                            int index,
+                                            const GURL& url,
+                                            ui::PageTransition transition,
+                                            bool check_navigation_success);
+  [[nodiscard]] bool AddTabAtIndexToBrowser(Browser* browser,
+                                            int index,
+                                            const GURL& url,
+                                            ui::PageTransition transition);
+  [[nodiscard]] bool AddTabAtIndex(int index,
+                                   const GURL& url,
+                                   ui::PageTransition transition);
 
   // Sets up default command line that will be used to launch the child browser
   // process with an in-process test. Called by SetUp() after SetUpCommandLine()
diff --git a/chrome/test/data/devtools/extensions/chrome_scheme/devtools.html b/chrome/test/data/devtools/extensions/can_inspect_url/devtools.html
similarity index 100%
rename from chrome/test/data/devtools/extensions/chrome_scheme/devtools.html
rename to chrome/test/data/devtools/extensions/can_inspect_url/devtools.html
diff --git a/chrome/test/data/devtools/extensions/chrome_scheme/devtools.js b/chrome/test/data/devtools/extensions/can_inspect_url/devtools.js
similarity index 71%
rename from chrome/test/data/devtools/extensions/chrome_scheme/devtools.js
rename to chrome/test/data/devtools/extensions/can_inspect_url/devtools.js
index 3d830c4..3cc81eb 100644
--- a/chrome/test/data/devtools/extensions/chrome_scheme/devtools.js
+++ b/chrome/test/data/devtools/extensions/can_inspect_url/devtools.js
@@ -7,16 +7,25 @@
 }
 
 async function test() {
-  const newPageURL = 'chrome://version/';
+  // The test driver uses the URL fragment of the initial inspected page to pass
+  // the URL to test against
+  const newPageURL = await new Promise((resolve, reject) => {
+    chrome.devtools.inspectedWindow.eval(
+        'location.hash.substr(1)', {}, (res, error) => {
+          if (error && error.isError)
+            reject(error);
+          else
+            resolve(res || error.value);
+        });
+  });
   const inspectedTabId = chrome.devtools.inspectedWindow.tabId;
   chrome.tabs.update(inspectedTabId, {url: newPageURL});
-  await new Promise(resolve => chrome.tabs.onUpdated.addListener(
-      (tabId, changedProps) => {
+  await new Promise(
+      resolve => chrome.tabs.onUpdated.addListener((tabId, changedProps) => {
         if (inspectedTabId !== tabId || changedProps.url !== newPageURL)
           return;
         resolve();
-      }
-  ));
+      }));
   // There may be a number of inherent races between the page and the DevTools
   // during the navigation. Let's do a number of eval in a hope of catching some
   // of them.
diff --git a/chrome/test/data/devtools/extensions/chrome_scheme/manifest.json b/chrome/test/data/devtools/extensions/can_inspect_url/manifest.json
similarity index 100%
rename from chrome/test/data/devtools/extensions/chrome_scheme/manifest.json
rename to chrome/test/data/devtools/extensions/can_inspect_url/manifest.json
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index cfb7628d..d04295ef 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -6977,16 +6977,39 @@
     "os": [
       "win",
       "linux",
-      "mac"
+      "mac",
+      "chromeos_ash",
+      "chromeos_lacros"
     ],
     "policy_pref_mapping_tests": [
       {
+        "policies": {},
+        "prefs": {
+          "profile.browser_guest_enabled": {
+            "location": "local_state",
+            "default_value": true
+          }
+        }
+      },
+      {
         "policies": {
           "BrowserGuestModeEnabled": true
         },
         "prefs": {
           "profile.browser_guest_enabled": {
-            "location": "local_state"
+            "location": "local_state",
+            "value": true
+          }
+        }
+      },
+      {
+        "policies": {
+          "BrowserGuestModeEnabled": false
+        },
+        "prefs": {
+          "profile.browser_guest_enabled": {
+            "location": "local_state",
+            "value": false
           }
         }
       }
@@ -7015,16 +7038,39 @@
     "os": [
       "win",
       "linux",
-      "mac"
+      "mac",
+      "chromeos_ash",
+      "chromeos_lacros"
     ],
     "policy_pref_mapping_tests": [
       {
+        "policies": {},
+        "prefs": {
+          "profile.add_person_enabled": {
+            "location": "local_state",
+            "default_value": true
+          }
+        }
+      },
+      {
         "policies": {
           "BrowserAddPersonEnabled": true
         },
         "prefs": {
           "profile.add_person_enabled": {
-            "location": "local_state"
+            "location": "local_state",
+            "value": true
+          }
+        }
+      },
+      {
+        "policies": {
+          "BrowserAddPersonEnabled": false
+        },
+        "prefs": {
+          "profile.add_person_enabled": {
+            "location": "local_state",
+            "value": false
           }
         }
       }
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index cef4db7..720cb5b 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -500,7 +500,6 @@
     "inline_login:closure_compile",
     "js/cr:closure_compile",
     "media_router:closure_compile",
-    "new_tab_page:closure_compile",
 
     # TODO(crbug.com/1000989): Add page specific targets here.
   ]
diff --git a/chrome/test/data/webui/new_tab_page/BUILD.gn b/chrome/test/data/webui/new_tab_page/BUILD.gn
deleted file mode 100644
index 61db1a6..0000000
--- a/chrome/test/data/webui/new_tab_page/BUILD.gn
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright 2020 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//third_party/closure_compiler/compile_js.gni")
-
-js_type_check("closure_compile") {
-  is_polymer3 = true
-  closure_flags = default_closure_args + mojom_js_args + [
-                    "browser_resolver_prefix_replacements=\"chrome://new-tab-page/=../../chrome/browser/resources/new_tab_page/\"",
-                    "browser_resolver_prefix_replacements=\"chrome://test/=./\"",
-                    "js_module_root=../../chrome/test/data/webui/",
-                    "js_module_root=./gen/chrome/test/data/webui/",
-                    "js_module_root=" +
-                        rebase_path("//chrome/browser/resources/new_tab_page/",
-                                    root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/new_tab_page/foo",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/cart",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/photos",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/drive",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/new_tab_page/modules/task_module",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/realbox",
-                            root_build_dir),
-                    "js_module_root=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/chrome/browser/ui/webui/new_tab_page/",
-                            root_build_dir),
-                    "hide_warnings_for=" + rebase_path(
-                            "$root_gen_dir/mojom-webui/ui/webui/resources/cr_components/most_visited",
-                            root_build_dir),
-                  ]
-  deps = [
-    ":metrics_utils_test",
-    ":test_support",
-    "modules:info_dialog_test",
-    "modules:module_descriptor_test",
-    "modules:module_header_test",
-    "modules:module_registry_test",
-    "modules:module_wrapper_test",
-    "modules:modules_test",
-  ]
-  if (!is_official_build) {
-    deps += [ "modules/dummy_v2:module_test" ]
-  }
-}
-
-js_library("metrics_test_support") {
-}
-
-js_library("metrics_utils_test") {
-  deps = [
-    ":metrics_test_support",
-    "//chrome/browser/resources/new_tab_page",
-    "//ui/webui/resources/js:load_time_data.m",
-  ]
-}
-
-js_library("test_support") {
-  deps = [
-    "//chrome/browser/resources/new_tab_page",
-    "//chrome/test/data/webui:test_browser_proxy",
-  ]
-}
diff --git a/chrome/test/data/webui/new_tab_page/modules/BUILD.gn b/chrome/test/data/webui/new_tab_page/modules/BUILD.gn
deleted file mode 100644
index 2cc44e1..0000000
--- a/chrome/test/data/webui/new_tab_page/modules/BUILD.gn
+++ /dev/null
@@ -1,59 +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.
-
-import("//third_party/closure_compiler/compile_js.gni")
-
-js_library("module_header_test") {
-  deps = [ "//chrome/browser/resources/new_tab_page" ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
-js_library("module_registry_test") {
-  deps = [
-    "..:test_support",
-    "//chrome/browser/resources/new_tab_page",
-    "//chrome/test/data/webui:test_browser_proxy",
-    "//chrome/test/data/webui:test_util",
-    "//ui/webui/resources/js:load_time_data.m",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
-js_library("modules_test") {
-  deps = [
-    "..:metrics_test_support",
-    "..:test_support",
-    "//chrome/browser/resources/new_tab_page",
-    "//chrome/test/data/webui:test_browser_proxy",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
-js_library("info_dialog_test") {
-  deps = [ "//chrome/browser/resources/new_tab_page" ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
-js_library("module_descriptor_test") {
-  deps = [
-    "..:metrics_test_support",
-    "..:test_support",
-    "//chrome/browser/resources/new_tab_page",
-    "//chrome/test/data/webui:test_browser_proxy",
-    "//ui/webui/resources/js:load_time_data.m",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
-js_library("module_wrapper_test") {
-  deps = [
-    "..:metrics_test_support",
-    "..:test_support",
-    "//chrome/browser/resources/new_tab_page",
-    "//chrome/test/data/webui:test_browser_proxy",
-    "//chrome/test/data/webui:test_util",
-    "//ui/webui/resources/js:load_time_data.m",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
diff --git a/chrome/test/data/webui/new_tab_page/modules/dummy_v2/BUILD.gn b/chrome/test/data/webui/new_tab_page/modules/dummy_v2/BUILD.gn
deleted file mode 100644
index bb126a8..0000000
--- a/chrome/test/data/webui/new_tab_page/modules/dummy_v2/BUILD.gn
+++ /dev/null
@@ -1,18 +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.
-
-import("//third_party/closure_compiler/compile_js.gni")
-
-js_library("module_test") {
-  deps = [
-    "../..:test_support",
-    "//chrome/browser/resources/new_tab_page",
-    "//chrome/browser/ui/webui/new_tab_page/foo:mojo_bindings_webui_js",
-    "//chrome/test/data/webui:chai_assert",
-    "//chrome/test/data/webui:test_browser_proxy",
-    "//chrome/test/data/webui:test_util",
-    "//ui/webui/resources/js:assert.m",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
diff --git a/chrome/test/data/webui/new_tab_page/modules/modules_test.js b/chrome/test/data/webui/new_tab_page/modules/modules_test.js
index 5f3854ed..9d3bf4957 100644
--- a/chrome/test/data/webui/new_tab_page/modules/modules_test.js
+++ b/chrome/test/data/webui/new_tab_page/modules/modules_test.js
@@ -4,7 +4,7 @@
 
 import 'chrome://test/mojo_webui_test_support.js';
 
-import {$$, Module, ModuleDescriptor, ModuleDescriptorV2, ModuleHeight, ModuleRegistry, ModulesElement, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
+import {$$, ModuleDescriptor, ModuleDescriptorV2, ModuleHeight, ModuleRegistry, ModulesElement, NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote, PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 
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 1f9d0781..916e5c4d 100644
--- a/chrome/test/data/webui/new_tab_page/test_support.js
+++ b/chrome/test/data/webui/new_tab_page/test_support.js
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {InitializeModuleCallback} from 'chrome://new-tab-page/new_tab_page.js';
 import {Theme} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
diff --git a/chrome/test/data/webui/settings/privacy_review_page_test.ts b/chrome/test/data/webui/settings/privacy_review_page_test.ts
index 31cb573f..aadfc120 100644
--- a/chrome/test/data/webui/settings/privacy_review_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_review_page_test.ts
@@ -347,6 +347,12 @@
       page.shadowRoot!.querySelector('#' + PrivacyReviewStep.MSBB)!.shadowRoot!
           .querySelector<HTMLElement>('#urlCollectionToggle')!.click();
       flush();
+      const actionResult =
+          await testMetricsBrowserProxy.whenCalled('recordAction');
+      assertEquals(
+          actionResult,
+          msbbStartOn ? 'Settings.PrivacyGuide.ChangeMSBBOff' :
+                        'Settings.PrivacyGuide.ChangeMSBBOn');
     }
 
     page.shadowRoot!.querySelector<HTMLElement>('#backButton')!.click();
@@ -378,6 +384,12 @@
       page.shadowRoot!.querySelector('#' + PrivacyReviewStep.HISTORY_SYNC)!
           .shadowRoot!.querySelector<HTMLElement>('#historyToggle')!.click();
       flush();
+      const actionResult =
+          await testMetricsBrowserProxy.whenCalled('recordAction');
+      assertEquals(
+          actionResult,
+          historySyncStartOn ? 'Settings.PrivacyGuide.ChangeHistorySyncOff' :
+                               'Settings.PrivacyGuide.ChangeHistorySyncOn');
     }
 
     page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
@@ -413,6 +425,13 @@
                   '#safeBrowsingRadioStandard' :
                   '#safeBrowsingRadioEnhanced')!.click();
       flush();
+      const actionResult =
+          await testMetricsBrowserProxy.whenCalled('recordAction');
+      assertEquals(
+          actionResult,
+          safeBrowsingStartsEnhanced ?
+              'Settings.PrivacyGuide.ChangeSafeBrowsingStandard' :
+              'Settings.PrivacyGuide.ChangeSafeBrowsingEnhanced');
     }
 
     page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
@@ -447,6 +466,13 @@
               cookieStartsBlock3PIncognito ? '#block3P' :
                                              '#block3PIncognito')!.click();
       flush();
+      const actionResult =
+          await testMetricsBrowserProxy.whenCalled('recordAction');
+      assertEquals(
+          actionResult,
+          cookieStartsBlock3PIncognito ?
+              'Settings.PrivacyGuide.ChangeCookiesBlock3P' :
+              'Settings.PrivacyGuide.ChangeCookiesBlock3PIncognito');
     }
 
     page.shadowRoot!.querySelector<HTMLElement>('#nextButton')!.click();
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 0bf3284..c975f5f9 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -59,6 +59,8 @@
       "app/app.h",
       "app/app_install.cc",
       "app/app_install.h",
+      "app/app_recover.cc",
+      "app/app_recover.h",
       "app/app_server.cc",
       "app/app_server.h",
       "app/app_uninstall.cc",
diff --git a/chrome/updater/app/app_recover.cc b/chrome/updater/app/app_recover.cc
new file mode 100644
index 0000000..7a47066
--- /dev/null
+++ b/chrome/updater/app/app_recover.cc
@@ -0,0 +1,197 @@
+// Copyright 2022 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/updater/app/app_recover.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/barrier_closure.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
+#include "base/version.h"
+#include "chrome/updater/app/app.h"
+#include "chrome/updater/constants.h"
+#include "chrome/updater/persisted_data.h"
+#include "chrome/updater/prefs.h"
+#include "chrome/updater/registration_data.h"
+#include "chrome/updater/service_proxy_factory.h"
+#include "chrome/updater/update_service.h"
+
+namespace updater {
+
+namespace {
+
+class AppRecover : public App {
+ public:
+  AppRecover(const base::Version& browser_version,
+             const std::string& session_id,
+             const std::string& browser_app_id)
+      : browser_version_(browser_version),
+        session_id_(session_id),
+        browser_app_id_(browser_app_id) {}
+
+ private:
+  ~AppRecover() override = default;
+  void Initialize() override;
+  void Uninitialize() override;
+  void FirstTaskRun() override;
+
+  std::vector<RegistrationRequest> RecordRegisteredApps() const;
+  int ReinstallUpdater() const;
+  base::FilePath GetUpdaterSetupPath() const;
+  void RegisterApps(const std::vector<RegistrationRequest>& registrations,
+                    int reinstall_result);
+
+  const base::Version browser_version_;
+  const std::string session_id_;  // TODO(crbug.com/1281971): Unused.
+  const std::string browser_app_id_;
+  scoped_refptr<GlobalPrefs> global_prefs_;
+};
+
+void AppRecover::Initialize() {
+  global_prefs_ = CreateGlobalPrefs(updater_scope());
+}
+
+void AppRecover::Uninitialize() {
+  global_prefs_ = nullptr;
+}
+
+void AppRecover::FirstTaskRun() {
+  const std::vector<RegistrationRequest> registrations = RecordRegisteredApps();
+
+  // Release global prefs lock so that the updater may run concurrently.
+  global_prefs_ = nullptr;
+
+  base::ThreadPool::PostTaskAndReplyWithResult(
+      FROM_HERE, {base::MayBlock(), base::WithBaseSyncPrimitives()},
+      base::BindOnce(&AppRecover::ReinstallUpdater, this),
+      base::BindOnce(&AppRecover::RegisterApps, this, registrations));
+}
+
+std::vector<RegistrationRequest> AppRecover::RecordRegisteredApps() const {
+  scoped_refptr<PersistedData> data =
+      base::MakeRefCounted<PersistedData>(global_prefs_->GetPrefService());
+  std::vector<RegistrationRequest> apps;
+  bool found_browser_registration = false;
+  for (const std::string& app : data->GetAppIds()) {
+    if (app == kUpdaterAppId) {
+      continue;
+    }
+    RegistrationRequest registration;
+    registration.app_id = app;
+    registration.brand_code = data->GetBrandCode(app);
+    registration.brand_path = data->GetBrandPath(app);
+    registration.ap = data->GetAP(app);
+    registration.existence_checker_path = data->GetExistenceCheckerPath(app);
+    if (app == browser_app_id_) {
+      found_browser_registration = true;
+    }
+    if (app == browser_app_id_ && browser_version_.IsValid()) {
+      registration.version = browser_version_;
+    } else {
+      registration.version = data->GetProductVersion(app);
+    }
+    apps.push_back(registration);
+  }
+  if (!found_browser_registration) {
+    RegistrationRequest registration;
+    registration.app_id = browser_app_id_;
+    registration.version = browser_version_;
+    // TODO(crbug.com/1281971): registration.existence_checker_path must be set.
+    apps.emplace_back(registration);
+  }
+  return apps;
+}
+
+int AppRecover::ReinstallUpdater() const {
+  base::FilePath setup_path;
+  if (!base::PathService::Get(base::FILE_EXE, &setup_path)) {
+    LOG(ERROR) << "PathService failed.";
+    return kRecoveryExitCodePathServiceFailed;
+  }
+  int exit_code = -1;
+  base::CommandLine uninstall_command(setup_path);
+  uninstall_command.AppendSwitch(kUninstallSwitch);
+  uninstall_command.AppendSwitch(kEnableLoggingSwitch);
+  uninstall_command.AppendSwitchASCII(kLoggingModuleSwitch,
+                                      kLoggingModuleSwitchValue);
+  if (updater_scope() == UpdaterScope::kSystem) {
+    uninstall_command.AppendSwitch(kSystemSwitch);
+  }
+  if (!base::LaunchProcess(uninstall_command, {}).WaitForExit(&exit_code)) {
+    VLOG(0) << "Failed to wait for the uninstaller to exit.";
+    return kRecoveryExitCodeWaitFailedUninstall;
+  }
+  VLOG(0) << "Uninstaller returned " << exit_code << ".";
+  if (exit_code) {
+    return exit_code;
+  }
+  base::CommandLine install_command(setup_path);
+  install_command.AppendSwitch(kInstallSwitch);
+  install_command.AppendSwitch(kEnableLoggingSwitch);
+  install_command.AppendSwitchASCII(kLoggingModuleSwitch,
+                                    kLoggingModuleSwitchValue);
+  if (updater_scope() == UpdaterScope::kSystem) {
+    install_command.AppendSwitch(kSystemSwitch);
+  }
+  // TODO(crbug.com/1281971): suppress the installer's UI.
+  if (!base::LaunchProcess(install_command, {}).WaitForExit(&exit_code)) {
+    VLOG(0) << "Failed to wait for the installer to exit.";
+    return kRecoveryExitCodeWaitFailedInstall;
+  }
+  VLOG(0) << "Installer returned " << exit_code << ".";
+  return exit_code;
+}
+
+void AppRecover::RegisterApps(
+    const std::vector<RegistrationRequest>& registrations,
+    int reinstall_result) {
+  if (reinstall_result) {
+    Shutdown(reinstall_result);
+    return;
+  }
+  scoped_refptr<UpdateService> service =
+      CreateUpdateServiceProxy(updater_scope());
+  base::RepeatingClosure barrier = base::BarrierClosure(
+      registrations.size(),
+      // The service is bound to keep it alive through all callbacks.
+      base::BindOnce(
+          [](scoped_refptr<UpdateService> /*service*/,
+             base::OnceClosure shutdown) { std::move(shutdown).Run(); },
+          service, base::BindOnce(&AppRecover::Shutdown, this, 0)));
+  for (const RegistrationRequest& registration : registrations) {
+    service->RegisterApp(
+        registration,
+        base::BindOnce(
+            [](const std::string& app, base::RepeatingClosure barrier,
+               const RegistrationResponse& response) {
+              VLOG(0) << "Registration for " << app
+                      << " result: " << response.status_code << ".";
+              barrier.Run();
+            },
+            registration.app_id, barrier));
+  }
+}
+
+}  // namespace
+
+scoped_refptr<App> MakeAppRecover() {
+  const base::CommandLine* command_line =
+      base::CommandLine::ForCurrentProcess();
+  return base::MakeRefCounted<AppRecover>(
+      base::Version(command_line->GetSwitchValueASCII(kBrowserVersionSwitch)),
+      command_line->GetSwitchValueASCII(kSessionIdSwitch),
+      command_line->GetSwitchValueASCII(kAppGuidSwitch));
+}
+
+}  // namespace updater
diff --git a/chrome/updater/app/app_recover.h b/chrome/updater/app/app_recover.h
new file mode 100644
index 0000000..3d1cd818
--- /dev/null
+++ b/chrome/updater/app/app_recover.h
@@ -0,0 +1,18 @@
+// Copyright 2022 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_UPDATER_APP_APP_RECOVER_H_
+#define CHROME_UPDATER_APP_APP_RECOVER_H_
+
+#include "base/memory/scoped_refptr.h"
+
+namespace updater {
+
+class App;
+
+scoped_refptr<App> MakeAppRecover();
+
+}  // namespace updater
+
+#endif  // CHROME_UPDATER_APP_APP_RECOVER_H_
diff --git a/chrome/updater/constants.cc b/chrome/updater/constants.cc
index 5741c34..476dedab 100644
--- a/chrome/updater/constants.cc
+++ b/chrome/updater/constants.cc
@@ -47,6 +47,12 @@
 const char kServerUpdateServiceInternalSwitchValue[] = "update-internal";
 const char kServerUpdateServiceSwitchValue[] = "update";
 
+// Recovery command line arguments.
+const char kRecoverSwitch[] = "recover";
+const char kBrowserVersionSwitch[] = "browser-version";
+const char kSessionIdSwitch[] = "sessionid";
+const char kAppGuidSwitch[] = "appguid";
+
 #if BUILDFLAG(IS_WIN)
 const char kInstallFromOutDir[] = "install-from-out-dir";
 #endif  // BUILDFLAG(IS_WIN)
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h
index 44d9bb21..563e6a1 100644
--- a/chrome/updater/constants.h
+++ b/chrome/updater/constants.h
@@ -105,6 +105,18 @@
 // Runs in test mode. Currently, it exits right away.
 extern const char kTestSwitch[];
 
+// Run in recovery mode.
+extern const char kRecoverSwitch[];
+
+// The version of the program triggering recovery.
+extern const char kBrowserVersionSwitch[];
+
+// The session ID of the Omaha session triggering recovery.
+extern const char kSessionIdSwitch[];
+
+// The app ID of the program triggering recovery.
+extern const char kAppGuidSwitch[];
+
 // Disables throttling for the crash reported until the following bug is fixed:
 // https://bugs.chromium.org/p/crashpad/issues/detail?id=23
 extern const char kNoRateLimitSwitch[];
@@ -216,6 +228,12 @@
 // Client Errors.
 constexpr int kErrorRegistrationFailed = 1;
 
+// Recovery component exit codes.
+constexpr int kRecoveryExitCodeSuccess = 0;
+constexpr int kRecoveryExitCodeWaitFailedUninstall = -1;
+constexpr int kRecoveryExitCodeWaitFailedInstall = -2;
+constexpr int kRecoveryExitCodePathServiceFailed = -3;
+
 // Policy Management constants.
 // The maximum value allowed for policy AutoUpdateCheckPeriodMinutes.
 constexpr int kMaxAutoUpdateCheckPeriodMinutes = 43200;
diff --git a/chrome/updater/test/integration_test_commands.h b/chrome/updater/test/integration_test_commands.h
index 784eac0..3426b13 100644
--- a/chrome/updater/test/integration_test_commands.h
+++ b/chrome/updater/test/integration_test_commands.h
@@ -82,6 +82,8 @@
 
   virtual void SetupFakeLegacyUpdaterData() const = 0;
   virtual void ExpectLegacyUpdaterDataMigrated() const = 0;
+  virtual void RunRecoveryComponent(const std::string& app_id,
+                                    const base::Version& version) const = 0;
   virtual void ExpectLastChecked() const = 0;
   virtual void ExpectLastStarted() const = 0;
 
diff --git a/chrome/updater/test/integration_test_commands_system.cc b/chrome/updater/test/integration_test_commands_system.cc
index 4e63d829..8212902 100644
--- a/chrome/updater/test/integration_test_commands_system.cc
+++ b/chrome/updater/test/integration_test_commands_system.cc
@@ -230,6 +230,13 @@
     RunCommand("expect_legacy_updater_data_migrated");
   }
 
+  void RunRecoveryComponent(const std::string& app_id,
+                            const base::Version& version) const override {
+    RunCommand(
+        "run_recovery_component",
+        {Param("app_id", app_id), Param("version", version.GetString())});
+  }
+
   void ExpectLastChecked() const override { RunCommand("expect_last_checked"); }
 
   void ExpectLastStarted() const override { RunCommand("expect_last_started"); }
diff --git a/chrome/updater/test/integration_test_commands_user.cc b/chrome/updater/test/integration_test_commands_user.cc
index db4142ac..8e95c512 100644
--- a/chrome/updater/test/integration_test_commands_user.cc
+++ b/chrome/updater/test/integration_test_commands_user.cc
@@ -207,6 +207,11 @@
     updater::test::ExpectLegacyUpdaterDataMigrated(updater_scope_);
   }
 
+  void RunRecoveryComponent(const std::string& app_id,
+                            const base::Version& version) const override {
+    updater::test::RunRecoveryComponent(updater_scope_, app_id, version);
+  }
+
   void ExpectLastChecked() const override {
     updater::test::ExpectLastChecked(updater_scope_);
   }
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index 9982d9c..fbc0733 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -273,6 +273,11 @@
     test_commands_->ExpectLegacyUpdaterDataMigrated();
   }
 
+  void RunRecoveryComponent(const std::string& app_id,
+                            const base::Version& version) {
+    test_commands_->RunRecoveryComponent(app_id, version);
+  }
+
   void ExpectLastChecked() { test_commands_->ExpectLastChecked(); }
 
   void ExpectLastStarted() { test_commands_->ExpectLastStarted(); }
@@ -694,6 +699,17 @@
   Uninstall();
 }
 
+TEST_F(IntegrationTest, RecoveryNoUpdater) {
+  const std::string appid = "test1";
+  const base::Version version("0.1");
+  RunRecoveryComponent(appid, version);
+  WaitForUpdaterExit();
+  ExpectInstalled();
+  ExpectActiveUpdater();
+  ExpectAppVersion(appid, version);
+  Uninstall();
+}
+
 #endif  // BUILDFLAG(IS_WIN) || !defined(COMPONENT_BUILD)
 
 }  // namespace test
diff --git a/chrome/updater/test/integration_tests_helper.cc b/chrome/updater/test/integration_tests_helper.cc
index fd0936a4..d33d502 100644
--- a/chrome/updater/test/integration_tests_helper.cc
+++ b/chrome/updater/test/integration_tests_helper.cc
@@ -254,6 +254,9 @@
      WithSystemScope(Wrap(&SetupFakeLegacyUpdaterData))},
     {"expect_legacy_updater_data_migrated",
      WithSystemScope(Wrap(&ExpectLegacyUpdaterDataMigrated))},
+    {"run_recovery_component",
+     WithSwitch("version", WithSwitch("app_id", WithSystemScope(Wrap(
+                                                    &RunRecoveryComponent))))},
     {"expect_last_checked", WithSystemScope(Wrap(&ExpectLastChecked))},
     {"expect_last_started", WithSystemScope(Wrap(&ExpectLastStarted))},
   };
diff --git a/chrome/updater/test/integration_tests_impl.cc b/chrome/updater/test/integration_tests_impl.cc
index 6585ccd..c5085f9 100644
--- a/chrome/updater/test/integration_tests_impl.cc
+++ b/chrome/updater/test/integration_tests_impl.cc
@@ -580,6 +580,17 @@
   loop.Run();
 }
 
+void RunRecoveryComponent(UpdaterScope scope,
+                          const std::string& app_id,
+                          const base::Version& version) {
+  base::CommandLine command(GetSetupExecutablePath());
+  command.AppendSwitchASCII(kBrowserVersionSwitch, version.GetString());
+  command.AppendSwitchASCII(kAppGuidSwitch, app_id);
+  int exit_code = -1;
+  EXPECT_TRUE(Run(scope, command, &exit_code));
+  EXPECT_EQ(exit_code, kRecoveryExitCodeSuccess);
+}
+
 void ExpectLastChecked(UpdaterScope updater_scope) {
   EXPECT_FALSE(base::MakeRefCounted<PersistedData>(
                    CreateGlobalPrefs(updater_scope)->GetPrefService())
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h
index eb2845fc..0ab1291 100644
--- a/chrome/updater/test/integration_tests_impl.h
+++ b/chrome/updater/test/integration_tests_impl.h
@@ -195,6 +195,10 @@
 void SetupFakeLegacyUpdaterData(UpdaterScope scope);
 void ExpectLegacyUpdaterDataMigrated(UpdaterScope scope);
 
+void RunRecoveryComponent(UpdaterScope scope,
+                          const std::string& app_id,
+                          const base::Version& version);
+
 void ExpectLastChecked(UpdaterScope scope);
 
 void ExpectLastStarted(UpdaterScope scope);
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index 04a47453..56e4394f 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -18,6 +18,7 @@
 #include "build/build_config.h"
 #include "chrome/updater/app/app.h"
 #include "chrome/updater/app/app_install.h"
+#include "chrome/updater/app/app_recover.h"
 #include "chrome/updater/app/app_uninstall.h"
 #include "chrome/updater/app/app_update.h"
 #include "chrome/updater/app/app_wake.h"
@@ -170,6 +171,11 @@
     return MakeAppUninstall()->Run();
   }
 
+  if (command_line->HasSwitch(kRecoverSwitch) ||
+      command_line->HasSwitch(kBrowserVersionSwitch)) {
+    return MakeAppRecover()->Run();
+  }
+
   if (command_line->HasSwitch(kWakeSwitch)) {
     return MakeAppWake()->Run();
   }
@@ -184,24 +190,21 @@
 const char* GetUpdaterCommand(const base::CommandLine* command_line) {
   // Contains the literals which are associated with specific updater commands.
   const char* commands[] = {
-      kComServiceSwitch,
-      kCrashHandlerSwitch,
-      kInstallSwitch,
-      kServerSwitch,
-      kTagSwitch,
-      kTestSwitch,
-      kUninstallIfUnusedSwitch,
-      kUninstallSelfSwitch,
-      kUninstallSwitch,
-      kUpdateSwitch,
-      kWakeSwitch,
-      kHealthCheckSwitch,
-      kHandoffSwitch,
+      kComServiceSwitch,    kCrashHandlerSwitch, kHealthCheckSwitch,
+      kInstallSwitch,       kRecoverSwitch,      kServerSwitch,
+      kTagSwitch,           kTestSwitch,         kUninstallIfUnusedSwitch,
+      kUninstallSelfSwitch, kUninstallSwitch,    kUpdateSwitch,
+      kWakeSwitch,          kHealthCheckSwitch,  kHandoffSwitch,
   };
   const char** it = std::find_if(
       std::begin(commands), std::end(commands),
       [command_line](auto cmd) { return command_line->HasSwitch(cmd); });
-  return it != std::end(commands) ? *it : "";
+  // Return the command. As a workaround for recovery component invocations
+  // that do not pass --recover, report the browser version switch as --recover.
+  return it != std::end(commands)
+             ? *it
+             : command_line->HasSwitch(kBrowserVersionSwitch) ? kRecoverSwitch
+                                                              : "";
 }
 
 }  // namespace
diff --git a/chromeos/network/client_cert_resolver_unittest.cc b/chromeos/network/client_cert_resolver_unittest.cc
index 652e007..c3a3062 100644
--- a/chromeos/network/client_cert_resolver_unittest.cc
+++ b/chromeos/network/client_cert_resolver_unittest.cc
@@ -726,7 +726,7 @@
 
   base::Value shill_properties(base::Value::Type::DICTIONARY);
   ClientCertResolver::ResolveClientCertificateSync(
-      client_cert::CONFIG_TYPE_EAP, client_cert_config, &shill_properties);
+      client_cert::ConfigType::kEap, client_cert_config, &shill_properties);
   std::string pkcs11_id =
       GetString(shill_properties, shill::kEapCertIdProperty);
   EXPECT_EQ(test_cert_id_, pkcs11_id);
@@ -765,7 +765,7 @@
 
   base::Value shill_properties(base::Value::Type::DICTIONARY);
   ClientCertResolver::ResolveClientCertificateSync(
-      client_cert::CONFIG_TYPE_EAP, client_cert_config, &shill_properties);
+      client_cert::ConfigType::kEap, client_cert_config, &shill_properties);
   std::string pkcs11_id =
       GetString(shill_properties, shill::kEapCertIdProperty);
   EXPECT_EQ(test_cert_id_, pkcs11_id);
@@ -806,7 +806,7 @@
 
   base::Value shill_properties(base::Value::Type::DICTIONARY);
   ClientCertResolver::ResolveClientCertificateSync(
-      client_cert::CONFIG_TYPE_EAP, client_cert_config, &shill_properties);
+      client_cert::ConfigType::kEap, client_cert_config, &shill_properties);
   std::string pkcs11_id =
       GetString(shill_properties, shill::kEapCertIdProperty);
   EXPECT_EQ(std::string(), pkcs11_id);
diff --git a/chromeos/network/client_cert_util.cc b/chromeos/network/client_cert_util.cc
index a31cf4e..5487458 100644
--- a/chromeos/network/client_cert_util.cc
+++ b/chromeos/network/client_cert_util.cc
@@ -110,7 +110,7 @@
                                       ConfigType* cert_config_type,
                                       int* tpm_slot,
                                       std::string* pkcs11_id) {
-  *cert_config_type = CONFIG_TYPE_NONE;
+  *cert_config_type = ConfigType::kNone;
   *tpm_slot = -1;
   pkcs11_id->clear();
 
@@ -128,7 +128,7 @@
         provider_properties->FindStringKey(shill::kOpenVPNClientCertIdProperty);
     if (pkcs11_id_str) {
       *pkcs11_id = *pkcs11_id_str;
-      *cert_config_type = CONFIG_TYPE_OPENVPN;
+      *cert_config_type = ConfigType::kOpenVpn;
       return;
     }
 
@@ -146,7 +146,7 @@
         return;
       }
 
-      *cert_config_type = CONFIG_TYPE_L2TP_IPSEC;
+      *cert_config_type = ConfigType::kL2tpIpsec;
     }
 
     // Look for IKEv2 specific properties.
@@ -163,7 +163,7 @@
         return;
       }
 
-      *cert_config_type = CONFIG_TYPE_IKEV2;
+      *cert_config_type = ConfigType::kIkev2;
     }
     return;
   }
@@ -187,7 +187,7 @@
       return;
     }
     *pkcs11_id = GetPkcs11AndSlotIdFromEapCertId(*cert_id, tpm_slot);
-    *cert_config_type = CONFIG_TYPE_EAP;
+    *cert_config_type = ConfigType::kEap;
   }
 }
 
@@ -196,10 +196,10 @@
                         const std::string& pkcs11_id,
                         base::Value* properties) {
   switch (cert_config_type) {
-    case CONFIG_TYPE_NONE: {
+    case ConfigType::kNone: {
       return;
     }
-    case CONFIG_TYPE_OPENVPN: {
+    case ConfigType::kOpenVpn: {
       properties->SetKey(shill::kOpenVPNPinProperty,
                          base::Value(kDefaultTPMPin));
       // Note: OpenVPN does not have a slot property, see crbug.com/769550.
@@ -207,7 +207,7 @@
                          base::Value(pkcs11_id));
       break;
     }
-    case CONFIG_TYPE_L2TP_IPSEC: {
+    case ConfigType::kL2tpIpsec: {
       properties->SetKey(shill::kL2tpIpsecPinProperty,
                          base::Value(kDefaultTPMPin));
       properties->SetKey(shill::kL2tpIpsecClientCertSlotProperty,
@@ -216,7 +216,7 @@
                          base::Value(pkcs11_id));
       break;
     }
-    case CONFIG_TYPE_IKEV2: {
+    case ConfigType::kIkev2: {
       // PIN property is not used by shill for a IKEv2 service since it is a
       // fixed value.
       properties->SetKey(shill::kIKEv2ClientCertSlotProperty,
@@ -225,7 +225,7 @@
                          base::Value(pkcs11_id));
       break;
     }
-    case CONFIG_TYPE_EAP: {
+    case ConfigType::kEap: {
       properties->SetKey(shill::kEapPinProperty, base::Value(kDefaultTPMPin));
       std::string key_id =
           base::StringPrintf("%i:%s", tpm_slot, pkcs11_id.c_str());
@@ -243,17 +243,17 @@
 void SetEmptyShillProperties(const ConfigType cert_config_type,
                              base::Value* properties) {
   switch (cert_config_type) {
-    case CONFIG_TYPE_NONE: {
+    case ConfigType::kNone: {
       return;
     }
-    case CONFIG_TYPE_OPENVPN: {
+    case ConfigType::kOpenVpn: {
       properties->SetKey(shill::kOpenVPNPinProperty,
                          base::Value(std::string()));
       properties->SetKey(shill::kOpenVPNClientCertIdProperty,
                          base::Value(std::string()));
       break;
     }
-    case CONFIG_TYPE_L2TP_IPSEC: {
+    case ConfigType::kL2tpIpsec: {
       properties->SetKey(shill::kL2tpIpsecPinProperty,
                          base::Value(std::string()));
       properties->SetKey(shill::kL2tpIpsecClientCertSlotProperty,
@@ -262,7 +262,7 @@
                          base::Value(std::string()));
       break;
     }
-    case CONFIG_TYPE_IKEV2: {
+    case ConfigType::kIkev2: {
       // PIN property is not used by shill for a IKEv2 service since it is a
       // fixed value.
       properties->SetKey(shill::kIKEv2ClientCertSlotProperty,
@@ -271,7 +271,7 @@
                          base::Value(std::string()));
       break;
     }
-    case CONFIG_TYPE_EAP: {
+    case ConfigType::kEap: {
       properties->SetKey(shill::kEapPinProperty, base::Value(std::string()));
       // Shill requires both CertID and KeyID for TLS connections, despite the
       // fact that by convention they are the same ID, because one identifies
@@ -284,7 +284,7 @@
 }
 
 ClientCertConfig::ClientCertConfig()
-    : location(CONFIG_TYPE_NONE),
+    : location(ConfigType::kNone),
       client_cert_type(onc::client_cert::kClientCertTypeNone) {}
 
 ClientCertConfig::ClientCertConfig(const ClientCertConfig& other) = default;
@@ -306,7 +306,7 @@
       return;
 
     dict_with_client_cert = eap;
-    cert_config->location = CONFIG_TYPE_EAP;
+    cert_config->location = ConfigType::kEap;
   }
 
   const base::Value* vpn =
@@ -317,14 +317,15 @@
     const base::Value* l2tp = vpn->FindDictKey(::onc::vpn::kL2TP);
     if (openvpn) {
       dict_with_client_cert = openvpn;
-      cert_config->location = CONFIG_TYPE_OPENVPN;
+      cert_config->location = ConfigType::kOpenVpn;
     } else if (ipsec) {
       dict_with_client_cert = ipsec;
       // Currently we support two kinds of IPsec-based VPN:
       // - L2TP/IPsec: IKE version is 1 and |l2tp| is set;
       // - IKEv2: IKE version is 2 and |l2tp| is not set.
       // Thus we only use |l2tp| to distinguish between these two cases.
-      cert_config->location = l2tp ? CONFIG_TYPE_L2TP_IPSEC : CONFIG_TYPE_IKEV2;
+      cert_config->location =
+          l2tp ? ConfigType::kL2tpIpsec : ConfigType::kIkev2;
     } else {
       return;
     }
@@ -337,7 +338,7 @@
     if (!eap)
       return;
     dict_with_client_cert = eap;
-    cert_config->location = CONFIG_TYPE_EAP;
+    cert_config->location = ConfigType::kEap;
   }
 
   if (dict_with_client_cert) {
diff --git a/chromeos/network/client_cert_util.h b/chromeos/network/client_cert_util.h
index f5f0edb..8fc8226 100644
--- a/chromeos/network/client_cert_util.h
+++ b/chromeos/network/client_cert_util.h
@@ -23,15 +23,15 @@
 
 COMPONENT_EXPORT(CHROMEOS_NETWORK) extern const char kDefaultTPMPin[];
 
-enum ConfigType {
-  CONFIG_TYPE_NONE,
-  CONFIG_TYPE_OPENVPN,
+enum class ConfigType {
+  kNone,
+  kOpenVpn,
   // We need two separate types for L2TP/IPsec and IKEv2: both of them are used
   // for IPsec and have the same properties, the only difference is that they
   // are mapped to different sets of shill service properties.
-  CONFIG_TYPE_L2TP_IPSEC,
-  CONFIG_TYPE_IKEV2,
-  CONFIG_TYPE_EAP,
+  kL2tpIpsec,
+  kIkev2,
+  kEap,
 };
 
 struct COMPONENT_EXPORT(CHROMEOS_NETWORK) ClientCertConfig {
@@ -80,7 +80,7 @@
 // and |pkcs11_id| are filled accordingly. In case of OpenVPN or because the
 // property was not set, |tpm_slot| will be set to -1.
 // If an error occurred or no client configuration is found, |cert_config_type|
-// will be set to CONFIG_TYPE_NONE, |tpm_slot| to -1 and |pkcs11_id| to the
+// will be set to ConfigType::kNone, |tpm_slot| to -1 and |pkcs11_id| to the
 // empty string.
 COMPONENT_EXPORT(CHROMEOS_NETWORK)
 void GetClientCertFromShillProperties(const base::Value& shill_properties,
diff --git a/chromeos/network/network_cert_migrator.cc b/chromeos/network/network_cert_migrator.cc
index 27e37c0d..ef6c0ed1 100644
--- a/chromeos/network/network_cert_migrator.cc
+++ b/chromeos/network/network_cert_migrator.cc
@@ -95,16 +95,16 @@
     int configured_slot_id = -1;
     std::string pkcs11_id;
     chromeos::client_cert::ConfigType config_type =
-        chromeos::client_cert::CONFIG_TYPE_NONE;
+        chromeos::client_cert::ConfigType::kNone;
     chromeos::client_cert::GetClientCertFromShillProperties(
         properties, &config_type, &configured_slot_id, &pkcs11_id);
-    if (config_type == chromeos::client_cert::CONFIG_TYPE_NONE ||
+    if (config_type == chromeos::client_cert::ConfigType::kNone ||
         pkcs11_id.empty()) {
       return result;
     }
 
     // OpenVPN configuration doesn't have a slot id to migrate.
-    if (config_type == chromeos::client_cert::CONFIG_TYPE_OPENVPN)
+    if (config_type == chromeos::client_cert::ConfigType::kOpenVpn)
       return result;
 
     int real_slot_id = -1;
diff --git a/chromeos/network/network_connection_handler_impl.cc b/chromeos/network/network_connection_handler_impl.cc
index ca0a67f..1967623 100644
--- a/chromeos/network/network_connection_handler_impl.cc
+++ b/chromeos/network/network_connection_handler_impl.cc
@@ -5,6 +5,7 @@
 #include "chromeos/network/network_connection_handler_impl.h"
 
 #include <memory>
+#include <ostream>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -70,14 +71,14 @@
   const base::Value* provider_properties =
       properties.FindDictKey(shill::kProviderProperty);
   switch (cert_config_type) {
-    case client_cert::CONFIG_TYPE_NONE:
+    case client_cert::ConfigType::kNone:
       return true;
-    case client_cert::CONFIG_TYPE_OPENVPN:
+    case client_cert::ConfigType::kOpenVpn:
       // We don't know whether a pasphrase or certificates are required, so
       // always return true here (otherwise we will never attempt to connect).
       // TODO(stevenjb/cernekee): Fix this?
       return true;
-    case client_cert::CONFIG_TYPE_L2TP_IPSEC: {
+    case client_cert::ConfigType::kL2tpIpsec: {
       if (!provider_properties)
         return false;
 
@@ -85,7 +86,7 @@
           *provider_properties, shill::kL2tpIpsecClientCertIdProperty);
       return !client_cert_id.empty();
     }
-    case client_cert::CONFIG_TYPE_EAP: {
+    case client_cert::ConfigType::kEap: {
       std::string cert_id =
           GetStringFromDictionary(properties, shill::kEapCertIdProperty);
       std::string key_id =
@@ -94,7 +95,7 @@
           GetStringFromDictionary(properties, shill::kEapIdentityProperty);
       return !cert_id.empty() && !key_id.empty() && !identity.empty();
     }
-    case client_cert::CONFIG_TYPE_IKEV2: {
+    case client_cert::ConfigType::kIkev2: {
       if (!provider_properties)
         return false;
 
@@ -173,6 +174,27 @@
          vpn_type == shill::kProviderOpenVpn;
 }
 
+std::ostream& operator<<(std::ostream& stream, client_cert::ConfigType type) {
+  switch (type) {
+    case client_cert::ConfigType::kNone:
+      stream << "None";
+      return stream;
+    case client_cert::ConfigType::kOpenVpn:
+      stream << "OpenVPN";
+      return stream;
+    case client_cert::ConfigType::kL2tpIpsec:
+      stream << "L2TP/IPsec";
+      return stream;
+    case client_cert::ConfigType::kIkev2:
+      stream << "IKEv2";
+      return stream;
+    case client_cert::ConfigType::kEap:
+      stream << "EAP";
+      return stream;
+  }
+  NOTREACHED();
+}
+
 }  // namespace
 
 NetworkConnectionHandlerImpl::ConnectRequest::ConnectRequest(
@@ -674,12 +696,12 @@
                                        &cert_config_from_policy);
   }
 
-  client_cert::ConfigType client_cert_type = client_cert::CONFIG_TYPE_NONE;
+  client_cert::ConfigType client_cert_type = client_cert::ConfigType::kNone;
   if (*type == shill::kTypeVPN) {
     // Check the VPN types that may use the client cert: OpenVPN, L2TP/IPsec,
     // and IKEv2.
     if (vpn_provider_type == shill::kProviderOpenVpn) {
-      client_cert_type = client_cert::CONFIG_TYPE_OPENVPN;
+      client_cert_type = client_cert::ConfigType::kOpenVpn;
     } else if (vpn_provider_type == shill::kProviderL2tpIpsec) {
       // L2TP/IPSec only requires a certificate if one is specified in ONC
       // or one was configured by the UI. Otherwise it is L2TP/IPSec with
@@ -687,24 +709,24 @@
       if (!vpn_client_cert_id.empty() ||
           cert_config_from_policy.client_cert_type !=
               ::onc::client_cert::kClientCertTypeNone) {
-        client_cert_type = client_cert::CONFIG_TYPE_L2TP_IPSEC;
+        client_cert_type = client_cert::ConfigType::kL2tpIpsec;
       }
     } else if (vpn_provider_type == shill::kProviderIKEv2) {
       const std::string* auth_type =
           properties->FindStringKey(shill::kIKEv2AuthenticationTypeProperty);
       if (auth_type && *auth_type == shill::kIKEv2AuthenticationTypeCert) {
-        client_cert_type = client_cert::CONFIG_TYPE_IKEV2;
+        client_cert_type = client_cert::ConfigType::kIkev2;
       }
     }
   } else if (*type == shill::kTypeWifi) {
     const std::string* security_class =
         properties->FindStringKey(shill::kSecurityClassProperty);
     if (security_class && *security_class == shill::kSecurity8021x)
-      client_cert_type = client_cert::CONFIG_TYPE_EAP;
+      client_cert_type = client_cert::ConfigType::kEap;
   }
 
   base::DictionaryValue config_properties;
-  if (client_cert_type != client_cert::CONFIG_TYPE_NONE) {
+  if (client_cert_type != client_cert::ConfigType::kNone) {
     // Note: if we get here then a certificate *may* be required, so we want
     // to ensure that certificates have loaded successfully before attempting
     // to connect.
@@ -759,7 +781,7 @@
 
     // If it's L2TP/IPsec PSK, there is no properties to configure, so proceed
     // to connect.
-    if (client_cert_type == client_cert::CONFIG_TYPE_NONE) {
+    if (client_cert_type == client_cert::ConfigType::kNone) {
       CallShillConnect(service_path);
       return;
     }
diff --git a/chromeos/network/onc/onc_translator_onc_to_shill.cc b/chromeos/network/onc/onc_translator_onc_to_shill.cc
index 7ad0ccb2..5e1148a 100644
--- a/chromeos/network/onc/onc_translator_onc_to_shill.cc
+++ b/chromeos/network/onc/onc_translator_onc_to_shill.cc
@@ -216,7 +216,7 @@
   shill_dictionary_->SetKey(shill::kOpenVPNRemoteCertKUProperty,
                             base::Value(cert_ku));
 
-  SetClientCertProperties(client_cert::CONFIG_TYPE_OPENVPN, onc_object_,
+  SetClientCertProperties(client_cert::ConfigType::kOpenVpn, onc_object_,
                           shill_dictionary_);
 
   const std::string* compression_algorithm =
@@ -248,7 +248,7 @@
 void LocalTranslator::TranslateIPsec() {
   const auto ike_version = onc_object_->FindIntKey(::onc::ipsec::kIKEVersion);
   if (ike_version.has_value() && ike_version.value() == 2) {
-    SetClientCertProperties(client_cert::CONFIG_TYPE_IKEV2, onc_object_,
+    SetClientCertProperties(client_cert::ConfigType::kIkev2, onc_object_,
                             shill_dictionary_);
 
     // The translation table set in this object is for L2TP/IPsec, so we do the
@@ -264,7 +264,7 @@
                             shill::kIKEv2RemoteIdentityProperty);
   } else {
     // For L2TP/IPsec.
-    SetClientCertProperties(client_cert::CONFIG_TYPE_L2TP_IPSEC, onc_object_,
+    SetClientCertProperties(client_cert::ConfigType::kL2tpIpsec, onc_object_,
                             shill_dictionary_);
     CopyFieldsAccordingToSignature();
   }
@@ -367,7 +367,7 @@
     }
   }
 
-  SetClientCertProperties(client_cert::CONFIG_TYPE_EAP, onc_object_,
+  SetClientCertProperties(client_cert::ConfigType::kEap, onc_object_,
                           shill_dictionary_);
 
   // Set shill::kEapUseLoginPasswordProperty according to whether or not the
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 7266f6c..cb82850 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-99-4827.0-1643629123-benchmark-99.0.4844.17-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-99-4827.0-1643629123-benchmark-99.0.4844.19-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 82a0d3e..8fe2f674 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-99-4827.0-1643628001-benchmark-99.0.4844.17-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-99-4827.0-1643628001-benchmark-99.0.4844.19-r1-redacted.afdo.xz
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 80cb7f6e..4dd3f69c 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -15973,7 +15973,7 @@
       'owners': ['mlerman@chromium.org', 'zmin@chromium.org'],
       'type': 'main',
       'schema': { 'type': 'boolean' },
-      'supported_on': ['chrome.*:38-'],
+      'supported_on': ['chrome.*:38-', 'chrome_os:100-'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -15982,9 +15982,11 @@
       'id': 275,
       'caption': '''Enable guest mode in browser''',
       'tags': [],
-      'desc': '''If this policy is set to true or not configured, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will enable guest logins. Guest logins are <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profiles where all windows are in incognito mode.
+      'desc': '''If this policy is set to true or not configured, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> and <ph name="LACROS_NAME">Lacros</ph> will enable guest logins. Guest logins are <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> profiles where all windows are in incognito mode.
 
-      If this policy is set to false, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will not allow guest profiles to be started.''',
+      If this policy is set to false, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> and <ph name="LACROS_NAME">Lacros</ph> will not allow guest profiles to be started.
+
+      Note: If this policy is not configured or set to true, but <ph name="LACROS_SECONDARY_PROFILES_ALLOWED_POLICY_NAME">LacrosSecondaryProfilesAllowed</ph> is set to false, <ph name="LACROS_NAME">Lacros</ph> will not allow guest profiles to be started.''',
     },
     {
       'name': 'BrowserGuestModeEnforced',
@@ -16009,7 +16011,7 @@
       'owners': ['mlerman@chromium.org', 'zmin@chromium.org'],
       'type': 'main',
       'schema': { 'type': 'boolean' },
-      'supported_on': ['chrome.*:39-'],
+      'supported_on': ['chrome.*:39-', 'chrome_os:100-'],
       'features': {
         'dynamic_refresh': True,
         'per_profile': False,
@@ -16018,9 +16020,11 @@
       'id': 276,
       'caption': '''Enable add person in user manager''',
       'tags': [],
-      'desc': '''If this policy is set to true or not configured, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will allow Add Person from the user manager.
+      'desc': '''If this policy is set to true or not configured, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> and <ph name="LACROS_NAME">Lacros</ph> will allow to add a new person from the user manager.
 
-      If this policy is set to false, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> will not allow creation of new profiles from the user manager.''',
+      If this policy is set to false, <ph name="PRODUCT_NAME">$1<ex>Google Chrome</ex></ph> and <ph name="LACROS_NAME">Lacros</ph> will not allow adding a new person from the user manager.
+
+      Note: If this policy is not configured or set to true, but <ph name="LACROS_SECONDARY_PROFILES_ALLOWED_POLICY_NAME">LacrosSecondaryProfilesAllowed</ph> is set to false, <ph name="LACROS_NAME">Lacros</ph> will not allow adding a new person from the user manager.''',
     },
     {
       'name': 'ForceBrowserSignin',
@@ -27592,11 +27596,11 @@
       'tags': [],
       'desc': '''This setting allows users to create and use secondary profiles, and use guest mode in the <ph name="LACROS_NAME">Lacros</ph> browser.
 
-      Similar to both BrowserAddPersonEnabled and BrowserGuestModeEnabled, if this policy is set to false or unset, the user cannot create or use secondary profiles, and use guest mode. Previously created secondary profiles, if any, will be unavailable.
+      Similar to both <ph name="BROWSER_ADD_PERSON_ENABLED_POLICY_NAME">BrowserAddPersonEnabled</ph> and <ph name="BROWSER_GUEST_MODE_ENABLED_POLICY_NAME">BrowserGuestModeEnabled</ph>, if this policy is set to false or unset, the user cannot create or use secondary profiles, and use guest mode. Previously created secondary profiles, if any, will be unavailable.
 
       If this policy is set to true, the user can create and use secondary profiles, and use guest mode.
 
-      Note, that if this policy is set to true but BrowserAddPersonEnabled is set to false, the user cannot create secondary profiles. The same for BrowserGuestModeEnabled and guest mode.''',
+      Note: If this policy is set to true but <ph name="BROWSER_ADD_PERSON_ENABLED_POLICY_NAME">BrowserAddPersonEnabled</ph> is set to false, the user cannot create secondary profiles. The same for <ph name="BROWSER_GUEST_MODE_ENABLED_POLICY_NAME">BrowserGuestModeEnabled</ph> and guest mode.''',
     },
     {
       'name': 'LacrosAvailability',
diff --git a/components/services/app_service/public/cpp/app_types.cc b/components/services/app_service/public/cpp/app_types.cc
index 672aa81..2ff6d5d 100644
--- a/components/services/app_service/public/cpp/app_types.cc
+++ b/components/services/app_service/public/cpp/app_types.cc
@@ -46,6 +46,7 @@
   app->paused = paused;
   app->intent_filters = CloneIntentFilters(intent_filters);
   app->resize_locked = resize_locked;
+  app->window_mode = window_mode;
 
   return app;
 }
@@ -180,6 +181,20 @@
   }
 }
 
+WindowMode ConvertMojomWindowModeToWindowMode(
+    apps::mojom::WindowMode mojom_window_mode) {
+  switch (mojom_window_mode) {
+    case apps::mojom::WindowMode::kUnknown:
+      return WindowMode::kUnknown;
+    case apps::mojom::WindowMode::kWindow:
+      return WindowMode::kWindow;
+    case apps::mojom::WindowMode::kBrowser:
+      return WindowMode::kBrowser;
+    case apps::mojom::WindowMode::kTabbedWindow:
+      return WindowMode::kTabbedWindow;
+  }
+}
+
 absl::optional<bool> GetOptionalBool(
     const apps::mojom::OptionalBool& mojom_optional_bool) {
   absl::optional<bool> optional_bool;
@@ -247,6 +262,7 @@
   }
 
   app->resize_locked = GetOptionalBool(mojom_app->resize_locked);
+  app->window_mode = ConvertMojomWindowModeToWindowMode(mojom_app->window_mode);
 
   return app;
 }
diff --git a/components/services/app_service/public/cpp/app_types.h b/components/services/app_service/public/cpp/app_types.h
index 5c179d0..965d3641 100644
--- a/components/services/app_service/public/cpp/app_types.h
+++ b/components/services/app_service/public/cpp/app_types.h
@@ -97,6 +97,17 @@
   kMaxValue = kBrowser,
 };
 
+// The window mode that each app will open in.
+enum class WindowMode {
+  kUnknown = 0,
+  // Opens in a standalone window
+  kWindow,
+  // Opens in the default web browser
+  kBrowser,
+  // Opens in a tabbed app window
+  kTabbedWindow,
+};
+
 struct COMPONENT_EXPORT(APP_TYPES) App {
   App(AppType app_type, const std::string& app_id);
 
@@ -181,7 +192,8 @@
   // operations will be restricted.
   absl::optional<bool> resize_locked;
 
-  // TODO(crbug.com/1253250): Add other App struct fields.
+  // Whether the app's display mode is in the browser or otherwise.
+  WindowMode window_mode = WindowMode::kUnknown;
 
   // When adding new fields to the App type, the `Clone` function and the
   // `AppUpdate` class should also be updated.
@@ -208,6 +220,10 @@
     apps::mojom::InstallSource mojom_install_source);
 
 COMPONENT_EXPORT(APP_TYPES)
+WindowMode ConvertMojomWindowModeToWindowMode(
+    apps::mojom::WindowMode mojom_window_mode);
+
+COMPONENT_EXPORT(APP_TYPES)
 absl::optional<bool> GetOptionalBool(
     const apps::mojom::OptionalBool& mojom_optional_bool);
 
diff --git a/components/services/app_service/public/cpp/app_update.cc b/components/services/app_service/public/cpp/app_update.cc
index 707ec28..f3c08f23 100644
--- a/components/services/app_service/public/cpp/app_update.cc
+++ b/components/services/app_service/public/cpp/app_update.cc
@@ -222,7 +222,8 @@
     state->intent_filters = CloneIntentFilters(delta->intent_filters);
   }
 
-  SET_OPTIONAL_VALUE(resize_locked);
+  SET_OPTIONAL_VALUE(resize_locked)
+  SET_ENUM_VALUE(window_mode, WindowMode::kUnknown)
 
   // When adding new fields to the App type, this function should also be
   // updated.
@@ -908,6 +909,10 @@
   return apps::mojom::WindowMode::kUnknown;
 }
 
+apps::WindowMode AppUpdate::GetWindowMode() const {
+  GET_VALUE_WITH_DEFAULT_VALUE(window_mode, WindowMode::kUnknown)
+}
+
 bool AppUpdate::WindowModeChanged() const {
   return mojom_delta_ &&
          (mojom_delta_->window_mode != apps::mojom::WindowMode::kUnknown) &&
diff --git a/components/services/app_service/public/cpp/app_update.h b/components/services/app_service/public/cpp/app_update.h
index adff68d..1054a40 100644
--- a/components/services/app_service/public/cpp/app_update.h
+++ b/components/services/app_service/public/cpp/app_update.h
@@ -198,6 +198,7 @@
   bool ResizeLockedChanged() const;
 
   apps::mojom::WindowMode WindowMode() const;
+  apps::WindowMode GetWindowMode() const;
   bool WindowModeChanged() const;
 
   const ::AccountId& AccountId() const;
diff --git a/components/services/app_service/public/cpp/app_update_unittest.cc b/components/services/app_service/public/cpp/app_update_unittest.cc
index f47c5cb..3dc59f5f 100644
--- a/components/services/app_service/public/cpp/app_update_unittest.cc
+++ b/components/services/app_service/public/cpp/app_update_unittest.cc
@@ -119,6 +119,8 @@
 
   absl::optional<bool> expect_resize_locked_;
 
+  WindowMode expect_window_mode_;
+
   AccountId account_id_ = AccountId::FromUserEmail("test@gmail.com");
 
   void CheckExpects(const AppUpdate& u) {
@@ -180,6 +182,8 @@
 
     EXPECT_EQ(expect_resize_locked_, u.GetResizeLocked());
 
+    EXPECT_EQ(expect_window_mode_, u.GetWindowMode());
+
     EXPECT_EQ(account_id_, u.AccountId());
   }
 
@@ -216,6 +220,7 @@
     expect_paused_ = absl::nullopt;
     expect_intent_filters_.clear();
     expect_resize_locked_ = absl::nullopt;
+    expect_window_mode_ = WindowMode::kUnknown;
     CheckExpects(u);
 
     if (delta) {
@@ -819,6 +824,26 @@
       EXPECT_EQ(expect_resize_locked_, state->resize_locked);
       CheckExpects(u);
     }
+
+    // WindowMode tests.
+
+    if (state) {
+      state->window_mode = WindowMode::kBrowser;
+      expect_window_mode_ = WindowMode::kBrowser;
+      CheckExpects(u);
+    }
+
+    if (delta) {
+      delta->window_mode = WindowMode::kWindow;
+      expect_window_mode_ = WindowMode::kWindow;
+      CheckExpects(u);
+    }
+
+    if (state) {
+      apps::AppUpdate::Merge(state, delta);
+      EXPECT_EQ(expect_window_mode_, state->window_mode);
+      CheckExpects(u);
+    }
   }
 };
 
diff --git a/components/services/app_service/public/mojom/types.mojom b/components/services/app_service/public/mojom/types.mojom
index b419afc..99048dd 100644
--- a/components/services/app_service/public/mojom/types.mojom
+++ b/components/services/app_service/public/mojom/types.mojom
@@ -91,7 +91,9 @@
   // Whether the app's display mode is in the browser or otherwise.
   WindowMode window_mode;
 
-  // When adding new fields, also update the Merge method and other helpers in
+  // When adding new fields, also update the App struct in
+  // components/services/app_service/public/cpp/app_types.* and the Merge method
+  // and other helpers in
   // components/services/app_service/public/cpp/app_update.*
 };
 
diff --git a/components/user_manager/known_user.cc b/components/user_manager/known_user.cc
index 8ca3d38c..f1a1eff 100644
--- a/components/user_manager/known_user.cc
+++ b/components/user_manager/known_user.cc
@@ -273,7 +273,7 @@
 }
 
 const std::string* KnownUser::FindStringPath(const AccountId& account_id,
-                                             base::StringPiece path) {
+                                             base::StringPiece path) const {
   const base::Value* user_pref_dict = FindPrefs(account_id);
   if (!user_pref_dict)
     return nullptr;
@@ -695,11 +695,9 @@
   SetStringPref(account_id, kPasswordSyncToken, token);
 }
 
-std::string KnownUser::GetPasswordSyncToken(const AccountId& account_id) {
-  if (const std::string* token = FindStringPath(account_id, kPasswordSyncToken))
-    return *token;
-  // Return empty string if sync token was not set for the account yet.
-  return std::string();
+const std::string* KnownUser::GetPasswordSyncToken(
+    const AccountId& account_id) const {
+  return FindStringPath(account_id, kPasswordSyncToken);
 }
 
 void KnownUser::SetOnboardingCompletedVersion(
@@ -1013,40 +1011,6 @@
   return KnownUser(local_state).ClearProfileRequiresPolicy(account_id);
 }
 
-void SetLastOnlineSignin(const AccountId& account_id, base::Time time) {
-  PrefService* local_state = GetLocalStateLegacy();
-  // Local State may not be initialized in tests.
-  if (!local_state)
-    return;
-  return KnownUser(local_state).SetLastOnlineSignin(account_id, time);
-}
-
-base::Time GetLastOnlineSignin(const AccountId& account_id) {
-  PrefService* local_state = GetLocalStateLegacy();
-  // Local State may not be initialized in tests.
-  if (!local_state)
-    return base::Time();
-  return KnownUser(local_state).GetLastOnlineSignin(account_id);
-}
-
-void SetOfflineSigninLimit(const AccountId& account_id,
-                           absl::optional<base::TimeDelta> time_delta) {
-  PrefService* local_state = GetLocalStateLegacy();
-  // Local State may not be initialized in tests.
-  if (!local_state)
-    return;
-  return KnownUser(local_state).SetOfflineSigninLimit(account_id, time_delta);
-}
-
-absl::optional<base::TimeDelta> GetOfflineSigninLimit(
-    const AccountId& account_id) {
-  PrefService* local_state = GetLocalStateLegacy();
-  // Local State may not be initialized in tests.
-  if (!local_state)
-    return absl::nullopt;
-  return KnownUser(local_state).GetOfflineSigninLimit(account_id);
-}
-
 void SetIsEnterpriseManaged(const AccountId& account_id,
                             bool is_enterprise_managed) {
   PrefService* local_state = GetLocalStateLegacy();
@@ -1084,22 +1048,5 @@
       .SetUserLastLoginInputMethodId(account_id, input_method_id);
 }
 
-void SetPasswordSyncToken(const AccountId& account_id,
-                          const std::string& token) {
-  PrefService* local_state = GetLocalStateLegacy();
-  // Local State may not be initialized in tests.
-  if (!local_state)
-    return;
-  return KnownUser(local_state).SetPasswordSyncToken(account_id, token);
-}
-
-std::string GetPasswordSyncToken(const AccountId& account_id) {
-  PrefService* local_state = GetLocalStateLegacy();
-  // Local State may not be initialized in tests.
-  if (!local_state)
-    return std::string();
-  return KnownUser(local_state).GetPasswordSyncToken(account_id);
-}
-
 }  // namespace known_user
 }  // namespace user_manager
diff --git a/components/user_manager/known_user.h b/components/user_manager/known_user.h
index 8e0ec7be..d4ae20a8 100644
--- a/components/user_manager/known_user.h
+++ b/components/user_manager/known_user.h
@@ -60,7 +60,7 @@
 
   // Returns `nullptr` if value is not found or not a string.
   const std::string* FindStringPath(const AccountId& account_id,
-                                    base::StringPiece path);
+                                    base::StringPiece path) const;
 
   // Returns true if |account_id| preference by |path| does exist,
   // fills in |out_value|. Otherwise returns false.
@@ -231,7 +231,7 @@
   void SetPasswordSyncToken(const AccountId& account_id,
                             const std::string& token);
 
-  std::string GetPasswordSyncToken(const AccountId& account_id);
+  const std::string* GetPasswordSyncToken(const AccountId& account_id) const;
 
   // Saves the current major version as the version in which the user completed
   // the onboarding flow.
@@ -405,26 +405,6 @@
 ClearProfileRequiresPolicy(const AccountId& account_id);
 
 // TODO(https://crbug.com/1150434): Deprecated, use
-// KnownUser::SetLastOnlineSignin instead.
-void USER_MANAGER_EXPORT SetLastOnlineSignin(const AccountId& account_id,
-                                             base::Time time);
-
-// TODO(https://crbug.com/1150434): Deprecated, use
-// KnownUser::GetLastOnlineSignin instead.
-base::Time USER_MANAGER_EXPORT GetLastOnlineSignin(const AccountId& account_id);
-
-// TODO(https://crbug.com/1150434): Deprecated, use
-// KnownUser::SetOfflineSigninLimit instead.
-void USER_MANAGER_EXPORT
-SetOfflineSigninLimit(const AccountId& account_id,
-                      absl::optional<base::TimeDelta> time_limit);
-
-// TODO(https://crbug.com/1150434): Deprecated, use
-// KnownUser::GetOfflineSigninLimit instead.
-absl::optional<base::TimeDelta> USER_MANAGER_EXPORT
-GetOfflineSigninLimit(const AccountId& account_id);
-
-// TODO(https://crbug.com/1150434): Deprecated, use
 // KnownUser::SetIsEnterpriseManaged instead.
 void USER_MANAGER_EXPORT SetIsEnterpriseManaged(const AccountId& account_id,
                                                 bool is_enterprise_managed);
@@ -443,18 +423,6 @@
 SetUserLastLoginInputMethodId(const AccountId& account_id,
                               const std::string& input_method_id);
 
-// Setter and getter for password sync token used for syncing SAML passwords
-// across multiple user devices.
-// TODO(https://crbug.com/1150434): Deprecated, use
-// KnownUser::SetPasswordSyncToken instead.
-void USER_MANAGER_EXPORT SetPasswordSyncToken(const AccountId& account_id,
-                                              const std::string& token);
-
-// TODO(https://crbug.com/1150434): Deprecated, use
-// KnownUser::GetPasswordSyncToken instead.
-std::string USER_MANAGER_EXPORT
-GetPasswordSyncToken(const AccountId& account_id);
-
 }  // namespace known_user
 }  // namespace user_manager
 
diff --git a/components/user_manager/known_user_unittest.cc b/components/user_manager/known_user_unittest.cc
index 29d6b7ec..648b601 100644
--- a/components/user_manager/known_user_unittest.cc
+++ b/components/user_manager/known_user_unittest.cc
@@ -508,11 +508,11 @@
 
 TEST_F(KnownUserTest, PasswordSyncToken) {
   KnownUser known_user(local_state());
-  EXPECT_EQ(known_user.GetPasswordSyncToken(kDefaultAccountId), std::string());
+  EXPECT_FALSE(known_user.GetPasswordSyncToken(kDefaultAccountId));
 
   known_user.SetPasswordSyncToken(kDefaultAccountId, "test");
 
-  EXPECT_EQ(known_user.GetPasswordSyncToken(kDefaultAccountId), "test");
+  EXPECT_EQ(*known_user.GetPasswordSyncToken(kDefaultAccountId), "test");
 }
 
 TEST_F(KnownUserTest, CleanEphemeralUsersRemovesEphemeralAdOnly) {
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 0afe852d..e2bbf2e88 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -2109,8 +2109,6 @@
     sources += [
       "../app_shim_remote_cocoa/web_drag_source_mac.h",
       "../app_shim_remote_cocoa/web_drag_source_mac.mm",
-      "accessibility/accessibility_event_recorder_mac.h",
-      "accessibility/accessibility_event_recorder_mac.mm",
       "accessibility/browser_accessibility_cocoa.h",
       "accessibility/browser_accessibility_cocoa.mm",
       "accessibility/browser_accessibility_mac.h",
diff --git a/content/browser/accessibility/accessibility_event_recorder_mac.h b/content/browser/accessibility/accessibility_event_recorder_mac.h
deleted file mode 100644
index f26b4d79..0000000
--- a/content/browser/accessibility/accessibility_event_recorder_mac.h
+++ /dev/null
@@ -1,54 +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.
-
-#ifndef CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_MAC_H_
-#define CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_MAC_H_
-
-#include "base/mac/scoped_cftyperef.h"
-#include "content/browser/accessibility/browser_accessibility_cocoa.h"
-#include "content/common/content_export.h"
-#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
-#include "ui/accessibility/platform/inspect/ax_inspect.h"
-
-@class BrowserAccessibilityCocoa;
-
-namespace content {
-
-// Implementation of AccessibilityEventRecorder that uses AXObserver to
-// watch for NSAccessibility events.
-class CONTENT_EXPORT AccessibilityEventRecorderMac
-    : public ui::AXEventRecorder {
- public:
-  AccessibilityEventRecorderMac(base::ProcessId pid,
-                                const ui::AXTreeSelector& selector);
-
-  AccessibilityEventRecorderMac(const AccessibilityEventRecorderMac&) = delete;
-  AccessibilityEventRecorderMac& operator=(
-      const AccessibilityEventRecorderMac&) = delete;
-
-  ~AccessibilityEventRecorderMac() override;
-
-  // Callback executed every time we receive an event notification.
-  void EventReceived(AXUIElementRef element,
-                     CFStringRef notification,
-                     CFDictionaryRef user_info);
-  static std::string SerializeTextSelectionChangedProperties(
-      CFDictionaryRef user_info);
-
- private:
-  // Add one notification to the list of notifications monitored by our
-  // observer.
-  void AddNotification(NSString* notification);
-
-  // The AXUIElement for the Chrome application.
-  base::ScopedCFTypeRef<AXUIElementRef> application_;
-
-  // The AXObserver we use to monitor AX notifications.
-  base::ScopedCFTypeRef<AXObserverRef> observer_ref_;
-  CFRunLoopSourceRef observer_run_loop_source_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_ACCESSIBILITY_ACCESSIBILITY_EVENT_RECORDER_MAC_H_
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index c623fe0..45366913 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -894,7 +894,7 @@
 // thoroughly tested and convert to tree positions.
 BrowserAccessibility::AXPosition
 BrowserAccessibility::CreatePositionForSelectionAt(int offset) const {
-  return CreateTextPositionAt(offset)->AsTextSelectionPosition();
+  return CreateTextPositionAt(offset)->AsDomSelectionPosition();
 }
 
 const std::string& BrowserAccessibility::GetName() const {
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index e53f02c..0fea479 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -1117,7 +1117,7 @@
 
   // "ax::mojom::MoveDirection" is only relevant on platforms that use object
   // replacement characters in the accessibility tree. Mac is not one of them.
-  const AXPosition caretPosition = range.focus()->LowestCommonAncestor(
+  const AXPosition caretPosition = range.focus()->LowestCommonAncestorPosition(
       *_owner->CreateTextPositionAt(0), ax::mojom::MoveDirection::kForward);
   DCHECK(!caretPosition->IsNullPosition())
       << "Calling HasVisibleCaretOrSelection() should have ensured that there "
@@ -1675,7 +1675,7 @@
 
   // "ax::mojom::MoveDirection" is only relevant on platforms that use object
   // replacement characters in the accessibility tree. Mac is not one of them.
-  const AXPosition startPosition = range.anchor()->LowestCommonAncestor(
+  const AXPosition startPosition = range.anchor()->LowestCommonAncestorPosition(
       *_owner->CreateTextPositionAt(0), ax::mojom::MoveDirection::kForward);
   DCHECK(!startPosition->IsNullPosition())
       << "Calling HasVisibleCaretOrSelection() should have ensured that there "
@@ -1692,9 +1692,9 @@
 
   BrowserAccessibilityManager* manager = _owner->manager();
   manager->SetSelection(BrowserAccessibility::AXRange(
-      _owner->CreateTextPositionAt(range.location)->AsTextSelectionPosition(),
+      _owner->CreateTextPositionAt(range.location)->AsDomSelectionPosition(),
       _owner->CreateTextPositionAt(NSMaxRange(range))
-          ->AsTextSelectionPosition()));
+          ->AsDomSelectionPosition()));
 }
 
 - (id)selectedTextMarkerRange {
@@ -3149,8 +3149,8 @@
     if (range.IsNull())
       return;
     BrowserAccessibilityManager* manager = _owner->manager();
-    manager->SetSelection(AXRange(range.anchor()->AsTextSelectionPosition(),
-                                  range.focus()->AsTextSelectionPosition()));
+    manager->SetSelection(AXRange(range.anchor()->AsDomSelectionPosition(),
+                                  range.focus()->AsDomSelectionPosition()));
   }
 }
 
diff --git a/content/browser/browsing_instance.h b/content/browser/browsing_instance.h
index d4c0aea2..a5556ad 100644
--- a/content/browser/browsing_instance.h
+++ b/content/browser/browsing_instance.h
@@ -241,6 +241,10 @@
   // should only be set if kProcessSharingWithStrictSiteInstances is not
   // enabled. This is a raw pointer to avoid a reference cycle between the
   // BrowsingInstance and the SiteInstanceImpl.
+  // Note: This can hold cross-origin isolated SiteInstances. It will however
+  // only do so under certain specific circumstances (for example on a low
+  // memory device), which don't use the COOP isolation heuristic that normally
+  // prevents the use of default SiteInstances for cross-origin isolated pages.
   raw_ptr<SiteInstanceImpl> default_site_instance_;
 
   // The cross-origin isolation status of the BrowsingInstance. This indicates
diff --git a/content/browser/cross_origin_opener_policy_browsertest.cc b/content/browser/cross_origin_opener_policy_browsertest.cc
index 0d878b5..93c2bdd 100644
--- a/content/browser/cross_origin_opener_policy_browsertest.cc
+++ b/content/browser/cross_origin_opener_policy_browsertest.cc
@@ -147,7 +147,6 @@
     return web_contents()->GetMainFrame();
   }
 
- private:
   void SetUpOnMainThread() override {
     ContentBrowserTest::SetUpOnMainThread();
     mock_cert_verifier_.mock_cert_verifier()->set_default_result(net::OK);
@@ -166,6 +165,7 @@
     ASSERT_TRUE(https_server()->Start());
   }
 
+ private:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     ContentBrowserTest::SetUpCommandLine(command_line);
     mock_cert_verifier_.SetUpCommandLine(command_line);
@@ -222,6 +222,59 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
+// Certain features are only active when SiteIsolation is off or restricted.
+// This is the case for example for Default SiteInstances that are used on
+// Android to limit the number of processes. Testing these particularities of
+// the process model and their interaction with cross-origin isolation requires
+// to disable SiteIsolation.
+class NoSiteIsolationCrossOriginIsolationBrowserTest
+    : public CrossOriginOpenerPolicyBrowserTest {
+ public:
+  NoSiteIsolationCrossOriginIsolationBrowserTest() {
+    // Disable the heuristic to isolate COOP pages from the default
+    // SiteInstance. This is otherwise on by default on Android.
+    feature_list_.InitWithFeatures(
+        {}, {features::kSiteIsolationForCrossOriginOpenerPolicy});
+  }
+
+  void SetUpOnMainThread() override {
+    CrossOriginOpenerPolicyBrowserTest::SetUpOnMainThread();
+    original_client_ = SetBrowserClientForTesting(&browser_client_);
+
+    // The custom ContentBrowserClient above typically ensures that this test
+    // runs without strict site isolation, but it's still possible to
+    // inadvertently override this when running with --site-per-process on the
+    // command line. This might happen on try bots, so these tests take this
+    // into account to prevent failures, but this is not an intended
+    // configuration for these tests.
+    if (AreAllSitesIsolatedForTesting()) {
+      LOG(WARNING) << "This test should be run without --site-per-process, "
+                   << "as it's designed to exercise code paths when strict "
+                   << "site isolation is turned off.";
+    }
+  }
+
+  void TearDownOnMainThread() override {
+    CrossOriginOpenerPolicyBrowserTest::TearDownOnMainThread();
+    SetBrowserClientForTesting(original_client_);
+  }
+
+  // A custom ContentBrowserClient to turn off strict site isolation, since
+  // process model differences exist in environments like Android. Note that
+  // kSitePerProcess is a higher-layer feature, so we can't just disable it
+  // here.
+  class NoSiteIsolationContentBrowserClient : public ContentBrowserClient {
+   public:
+    bool ShouldEnableStrictSiteIsolation() override { return false; }
+  };
+
+ private:
+  NoSiteIsolationContentBrowserClient browser_client_;
+  raw_ptr<ContentBrowserClient> original_client_ = nullptr;
+
+  base::test::ScopedFeatureList feature_list_;
+};
+
 using VirtualBrowsingContextGroupTest = CrossOriginOpenerPolicyBrowserTest;
 using SoapByDefaultVirtualBrowsingContextGroupTest =
     CrossOriginOpenerPolicyBrowserTest;
@@ -3359,6 +3412,9 @@
 INSTANTIATE_TEST_SUITE_P(All,
                          SameOriginAllowPopupsPlusCoepBrowserTest,
                          kTestParams);
+INSTANTIATE_TEST_SUITE_P(All,
+                         NoSiteIsolationCrossOriginIsolationBrowserTest,
+                         kTestParams);
 
 IN_PROC_BROWSER_TEST_P(NoSharedArrayBufferByDefault, BaseCase) {
   GURL url = https_server()->GetURL("a.test", "/empty.html");
@@ -4243,4 +4299,55 @@
   EXPECT_TRUE(current_frame_host()->GetSiteInstance()->IsCrossOriginIsolated());
 }
 
+IN_PROC_BROWSER_TEST_P(NoSiteIsolationCrossOriginIsolationBrowserTest,
+                       COICanLiveInDefaultSI) {
+  GURL isolated_page(
+      https_server()->GetURL("a.test",
+                             "/set-header"
+                             "?cross-origin-opener-policy: same-origin"
+                             "&cross-origin-embedder-policy: require-corp"));
+  GURL non_isolated_page(https_server()->GetURL("a.test", "/title1.html"));
+
+  EXPECT_TRUE(NavigateToURL(shell(), isolated_page));
+  SiteInstanceImpl* main_frame_si = current_frame_host()->GetSiteInstance();
+  EXPECT_TRUE(main_frame_si->IsCrossOriginIsolated());
+  EXPECT_TRUE(main_frame_si->IsDefaultSiteInstance());
+
+  {
+    // Open a popup to a page with similar isolation. Pages that have compatible
+    // cross origin isolation should be put in the same default SiteInstance.
+    ShellAddedObserver shell_observer;
+    EXPECT_TRUE(ExecJs(current_frame_host(),
+                       JsReplace("window.open($1);", isolated_page)));
+    WebContentsImpl* popup = static_cast<WebContentsImpl*>(
+        shell_observer.GetShell()->web_contents());
+    EXPECT_TRUE(WaitForLoadStop(popup));
+
+    SiteInstanceImpl* popup_si = popup->GetMainFrame()->GetSiteInstance();
+    EXPECT_TRUE(popup_si->IsCrossOriginIsolated());
+    EXPECT_TRUE(popup_si->IsDefaultSiteInstance());
+    EXPECT_EQ(popup_si, main_frame_si);
+
+    popup->Close();
+  }
+
+  {
+    // Open a popup to a same origin non-isolated page. This page should live in
+    // a different BrowsingInstance in the default non-isolated SiteInstance.
+    ShellAddedObserver shell_observer;
+    EXPECT_TRUE(ExecJs(current_frame_host(),
+                       JsReplace("window.open($1);", non_isolated_page)));
+    WebContentsImpl* popup = static_cast<WebContentsImpl*>(
+        shell_observer.GetShell()->web_contents());
+    EXPECT_TRUE(WaitForLoadStop(popup));
+
+    SiteInstanceImpl* popup_si = popup->GetMainFrame()->GetSiteInstance();
+    EXPECT_FALSE(popup_si->IsCrossOriginIsolated());
+    EXPECT_TRUE(popup_si->IsDefaultSiteInstance());
+    EXPECT_NE(popup_si, main_frame_si);
+
+    popup->Close();
+  }
+}
+
 }  // namespace content
diff --git a/content/browser/net/sandboxed_http_cache_browsertest.cc b/content/browser/net/sandboxed_http_cache_browsertest.cc
index c36c457..e20711d 100644
--- a/content/browser/net/sandboxed_http_cache_browsertest.cc
+++ b/content/browser/net/sandboxed_http_cache_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/feature_list.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/page.h"
@@ -54,7 +55,14 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest, OpeningFileIsProhibited) {
+// crbug.com/1293674
+#if BUILDFLAG(IS_WIN)
+#define MAYBE_OpeningFileIsProhibited DISABLED_OpeningFileIsProhibited
+#else
+#define MAYBE_OpeningFileIsProhibited OpeningFileIsProhibited
+#endif
+IN_PROC_BROWSER_TEST_F(SandboxedHttpCacheBrowserTest,
+                       MAYBE_OpeningFileIsProhibited) {
   base::RunLoop run_loop;
   mojo::Remote<network::mojom::NetworkServiceTest> network_service_test;
   content::GetNetworkService()->BindTestInterface(
@@ -76,4 +84,4 @@
 // TODO(yhirano): Add more tests.
 
 }  // namespace
-}  // namespace content
\ No newline at end of file
+}  // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc
index 09113c3..fc9a800 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_browsertest.cc
@@ -533,8 +533,9 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Verifies that getting active input control accounts for iframe positioning.
+// Flaky: crbug.com/1293700
 IN_PROC_BROWSER_TEST_F(RenderWidgetHostViewAuraActiveWidgetTest,
-                       TextControlBoundingRegionInIframe) {
+                       DISABLED_TextControlBoundingRegionInIframe) {
   GURL page(
       embedded_test_server()->GetURL("example.com", "/input_in_iframe.html"));
   EXPECT_TRUE(NavigateToURL(shell(), page));
diff --git a/content/public/browser/ax_inspect_factory_mac.mm b/content/public/browser/ax_inspect_factory_mac.mm
index ff7c6b60..f362cbb 100644
--- a/content/public/browser/ax_inspect_factory_mac.mm
+++ b/content/public/browser/ax_inspect_factory_mac.mm
@@ -4,9 +4,9 @@
 
 #include "content/public/browser/ax_inspect_factory.h"
 
-#include "content/browser/accessibility/accessibility_event_recorder_mac.h"
 #include "content/browser/accessibility/accessibility_tree_formatter_blink.h"
 #include "content/browser/accessibility/browser_accessibility_manager.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder_mac.h"
 #include "ui/accessibility/platform/inspect/ax_tree_formatter_mac.h"
 
 namespace content {
@@ -58,7 +58,7 @@
 
   switch (type) {
     case ui::AXApiType::kMac:
-      return std::make_unique<AccessibilityEventRecorderMac>(pid, selector);
+      return std::make_unique<ui::AXEventRecorderMac>(pid, selector);
     default:
       NOTREACHED() << "Unsupported API type " << type;
   }
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 0197892..ffa7bc50 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -232,10 +232,8 @@
   }
 
   DCHECK_EQ(base::Value::Type::STRING, value->type());
-  std::string actual_response;
-  if (value->GetAsString(&actual_response))
-    DCHECK_EQ(expected_response, actual_response);
-
+  if (value->is_string())
+    DCHECK_EQ(expected_response, value->GetString());
   return true;
 }
 
@@ -1461,9 +1459,15 @@
   // Prerendering pages will never have user gesture.
   bool user_gesture = adapter.render_frame_host()->GetLifecycleState() !=
                       RenderFrameHost::LifecycleState::kPrerendering;
-  return ExecuteScriptHelper(adapter.render_frame_host(), script, user_gesture,
-                             ISOLATED_WORLD_ID_GLOBAL, &value) &&
-         value && value->GetAsString(result);
+  if (!ExecuteScriptHelper(adapter.render_frame_host(), script, user_gesture,
+                           ISOLATED_WORLD_ID_GLOBAL, &value)) {
+    return false;
+  }
+  if (value && value->is_string()) {
+    *result = value->GetString();
+    return true;
+  }
+  return false;
 }
 
 bool ExecuteScriptWithoutUserGestureAndExtractBool(
@@ -1487,9 +1491,16 @@
     std::string* result) {
   DCHECK(result);
   std::unique_ptr<base::Value> value;
-  return ExecuteScriptHelper(adapter.render_frame_host(), script, false,
-                             ISOLATED_WORLD_ID_GLOBAL, &value) &&
-         value && value->GetAsString(result);
+  if (!ExecuteScriptHelper(adapter.render_frame_host(), script, false,
+                           ISOLATED_WORLD_ID_GLOBAL, &value)) {
+    return false;
+  }
+
+  if (value && value->is_string()) {
+    *result = value->GetString();
+    return true;
+  }
+  return false;
 }
 
 // EvalJsResult methods.
diff --git a/gin/v8_platform_page_allocator.cc b/gin/v8_platform_page_allocator.cc
index 0baf393a..b1be62d 100644
--- a/gin/v8_platform_page_allocator.cc
+++ b/gin/v8_platform_page_allocator.cc
@@ -98,7 +98,7 @@
   // safe to just decommit the tail.
   base::DecommitSystemPages(
       release_base, release_size,
-      ::partition_alloc::PageAccessibilityDisposition::kUpdatePermissions);
+      ::partition_alloc::PageAccessibilityDisposition::kRequireUpdate);
 #else
 #error Unsupported platform
 #endif
@@ -110,13 +110,13 @@
                                    Permission permissions) {
   // If V8 sets permissions to none, we can discard the memory.
   if (permissions == v8::PageAllocator::Permission::kNoAccess) {
-    // Use PageAccessibilityDisposition::kKeepPermissionsIfPossible as an
+    // Use PageAccessibilityDisposition::kAllowKeepForPerf as an
     // optimization, to avoid perf regression (see crrev.com/c/2563038 for
     // details). This may cause the memory region to still be accessible on
     // certain platforms, but at least the physical pages will be discarded.
-    base::DecommitSystemPages(address, length,
-                              ::partition_alloc::PageAccessibilityDisposition::
-                                  kKeepPermissionsIfPossible);
+    base::DecommitSystemPages(
+        address, length,
+        ::partition_alloc::PageAccessibilityDisposition::kAllowKeepForPerf);
     return true;
   } else {
     return base::TrySetSystemPagesAccess(address, length,
diff --git a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-device/properties.textpb b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-device/properties.textpb
new file mode 100644
index 0000000..20f89474
--- /dev/null
+++ b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-device/properties.textpb
@@ -0,0 +1,17 @@
+{
+  "$build/goma": {
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.webrtc.fyi",
+  "recipe": "chromium",
+  "xcode_build_version": "13a233"
+}
\ No newline at end of file
diff --git a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-simulator/properties.textpb b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-simulator/properties.textpb
new file mode 100644
index 0000000..20f89474
--- /dev/null
+++ b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-simulator/properties.textpb
@@ -0,0 +1,17 @@
+{
+  "$build/goma": {
+    "rpc_extra_params": "?prod",
+    "server_host": "goma.chromium.org",
+    "use_luci_auth": true
+  },
+  "$recipe_engine/resultdb/test_presentation": {
+    "column_keys": [],
+    "grouping_keys": [
+      "status",
+      "v.test_suite"
+    ]
+  },
+  "builder_group": "chromium.webrtc.fyi",
+  "recipe": "chromium",
+  "xcode_build_version": "13a233"
+}
\ No newline at end of file
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index c222021..190565b 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -77445,32 +77445,39 @@
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.webrtc.fyi"
       exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org",'
-        '    "use_luci_auth": true'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
         '  },'
-        '  "$recipe_engine/resultdb/test_presentation": {'
-        '    "column_keys": [],'
-        '    "grouping_keys": ['
-        '      "status",'
-        '      "v.test_suite"'
-        '    ]'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-device/properties.textpb",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
         '  },'
         '  "builder_group": "chromium.webrtc.fyi",'
-        '  "recipe": "webrtc/chromium_ios",'
-        '  "xcode_build_version": "12d4e"'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
         '}'
       execution_timeout_secs: 7200
       caches {
-        name: "xcode_ios_12d4e"
-        path: "xcode_ios_12d4e.app"
+        name: "xcode_ios_13a233"
+        path: "xcode_ios_13a233.app"
       }
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
@@ -77489,32 +77496,39 @@
       dimensions: "os:Mac"
       dimensions: "pool:luci.chromium.webrtc.fyi"
       exe {
-        cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
-        cipd_version: "refs/heads/main"
-        cmd: "luciexe"
+        cipd_package: "infra/chromium/bootstrapper/${platform}"
+        cipd_version: "latest"
+        cmd: "bootstrapper"
       }
       properties:
         '{'
-        '  "$build/goma": {'
-        '    "rpc_extra_params": "?prod",'
-        '    "server_host": "goma.chromium.org",'
-        '    "use_luci_auth": true'
+        '  "$bootstrap/exe": {'
+        '    "exe": {'
+        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
+        '      "cipd_version": "refs/heads/main",'
+        '      "cmd": ['
+        '        "luciexe"'
+        '      ]'
+        '    }'
         '  },'
-        '  "$recipe_engine/resultdb/test_presentation": {'
-        '    "column_keys": [],'
-        '    "grouping_keys": ['
-        '      "status",'
-        '      "v.test_suite"'
-        '    ]'
+        '  "$bootstrap/properties": {'
+        '    "properties_file": "infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI ios-simulator/properties.textpb",'
+        '    "top_level_project": {'
+        '      "ref": "refs/heads/main",'
+        '      "repo": {'
+        '        "host": "chromium.googlesource.com",'
+        '        "project": "chromium/src"'
+        '      }'
+        '    }'
         '  },'
         '  "builder_group": "chromium.webrtc.fyi",'
-        '  "recipe": "webrtc/chromium_ios",'
-        '  "xcode_build_version": "12d4e"'
+        '  "led_builder_is_bootstrapped": true,'
+        '  "recipe": "chromium"'
         '}'
       execution_timeout_secs: 7200
       caches {
-        name: "xcode_ios_12d4e"
-        path: "xcode_ios_12d4e.app"
+        name: "xcode_ios_13a233"
+        path: "xcode_ios_13a233.app"
       }
       build_numbers: YES
       service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
diff --git a/infra/config/subprojects/webrtc/webrtc.fyi.star b/infra/config/subprojects/webrtc/webrtc.fyi.star
index fb20b88..b9f23b5 100644
--- a/infra/config/subprojects/webrtc/webrtc.fyi.star
+++ b/infra/config/subprojects/webrtc/webrtc.fyi.star
@@ -137,16 +137,14 @@
 
 builder(
     name = "WebRTC Chromium FYI ios-device",
-    executable = "recipe:webrtc/chromium_ios",
     goma_backend = goma.backend.RBE_PROD,
     os = os.MAC_ANY,
-    xcode = xcode.x12d4e,
+    xcode = xcode.x13main,
 )
 
 builder(
     name = "WebRTC Chromium FYI ios-simulator",
-    executable = "recipe:webrtc/chromium_ios",
     goma_backend = goma.backend.RBE_PROD,
     os = os.MAC_ANY,
-    xcode = xcode.x12d4e,
+    xcode = xcode.x13main,
 )
diff --git a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device.json b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device.json
deleted file mode 100644
index 0a26824..0000000
--- a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-device.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "comments": [
-    "Builder for 64-bit devices.",
-    "Build is performed with gn+ninja."
-  ],
-  "xcode build version": "13a233",
-  "gn_args": [
-    "enable_run_ios_unittests_with_xctest=true",
-    "goma_dir=\"$(goma_dir)\"",
-    "ios_build_chrome=false",
-    "ios_enable_code_signing=false",
-    "ios_set_attributes_for_xcode_project_generation=false",
-    "is_component_build=false",
-    "is_debug=false",
-    "target_cpu=\"arm64\"",
-    "target_os=\"ios\"",
-    "use_goma=true"
-  ],
-  "additional_compile_targets": [
-    "all"
-  ],
-  "tests": [
-  ]
-}
diff --git a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json b/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
deleted file mode 100644
index 133a120..0000000
--- a/ios/build/bots/chromium.webrtc.fyi/WebRTC Chromium FYI ios-simulator.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
-  "comments": [
-    "Runs tests on @3x, @2x, 64-bit phone, tablet, iOS 14."
-  ],
-  "xcode build version": "13a233",
-  "gn_args": [
-    "enable_run_ios_unittests_with_xctest=true",
-    "goma_dir=\"$(goma_dir)\"",
-    "ios_build_chrome=false",
-    "ios_enable_code_signing=false",
-    "ios_set_attributes_for_xcode_project_generation=false",
-    "is_component_build=false",
-    "is_debug=true",
-    "symbol_level=1",
-    "target_cpu=\"x64\"",
-    "target_os=\"ios\"",
-    "use_goma=true"
-  ],
-  "additional_compile_targets": [
-    "all"
-  ],
-  "configuration": "Debug",
-  "sdk": "iphonesimulator10.0",
-  "tests": [
-    {
-      "include": "webrtc_tests.json",
-      "device type": "iPhone 11",
-      "os": "14.4",
-      "host os": "Mac-11",
-      "pool": "chromium.tests"
-    },
-    {
-      "include": "webrtc_tests.json",
-      "device type": "iPhone X",
-      "os": "14.4",
-      "host os": "Mac-11",
-      "pool": "chromium.tests"
-    },
-    {
-      "include": "webrtc_tests.json",
-      "device type": "iPad Air (3rd generation)",
-      "os": "14.4",
-      "host os": "Mac-11",
-      "pool": "chromium.tests"
-    }
-  ]
-}
diff --git a/ios/build/bots/tests/webrtc_tests.json b/ios/build/bots/tests/webrtc_tests.json
deleted file mode 100644
index a33674eb..0000000
--- a/ios/build/bots/tests/webrtc_tests.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "tests": [
-    {
-      "app": "ios_remoting_unittests"
-    }
-  ]
-}
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index e9f6746..a794bf5 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -391,7 +391,6 @@
     "//ios/public/provider/chrome/browser/app_distribution:app_distribution_api",
     "//ios/public/provider/chrome/browser/mailto",
     "//ios/public/provider/chrome/browser/overrides:overrides_api",
-    "//ios/public/provider/chrome/browser/signin",
     "//ios/public/provider/chrome/browser/ui_utils:ui_utils_api",
     "//ios/public/provider/chrome/browser/user_feedback",
     "//ios/web/common:features",
diff --git a/ios/chrome/app/application_delegate/BUILD.gn b/ios/chrome/app/application_delegate/BUILD.gn
index d23b999..de46c7c7 100644
--- a/ios/chrome/app/application_delegate/BUILD.gn
+++ b/ios/chrome/app/application_delegate/BUILD.gn
@@ -248,6 +248,7 @@
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/app_distribution:app_distribution_api",
     "//ios/public/provider/chrome/browser/discover_feed",
+    "//ios/public/provider/chrome/browser/signin",
     "//ios/public/provider/chrome/browser/user_feedback",
     "//ios/web",
     "//net",
diff --git a/ios/chrome/app/application_delegate/app_state.mm b/ios/chrome/app/application_delegate/app_state.mm
index 614ca00..0a8d4a3 100644
--- a/ios/chrome/app/application_delegate/app_state.mm
+++ b/ios/chrome/app/application_delegate/app_state.mm
@@ -30,10 +30,9 @@
 #import "ios/chrome/app/main_application_delegate.h"
 #include "ios/chrome/browser/application_context.h"
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/browsing_data/sessions_storage_util.h"
+#include "ios/chrome/browser/browsing_data/sessions_storage_util.h"
 #include "ios/chrome/browser/chrome_constants.h"
 #include "ios/chrome/browser/crash_report/crash_helper.h"
-#import "ios/chrome/browser/crash_report/crash_keys_helper.h"
 #include "ios/chrome/browser/crash_report/crash_keys_helper.h"
 #include "ios/chrome/browser/crash_report/crash_loop_detection_util.h"
 #include "ios/chrome/browser/crash_report/features.h"
@@ -58,6 +57,7 @@
 #include "ios/public/provider/chrome/browser/app_distribution/app_distribution_api.h"
 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h"
+#include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider.h"
 #include "ios/web/public/thread/web_task_traits.h"
 #include "net/url_request/url_request_context.h"
@@ -415,12 +415,12 @@
 
 - (void)application:(UIApplication*)application
     didDiscardSceneSessions:(NSSet<UISceneSession*>*)sceneSessions {
-  NSMutableArray<NSString*>* sessionIDs =
-      [NSMutableArray arrayWithCapacity:sceneSessions.count];
-  // This method is invoked by iOS to inform the application that the sessions
-  // for "closed windows" is garbage collected and that any data associated with
-  // them by the application needs to be deleted.
-  //
+  DCHECK_GE(self.initStage, InitStageBrowserObjectsForBackgroundHandlers);
+
+  ios::GetChromeBrowserProvider()
+      .GetChromeIdentityService()
+      ->ApplicationDidDiscardSceneSessions(sceneSessions);
+
   // Usually Chrome uses -[SceneState sceneSessionID] as identifier to properly
   // support devices that do not support multi-window (and which use a constant
   // identifier). For devices that do not support multi-window the session is
@@ -430,6 +430,8 @@
   // session is garbage collected.
   //
   // Thus it is always correct to use -persistentIdentifier here.
+  NSMutableArray<NSString*>* sessionIDs =
+      [NSMutableArray arrayWithCapacity:sceneSessions.count];
   for (UISceneSession* session in sceneSessions) {
     [sessionIDs addObject:session.persistentIdentifier];
   }
@@ -634,7 +636,6 @@
   [[PreviousSessionInfo sharedInstance] beginRecordingCurrentSession];
 }
 
-
 #pragma mark - UIBlockerManager
 
 - (void)incrementBlockingUICounterForTarget:(id<UIBlockerTarget>)target {
diff --git a/ios/chrome/app/main_application_delegate.mm b/ios/chrome/app/main_application_delegate.mm
index 6a6b926..2c4caf0 100644
--- a/ios/chrome/app/main_application_delegate.mm
+++ b/ios/chrome/app/main_application_delegate.mm
@@ -24,8 +24,6 @@
 #import "ios/chrome/browser/ui/main/scene_controller.h"
 #import "ios/chrome/browser/ui/main/scene_delegate.h"
 #import "ios/chrome/browser/ui/main/scene_state.h"
-#include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
-#include "ios/public/provider/chrome/browser/signin/chrome_identity_service.h"
 #import "ios/web/common/uikit_ui_util.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -38,7 +36,7 @@
 const int kMainIntentCheckDelay = 1;
 }  // namespace
 
-@interface MainApplicationDelegate () {
+@interface MainApplicationDelegate () <AppStateObserver> {
   MainController* _mainController;
   // Memory helper used to log the number of memory warnings received.
   MemoryWarningHelper* _memoryHelper;
@@ -53,6 +51,9 @@
   id<TabOpening> _tabOpener;
   // Handles tab switcher.
   id<TabSwitching> _tabSwitcher;
+  // The set of "scene sessions" that needs to be discarded. See
+  // -applicatiopn:didDiscardSceneSessions: for details.
+  NSSet<UISceneSession*>* _sceneSessionsToDiscard;
 }
 
 // YES if application:didFinishLaunchingWithOptions: was called. Used to
@@ -158,9 +159,23 @@
 
 - (void)application:(UIApplication*)application
     didDiscardSceneSessions:(NSSet<UISceneSession*>*)sceneSessions {
-  ios::GetChromeBrowserProvider()
-      .GetChromeIdentityService()
-      ->ApplicationDidDiscardSceneSessions(sceneSessions);
+  // This method is invoked by iOS to inform the application that the sessions
+  // for "closed windows" is garbage collected and that any data associated with
+  // them by the application needs to be deleted.
+  //
+  // The documentation says that if the application is not running when the OS
+  // decides to discard the sessions, then it will call this method the next
+  // time the application starts up. As seen by crbug.com/1292641, this call
+  // happens before -[UIApplicationDelegate sceneWillConnect:] which means
+  // that it can happen before Chrome has properly initialized. In that case,
+  // record the list of sessions to discard and clean them once Chrome is
+  // initialized.
+  if (_appState.initStage <= InitStageBrowserObjectsForBackgroundHandlers) {
+    _sceneSessionsToDiscard = [sceneSessions copy];
+    [_appState addObserver:self];
+    return;
+  }
+
   [_appState application:application didDiscardSceneSessions:sceneSessions];
 }
 
@@ -261,6 +276,23 @@
                                memoryHelper:_memoryHelper];
 }
 
+#pragma mark - AppStateObserver methods
+
+- (void)appState:(AppState*)appState
+    didTransitionFromInitStage:(InitStage)previousInitStage {
+  DCHECK_EQ(_appState, appState);
+
+  // The app transitioned to InitStageBrowserObjectsForBackgroundHandlers
+  // or past that stage.
+  if (_appState.initStage >= InitStageBrowserObjectsForBackgroundHandlers) {
+    DCHECK(_sceneSessionsToDiscard);
+    [_appState removeObserver:self];
+    [_appState application:[UIApplication sharedApplication]
+        didDiscardSceneSessions:_sceneSessionsToDiscard];
+    _sceneSessionsToDiscard = nil;
+  }
+}
+
 #pragma mark - optionals from UIApplicationDelegate required by ChromeInternal
 
 // TODO(crbug.com/1227456): Remove when fixed in ChromeInternal
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index a11efd8..04020b4 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2140,6 +2140,21 @@
       <message name="IDS_IOS_SEARCH_COPIED_TEXT" desc="If a user has some text on their clipboard, this string will appear as an option when the user long-presses on Chrome's address bar. The user can select this option to search the default search engine for the text. This string is a complete sentence. If necessary for your language, you can translate as 'Search for Text That You Copied.' [iOS only]" meaning="Maximum characters: 32">
           Search for Copied Text
       </message>
+      <message name="IDS_IOS_DEFAULT_PAGE_MODE_DESKTOP" desc="The label used to let the user choose that their pages will be loaded using the Desktop mode (vs Mobile)" meaning="The pages will be loaded in Desktop mode">
+        Desktop
+      </message>
+      <message name="IDS_IOS_DEFAULT_PAGE_MODE_LABEL" desc="The label of the cell used to display the current mode (Desktop or Mobile) in which the pages will be loaded by default">
+        Default Site Mode
+      </message>
+      <message name="IDS_IOS_DEFAULT_PAGE_MODE_MOBILE" desc="The label used to let the user choose that their pages will be loaded using the Mobile mode (vs Desktop)" meaning="The pages will be loaded in Mobile mode">
+        Mobile
+      </message>
+      <message name="IDS_IOS_DEFAULT_PAGE_MODE_SUBTITLE" desc="The subtitle explaining what choosing a different mode (Desktop or Mobile) will do">
+        Select how you would like Chrome to request the site everytime
+      </message>
+      <message name="IDS_IOS_DEFAULT_PAGE_MODE_TITLE" desc="The title of the screen in which the user can choose the default mode (Desktop or Mobile) in which the pages will be loaded">
+        Default Site Mode
+      </message>
       <message name="IDS_IOS_SEARCH_ENGINE_SETTING_CUSTOM_SECTION_HEADER" desc="The header for custom search engines section in Search Engine selection setting [iOS only]">
         Recently Visited
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_DESKTOP.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_DESKTOP.png.sha1
new file mode 100644
index 0000000..101932a
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_DESKTOP.png.sha1
@@ -0,0 +1 @@
+4327d55c8f5a5354d996478e31ecc886b3f6cc20
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_LABEL.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_LABEL.png.sha1
new file mode 100644
index 0000000..2d2c7d7
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_LABEL.png.sha1
@@ -0,0 +1 @@
+6940ac206afee626edd53594550e1421f07be7bb
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_MOBILE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_MOBILE.png.sha1
new file mode 100644
index 0000000..101932a
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_MOBILE.png.sha1
@@ -0,0 +1 @@
+4327d55c8f5a5354d996478e31ecc886b3f6cc20
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_SUBTITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_SUBTITLE.png.sha1
new file mode 100644
index 0000000..101932a
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_SUBTITLE.png.sha1
@@ -0,0 +1 @@
+4327d55c8f5a5354d996478e31ecc886b3f6cc20
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_TITLE.png.sha1
new file mode 100644
index 0000000..101932a
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_PAGE_MODE_TITLE.png.sha1
@@ -0,0 +1 @@
+4327d55c8f5a5354d996478e31ecc886b3f6cc20
\ No newline at end of file
diff --git a/ios/chrome/browser/signin/authentication_service_unittest.mm b/ios/chrome/browser/signin/authentication_service_unittest.mm
index 759132b..1fdc384 100644
--- a/ios/chrome/browser/signin/authentication_service_unittest.mm
+++ b/ios/chrome/browser/signin/authentication_service_unittest.mm
@@ -172,7 +172,7 @@
 
   // Sets a restricted pattern.
   void SetPattern(const std::string pattern) {
-    base::ListValue allowed_patterns;
+    base::Value allowed_patterns(base::Value::Type::LIST);
     allowed_patterns.Append(pattern);
     GetApplicationContext()->GetLocalState()->Set(
         prefs::kRestrictAccountsToPatterns, allowed_patterns);
diff --git a/ios/chrome/browser/signin/chrome_account_manager_service_unittest.mm b/ios/chrome/browser/signin/chrome_account_manager_service_unittest.mm
index 2ba517c6..e31b4f04 100644
--- a/ios/chrome/browser/signin/chrome_account_manager_service_unittest.mm
+++ b/ios/chrome/browser/signin/chrome_account_manager_service_unittest.mm
@@ -60,7 +60,7 @@
 
   // Sets a restricted pattern.
   void SetPattern(const std::string pattern) {
-    base::ListValue allowed_patterns;
+    base::Value allowed_patterns(base::Value::Type::LIST);
     allowed_patterns.Append(pattern);
     GetApplicationContext()->GetLocalState()->Set(
         prefs::kRestrictAccountsToPatterns, allowed_patterns);
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index e4b13bb..f0e0413 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-83dbbd2f52069cc70b6e157420a6bc246f55b3ac
\ No newline at end of file
+df4521b342d7ae5aedea572d8d1c26744d8f20e8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index cc3bd7b..26635d8c 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-39267668c68f3bbc909380083bacb6ea722850c8
\ No newline at end of file
+04d7b437e43b3efaa3f2d085160cfcf9bfeb768b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 387160d..26d9ba4 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-c82e081727f496494767c82dfe1ed58651c073cf
\ No newline at end of file
+e0fce4d71c87047bcba1b13456bd18e9df5bff2e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 8fcefeb..4b09be2 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-ae4b4f75a0328c2ba662f6f1adf467e497703036
\ No newline at end of file
+cc11af328921d39eb4673317ab9f8d60d46d609f
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
index bb361fcc..fb8db30 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-a898dff0cb9edee7b210c7b2057f10753839d9b0
\ No newline at end of file
+e4c9388edda9120d7246f6e7d1670210393a10f5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
index 1aa3c67..c3839246 100644
--- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-2f265f1b75394f0d725e7bfe5b10d2cc63352a93
\ No newline at end of file
+f34858c6bed6908b7210b0e3969041a0769c7c7b
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index be288a5..a43fd78 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-1f392a7b5cfe17e4fe815f0a76b6cf3188d3294a
\ No newline at end of file
+1fa88e5d9ccae9e885475792532e4ad62d98dfde
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 982665f7..55558485 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-081b61bb5d0c3a66f6b223bba35d514256682acb
\ No newline at end of file
+5063156623b224b03e079cd7b76035bac9305b01
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 5904bd7..31accb9 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4b59603a0fec87f758bded79c7d808ecf15c51c5
\ No newline at end of file
+d4dbc687c4c8e6f74421e3ba003ebafbbc673486
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 41fbea4..57db2b5 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-f1f9b6559310777b54dac13df64358457c53eeec
\ No newline at end of file
+f83de7381e27f42380bfa34b61d5c3f244829ba2
\ No newline at end of file
diff --git a/ios/web/js_messaging/web_frame_impl.h b/ios/web/js_messaging/web_frame_impl.h
index 54cb105..c7fd85f3 100644
--- a/ios/web/js_messaging/web_frame_impl.h
+++ b/ios/web/js_messaging/web_frame_impl.h
@@ -108,7 +108,7 @@
   // Encrypts |payload| and returns a JSON string of a dictionary containing
   // the encrypted metadata and its initialization vector. If encryption fails,
   // an empty string will be returned.
-  const std::string EncryptPayload(base::DictionaryValue payload,
+  const std::string EncryptPayload(base::Value payload,
                                    const std::string& additiona_data);
 
   // A structure to store the callbacks associated with the
diff --git a/ios/web/js_messaging/web_frame_impl.mm b/ios/web/js_messaging/web_frame_impl.mm
index a96605db..cffe11c 100644
--- a/ios/web/js_messaging/web_frame_impl.mm
+++ b/ios/web/js_messaging/web_frame_impl.mm
@@ -122,7 +122,7 @@
 }
 
 const std::string WebFrameImpl::EncryptPayload(
-    base::DictionaryValue payload,
+    base::Value payload,
     const std::string& additiona_data) {
   crypto::Aead aead(crypto::Aead::AES_256_GCM);
   aead.Init(&frame_key_->key());
@@ -144,7 +144,7 @@
   base::Base64Encode(payload_ciphertext, &encoded_payload);
 
   std::string payload_string;
-  base::DictionaryValue payload_dict;
+  base::Value payload_dict(base::Value::Type::DICTIONARY);
   payload_dict.SetKey("payload", base::Value(encoded_payload));
   payload_dict.SetKey("iv", base::Value(encoded_payload_iv));
   base::JSONWriter::Write(payload_dict, &payload_string);
@@ -177,13 +177,13 @@
                                      reply_with_result);
   }
 
-  base::DictionaryValue message_payload;
+  base::Value message_payload(base::Value::Type::DICTIONARY);
   message_payload.SetKey("messageId", base::Value(message_id));
   message_payload.SetKey("replyWithResult", base::Value(reply_with_result));
   const std::string& encrypted_message_json =
       EncryptPayload(std::move(message_payload), std::string());
 
-  base::DictionaryValue function_payload;
+  base::Value function_payload(base::Value::Type::DICTIONARY);
   function_payload.SetKey("functionName", base::Value(name));
   base::ListValue parameters_value(parameters);
   function_payload.SetKey("parameters", std::move(parameters_value));
diff --git a/media/base/audio_processing.h b/media/base/audio_processing.h
index 7f2120f..ca80199 100644
--- a/media/base/audio_processing.h
+++ b/media/base/audio_processing.h
@@ -81,6 +81,10 @@
     return NeedWebrtcAudioProcessing() || stereo_mirroring;
   }
 
+  bool NeedPlayoutReference() const {
+    return echo_cancellation || automatic_gain_control;
+  }
+
   // Stringifies the settings for human-readable logging.
   std::string ToString() const;
 };
diff --git a/media/webrtc/audio_processor.cc b/media/webrtc/audio_processor.cc
index eb43361..8487a7a 100644
--- a/media/webrtc/audio_processor.cc
+++ b/media/webrtc/audio_processor.cc
@@ -294,15 +294,6 @@
   return output_format_;
 }
 
-bool AudioProcessor::RequiresPlayoutReference() const {
-  const bool effects_require_playout_reference =
-      settings_.echo_cancellation || settings_.automatic_gain_control;
-  if (effects_require_playout_reference) {
-    DCHECK(!!webrtc_audio_processing_);
-  }
-  return effects_require_playout_reference;
-}
-
 void AudioProcessor::SetOutputWillBeMuted(bool muted) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
   SendLogMessage(
diff --git a/media/webrtc/audio_processor.h b/media/webrtc/audio_processor.h
index 523775e..02cb07a 100644
--- a/media/webrtc/audio_processor.h
+++ b/media/webrtc/audio_processor.h
@@ -111,10 +111,6 @@
     return !!webrtc_audio_processing_;
   }
 
-  // Returns true if the audio processing effects require seeing the playout
-  // audio in order to function properly.
-  bool RequiresPlayoutReference() const;
-
   // Instructs the Audio Processing Module (APM) to reduce its complexity when
   // |muted| is true. This mode is triggered when all audio tracks are disabled.
   // The default APM complexity mode is restored by |muted| set to false.
diff --git a/media/webrtc/audio_processor_test.cc b/media/webrtc/audio_processor_test.cc
index 1e92d86..e4dea7b 100644
--- a/media/webrtc/audio_processor_test.cc
+++ b/media/webrtc/audio_processor_test.cc
@@ -651,48 +651,4 @@
   audio_processor.ProcessCapturedAudio(*data_bus, base::TimeTicks::Now(), -1,
                                        1.0, false);
 }
-
-TEST_F(AudioProcessorTest,
-       RequiresPlayoutReferenceWhenEchoCancellationIsEnabled) {
-  MockProcessedCaptureCallback mock_capture_callback;
-  AudioProcessingSettings settings;
-  DisableDefaultSettings(settings);
-  settings.echo_cancellation = true;
-  AudioProcessor audio_processor(
-      mock_capture_callback.Get(), LogCallbackForTesting(), settings, params_,
-      AudioProcessor::GetDefaultOutputFormat(params_, settings));
-  EXPECT_TRUE(audio_processor.RequiresPlayoutReference());
-}
-
-TEST_F(AudioProcessorTest, RequiresPlayoutReferenceWhenGainControlIsEnabled) {
-  MockProcessedCaptureCallback mock_capture_callback;
-  AudioProcessingSettings settings;
-  DisableDefaultSettings(settings);
-  settings.automatic_gain_control = true;
-  AudioProcessor audio_processor(
-      mock_capture_callback.Get(), LogCallbackForTesting(), settings, params_,
-      AudioProcessor::GetDefaultOutputFormat(params_, settings));
-  EXPECT_TRUE(audio_processor.RequiresPlayoutReference());
-}
-
-TEST_F(AudioProcessorTest,
-       DoesNotRequirePlayoutReferenceWhenAecAndAgcIsDisabled) {
-  MockProcessedCaptureCallback mock_capture_callback;
-  AudioProcessingSettings settings;
-  // Disable effects that need the playout signal.
-  settings.echo_cancellation = false;
-  settings.automatic_gain_control = false;
-  // Enable all other effects.
-  settings.experimental_automatic_gain_control = true;
-  settings.noise_suppression = true;
-  settings.transient_noise_suppression = true;
-  settings.high_pass_filter = true;
-  settings.stereo_mirroring = true;
-  settings.force_apm_creation = true;
-  AudioProcessor audio_processor(
-      mock_capture_callback.Get(), LogCallbackForTesting(), settings, params_,
-      AudioProcessor::GetDefaultOutputFormat(params_, settings));
-  EXPECT_FALSE(audio_processor.RequiresPlayoutReference());
-}
-
 }  // namespace media
diff --git a/net/socket/client_socket_pool_manager_impl.cc b/net/socket/client_socket_pool_manager_impl.cc
index ed80c33..a30284e 100644
--- a/net/socket/client_socket_pool_manager_impl.cc
+++ b/net/socket/client_socket_pool_manager_impl.cc
@@ -25,10 +25,12 @@
 ClientSocketPoolManagerImpl::ClientSocketPoolManagerImpl(
     const CommonConnectJobParams& common_connect_job_params,
     const CommonConnectJobParams& websocket_common_connect_job_params,
-    HttpNetworkSession::SocketPoolType pool_type)
+    HttpNetworkSession::SocketPoolType pool_type,
+    bool cleanup_on_ip_address_change)
     : common_connect_job_params_(common_connect_job_params),
       websocket_common_connect_job_params_(websocket_common_connect_job_params),
-      pool_type_(pool_type) {
+      pool_type_(pool_type),
+      cleanup_on_ip_address_change_(cleanup_on_ip_address_change) {
   // |websocket_endpoint_lock_manager| must only be set for websocket
   // connections.
   DCHECK(!common_connect_job_params_.websocket_endpoint_lock_manager);
@@ -84,7 +86,7 @@
         sockets_per_proxy_server, sockets_per_group,
         unused_idle_socket_timeout(pool_type_), proxy_server,
         pool_type_ == HttpNetworkSession::WEBSOCKET_SOCKET_POOL,
-        &common_connect_job_params_);
+        &common_connect_job_params_, cleanup_on_ip_address_change_);
   }
 
   std::pair<SocketPoolMap::iterator, bool> ret =
diff --git a/net/socket/client_socket_pool_manager_impl.h b/net/socket/client_socket_pool_manager_impl.h
index 51fe279..c85f6b5 100644
--- a/net/socket/client_socket_pool_manager_impl.h
+++ b/net/socket/client_socket_pool_manager_impl.h
@@ -32,7 +32,8 @@
   ClientSocketPoolManagerImpl(
       const CommonConnectJobParams& common_connect_job_params,
       const CommonConnectJobParams& websocket_common_connect_job_params,
-      HttpNetworkSession::SocketPoolType pool_type);
+      HttpNetworkSession::SocketPoolType pool_type,
+      bool cleanup_on_ip_address_change = true);
 
   ClientSocketPoolManagerImpl(const ClientSocketPoolManagerImpl&) = delete;
   ClientSocketPoolManagerImpl& operator=(const ClientSocketPoolManagerImpl&) =
@@ -59,6 +60,8 @@
 
   const HttpNetworkSession::SocketPoolType pool_type_;
 
+  const bool cleanup_on_ip_address_change_;
+
   SocketPoolMap socket_pools_;
 
   THREAD_CHECKER(thread_checker_);
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index b47590a..044b831 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -118,7 +118,8 @@
     base::TimeDelta unused_idle_socket_timeout,
     const ProxyServer& proxy_server,
     bool is_for_websockets,
-    const CommonConnectJobParams* common_connect_job_params)
+    const CommonConnectJobParams* common_connect_job_params,
+    bool cleanup_on_ip_address_change)
     : TransportClientSocketPool(max_sockets,
                                 max_sockets_per_group,
                                 unused_idle_socket_timeout,
@@ -126,6 +127,7 @@
                                 proxy_server,
                                 is_for_websockets,
                                 common_connect_job_params,
+                                cleanup_on_ip_address_change,
                                 std::make_unique<ConnectJobFactory>(),
                                 common_connect_job_params->ssl_client_context,
                                 true /* connect_backup_jobs_enabled */) {}
@@ -144,7 +146,8 @@
   if (ssl_client_context_)
     ssl_client_context_->RemoveObserver(this);
 
-  NetworkChangeNotifier::RemoveIPAddressObserver(this);
+  if (cleanup_on_ip_address_change_)
+    NetworkChangeNotifier::RemoveIPAddressObserver(this);
 }
 
 std::unique_ptr<TransportClientSocketPool>
@@ -163,8 +166,9 @@
       new TransportClientSocketPool(
           max_sockets, max_sockets_per_group, unused_idle_socket_timeout,
           used_idle_socket_timeout, proxy_server, is_for_websockets,
-          common_connect_job_params, std::move(connect_job_factory),
-          ssl_client_context, connect_backup_jobs_enabled));
+          common_connect_job_params, true /* cleanup_on_ip_address_change */,
+          std::move(connect_job_factory), ssl_client_context,
+          connect_backup_jobs_enabled));
 }
 
 TransportClientSocketPool::CallbackResultPair::CallbackResultPair()
@@ -747,6 +751,7 @@
     const ProxyServer& proxy_server,
     bool is_for_websockets,
     const CommonConnectJobParams* common_connect_job_params,
+    bool cleanup_on_ip_address_change,
     std::unique_ptr<ConnectJobFactory> connect_job_factory,
     SSLClientContext* ssl_client_context,
     bool connect_backup_jobs_enabled)
@@ -761,13 +766,15 @@
       unused_idle_socket_timeout_(unused_idle_socket_timeout),
       used_idle_socket_timeout_(used_idle_socket_timeout),
       proxy_server_(proxy_server),
+      cleanup_on_ip_address_change_(cleanup_on_ip_address_change),
       connect_backup_jobs_enabled_(connect_backup_jobs_enabled &&
                                    g_connect_backup_jobs_enabled),
       ssl_client_context_(ssl_client_context) {
   DCHECK_LE(0, max_sockets_per_group);
   DCHECK_LE(max_sockets_per_group, max_sockets);
 
-  NetworkChangeNotifier::AddIPAddressObserver(this);
+  if (cleanup_on_ip_address_change_)
+    NetworkChangeNotifier::AddIPAddressObserver(this);
 
   if (ssl_client_context_)
     ssl_client_context_->AddObserver(this);
@@ -1063,6 +1070,7 @@
 }
 
 void TransportClientSocketPool::OnIPAddressChanged() {
+  DCHECK(cleanup_on_ip_address_change_);
   FlushWithError(ERR_NETWORK_CHANGED, kNetworkChanged);
 }
 
diff --git a/net/socket/transport_client_socket_pool.h b/net/socket/transport_client_socket_pool.h
index 355c7bac..03e4485 100644
--- a/net/socket/transport_client_socket_pool.h
+++ b/net/socket/transport_client_socket_pool.h
@@ -152,7 +152,8 @@
       base::TimeDelta unused_idle_socket_timeout,
       const ProxyServer& proxy_server,
       bool is_for_websockets,
-      const CommonConnectJobParams* common_connect_job_params);
+      const CommonConnectJobParams* common_connect_job_params,
+      bool cleanup_on_ip_address_change = true);
 
   TransportClientSocketPool(const TransportClientSocketPool&) = delete;
   TransportClientSocketPool& operator=(const TransportClientSocketPool&) =
@@ -596,6 +597,7 @@
       const ProxyServer& proxy_server,
       bool is_for_websockets,
       const CommonConnectJobParams* common_connect_job_params,
+      bool cleanup_on_ip_address_change,
       std::unique_ptr<ConnectJobFactory> connect_job_factory,
       SSLClientContext* ssl_client_context,
       bool connect_backup_jobs_enabled);
@@ -787,6 +789,8 @@
 
   const ProxyServer proxy_server_;
 
+  const bool cleanup_on_ip_address_change_;
+
   // TODO(vandebo) Remove when backup jobs move to TransportClientSocketPool
   bool connect_backup_jobs_enabled_;
 
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc
index 57de948..4dcecde 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -1035,6 +1035,63 @@
   EXPECT_EQ(0, pool_->IdleSocketCount());
 }
 
+TEST(TransportClientSocketPoolStandaloneTest, DontCleanupOnIPAddressChange) {
+  // This test manually sets things up in the same way
+  // TransportClientSocketPoolTest does, but it creates a
+  // TransportClientSocketPool with cleanup_on_ip_address_changed = false. Since
+  // this is the only test doing this, it's not worth extending
+  // TransportClientSocketPoolTest to support this scenario.
+  base::test::SingleThreadTaskEnvironment task_environment;
+  std::unique_ptr<MockCertVerifier> cert_verifier =
+      std::make_unique<MockCertVerifier>();
+  SpdySessionDependencies session_deps;
+  session_deps.cert_verifier = std::move(cert_verifier);
+  std::unique_ptr<HttpNetworkSession> http_network_session =
+      SpdySessionDependencies::SpdyCreateSession(&session_deps);
+  auto common_connect_job_params = std::make_unique<CommonConnectJobParams>(
+      http_network_session->CreateCommonConnectJobParams());
+  MockTransportClientSocketFactory client_socket_factory(NetLog::Get());
+  common_connect_job_params->client_socket_factory = &client_socket_factory;
+
+  scoped_refptr<ClientSocketPool::SocketParams> params(
+      ClientSocketPool::SocketParams::CreateForHttpForTesting());
+  auto pool = std::make_unique<TransportClientSocketPool>(
+      kMaxSockets, kMaxSocketsPerGroup, kUnusedIdleSocketTimeout,
+      ProxyServer::Direct(), false /* is_for_websockets */,
+      common_connect_job_params.get(),
+      false /* cleanup_on_ip_address_change */);
+  const ClientSocketPool::GroupId group_id(
+      url::SchemeHostPort(url::kHttpScheme, "www.google.com", 80),
+      PrivacyMode::PRIVACY_MODE_DISABLED, NetworkIsolationKey(),
+      SecureDnsPolicy::kAllow);
+  TestCompletionCallback callback;
+  ClientSocketHandle handle;
+  int rv =
+      handle.Init(group_id, params, absl::nullopt /* proxy_annotation_tag */,
+                  LOW, SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
+                  callback.callback(), ClientSocketPool::ProxyAuthCallback(),
+                  pool.get(), NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+  EXPECT_FALSE(handle.is_initialized());
+  EXPECT_FALSE(handle.socket());
+
+  EXPECT_THAT(callback.WaitForResult(), IsOk());
+  EXPECT_TRUE(handle.is_initialized());
+  EXPECT_TRUE(handle.socket());
+
+  handle.Reset();
+  // Need to run all pending to release the socket back to the pool.
+  base::RunLoop().RunUntilIdle();
+  // Now we should have 1 idle socket.
+  EXPECT_EQ(1, pool->IdleSocketCount());
+
+  // Since we set cleanup_on_ip_address_change = false, we should still have 1
+  // idle socket after an IP address change.
+  NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
+  base::RunLoop().RunUntilIdle();  // Notification happens async.
+  EXPECT_EQ(1, pool->IdleSocketCount());
+}
+
 TEST_F(TransportClientSocketPoolTest, SSLCertError) {
   StaticSocketDataProvider data;
   tagging_client_socket_factory_.AddSocketDataProvider(&data);
diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc
index 2c37cc2d..8ee6d798 100644
--- a/net/spdy/spdy_session_pool.cc
+++ b/net/spdy/spdy_session_pool.cc
@@ -92,7 +92,8 @@
     bool enable_priority_update,
     bool go_away_on_ip_change,
     SpdySessionPool::TimeFunc time_func,
-    NetworkQualityEstimator* network_quality_estimator)
+    NetworkQualityEstimator* network_quality_estimator,
+    bool cleanup_sessions_on_ip_address_changed)
     : http_server_properties_(http_server_properties),
       transport_security_state_(transport_security_state),
       ssl_client_context_(ssl_client_context),
@@ -113,8 +114,11 @@
       go_away_on_ip_change_(go_away_on_ip_change),
       time_func_(time_func),
       push_delegate_(nullptr),
-      network_quality_estimator_(network_quality_estimator) {
-  NetworkChangeNotifier::AddIPAddressObserver(this);
+      network_quality_estimator_(network_quality_estimator),
+      cleanup_sessions_on_ip_address_changed_(
+          cleanup_sessions_on_ip_address_changed) {
+  if (cleanup_sessions_on_ip_address_changed_)
+    NetworkChangeNotifier::AddIPAddressObserver(this);
   if (ssl_client_context_)
     ssl_client_context_->AddObserver(this);
 }
@@ -141,7 +145,8 @@
 
   if (ssl_client_context_)
     ssl_client_context_->RemoveObserver(this);
-  NetworkChangeNotifier::RemoveIPAddressObserver(this);
+  if (cleanup_sessions_on_ip_address_changed_)
+    NetworkChangeNotifier::RemoveIPAddressObserver(this);
 }
 
 int SpdySessionPool::CreateAvailableSessionFromSocketHandle(
@@ -482,6 +487,7 @@
 }
 
 void SpdySessionPool::OnIPAddressChanged() {
+  DCHECK(cleanup_sessions_on_ip_address_changed_);
   WeakSessionList current_sessions = GetCurrentSessions();
   for (WeakSessionList::const_iterator it = current_sessions.begin();
        it != current_sessions.end(); ++it) {
diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h
index 023ae2f..284a9c7f 100644
--- a/net/spdy/spdy_session_pool.h
+++ b/net/spdy/spdy_session_pool.h
@@ -145,7 +145,8 @@
                   bool enable_priority_update,
                   bool go_away_on_ip_change,
                   SpdySessionPool::TimeFunc time_func,
-                  NetworkQualityEstimator* network_quality_estimator);
+                  NetworkQualityEstimator* network_quality_estimator,
+                  bool cleanup_sessions_on_ip_address_changed = true);
 
   SpdySessionPool(const SpdySessionPool&) = delete;
   SpdySessionPool& operator=(const SpdySessionPool&) = delete;
@@ -493,6 +494,8 @@
 
   raw_ptr<NetworkQualityEstimator> network_quality_estimator_;
 
+  const bool cleanup_sessions_on_ip_address_changed_;
+
   base::WeakPtrFactory<SpdySessionPool> weak_ptr_factory_{this};
 };
 
diff --git a/services/audio/BUILD.gn b/services/audio/BUILD.gn
index 991002b..6c95974 100644
--- a/services/audio/BUILD.gn
+++ b/services/audio/BUILD.gn
@@ -111,6 +111,8 @@
 
   if (chrome_wide_echo_cancellation_supported) {
     sources += [
+      "audio_processor_handler.cc",
+      "audio_processor_handler.h",
       "mixing_graph.cc",
       "mixing_graph.h",
       "mixing_graph_impl.cc",
diff --git a/services/audio/audio_processor_handler.cc b/services/audio/audio_processor_handler.cc
new file mode 100644
index 0000000..a00f986c
--- /dev/null
+++ b/services/audio/audio_processor_handler.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2022 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 "services/audio/audio_processor_handler.h"
+
+#include "base/trace_event/trace_event.h"
+#include "media/base/audio_bus.h"
+
+namespace audio {
+
+AudioProcessorHandler::AudioProcessorHandler(
+    const media::AudioProcessingSettings& settings) {
+  DCHECK(settings.NeedAudioModification());
+}
+
+AudioProcessorHandler::~AudioProcessorHandler() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
+}
+
+void AudioProcessorHandler::OnPlayoutData(const media::AudioBus& audio_bus,
+                                          int sample_rate,
+                                          base::TimeDelta delay) {
+  TRACE_EVENT2("audio", "AudioProcessorHandler::OnPlayoutData", " this ",
+               static_cast<void*>(this), "delay", delay.InMillisecondsF());
+  // TODO(https://crbug.com/1215061): Forward playout data to audio processor.
+}
+}  // namespace audio
diff --git a/services/audio/audio_processor_handler.h b/services/audio/audio_processor_handler.h
new file mode 100644
index 0000000..bc0b2a78
--- /dev/null
+++ b/services/audio/audio_processor_handler.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2022 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 SERVICES_AUDIO_AUDIO_PROCESSOR_HANDLER_H_
+#define SERVICES_AUDIO_AUDIO_PROCESSOR_HANDLER_H_
+
+#include "base/sequence_checker.h"
+#include "media/base/audio_processing.h"
+#include "services/audio/reference_output.h"
+
+namespace media {
+class AudioBus;
+}  // namespace media
+
+namespace audio {
+
+// Encapsulates audio processing effects in the audio process.
+// TODO(https://crbug.com/1215061): Create and manage a media::AudioProcessor.
+// This class is currently a no-op implementation of ReferenceOutput::Listener.
+class AudioProcessorHandler final : public ReferenceOutput::Listener {
+ public:
+  explicit AudioProcessorHandler(
+      const media::AudioProcessingSettings& settings);
+  AudioProcessorHandler(const AudioProcessorHandler&) = delete;
+  AudioProcessorHandler& operator=(const AudioProcessorHandler&) = delete;
+  ~AudioProcessorHandler() final;
+
+ private:
+  // ReferenceOutput::Listener implementation.
+  void OnPlayoutData(const media::AudioBus& audio_bus,
+                     int sample_rate,
+                     base::TimeDelta delay) final;
+
+  SEQUENCE_CHECKER(owning_sequence_);
+};
+
+}  // namespace audio
+
+#endif  // SERVICES_AUDIO_AUDIO_PROCESSOR_HANDLER_H_
diff --git a/services/audio/input_controller.cc b/services/audio/input_controller.cc
index 667cd182..24ee8a4 100644
--- a/services/audio/input_controller.cc
+++ b/services/audio/input_controller.cc
@@ -27,10 +27,13 @@
 #include "media/audio/audio_io.h"
 #include "media/audio/audio_manager.h"
 #include "media/base/audio_bus.h"
+#include "media/base/audio_processing.h"
 #include "media/base/user_input_monitor.h"
+#include "services/audio/audio_processor_handler.h"
 #include "services/audio/concurrent_stream_metric_reporter.h"
 #include "services/audio/device_output_listener.h"
 #include "services/audio/output_tapper.h"
+#include "services/audio/reference_output.h"
 
 namespace audio {
 namespace {
@@ -113,6 +116,28 @@
 
 }  // namespace
 
+#if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION)
+// No-op implementation of ReferenceOutput::Listener to allow the
+// InputController to subscribe to playout audio without instantiating a more
+// heavy-duty Listener subclass.
+// TODO(https://crbug.com/1224845): Remove after the output mixing experiment.
+class InputController::NoopReferenceOutputListener
+    : public ReferenceOutput::Listener {
+ public:
+  NoopReferenceOutputListener() {}
+
+ private:
+  // ReferenceOutput::Listener implementation.
+  void OnPlayoutData(const media::AudioBus& audio_bus,
+                     int sample_rate,
+                     base::TimeDelta delay) final {
+    TRACE_EVENT2("audio", "NoopReferenceOutputListener::OnPlayoutData",
+                 " this ", static_cast<void*>(this), "delay",
+                 delay.InMillisecondsF());
+  }
+};
+#endif  // CHROME_WIDE_ECHO_CANCELLATION
+
 // Private subclass of AIC that covers the state while capturing audio.
 // This class implements the callback interface from the lower level audio
 // layer and gets called back on the audio hw thread.
@@ -224,14 +249,10 @@
   DCHECK(activity_monitor_);
 
 #if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION)
-  if (device_output_listener) {
-    // Unretained() is safe, because |event_handler_| outlives
-    // |output_tapper_|.
-    output_tapper_ = std::make_unique<OutputTapper>(
-        device_output_listener,
-        base::BindRepeating(&EventHandler::OnLog,
-                            base::Unretained(event_handler_)));
-  }
+  // TODO(https://crbug.com/1215061): Receive real processing settings via the
+  // constructor.
+  absl::optional<media::AudioProcessingSettings> processing_settings;
+  MaybeSetUpAudioProcessing(processing_settings, device_output_listener);
 #endif
 
   if (!user_input_monitor_) {
@@ -240,6 +261,39 @@
   }
 }
 
+#if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION)
+void InputController::MaybeSetUpAudioProcessing(
+    const absl::optional<media::AudioProcessingSettings>& settings,
+    DeviceOutputListener* device_output_listener) {
+  if (!device_output_listener)
+    return;
+
+  // TODO(https://crbug.com/1224845): Clean up this initialization logic once
+  // the output mixing experiment is over.
+  // |settings| will not be populated while the mixing experiment is ongoing, so
+  // there is no interference here. The mixing experiment always gets a
+  // NoopReferenceOutputListener.
+  ReferenceOutput::Listener* output_listener = nullptr;
+  if (settings && settings->NeedAudioModification()) {
+    audio_processor_handler_ =
+        std::make_unique<AudioProcessorHandler>(*settings);
+    if (settings->NeedPlayoutReference())
+      output_listener = audio_processor_handler_.get();
+  } else {
+    noop_reference_output_listener_ =
+        std::make_unique<NoopReferenceOutputListener>();
+    output_listener = noop_reference_output_listener_.get();
+  }
+  if (output_listener) {
+    // Unretained() is safe, since |event_handler_| outlives |output_tapper_|.
+    output_tapper_ = std::make_unique<OutputTapper>(
+        device_output_listener, output_listener,
+        base::BindRepeating(&EventHandler::OnLog,
+                            base::Unretained(event_handler_)));
+  }
+}
+#endif
+
 InputController::~InputController() {
   DCHECK_CALLED_ON_VALID_THREAD(owning_thread_);
   DCHECK(!audio_callback_);
diff --git a/services/audio/input_controller.h b/services/audio/input_controller.h
index 3bb32865..dfa50533 100644
--- a/services/audio/input_controller.h
+++ b/services/audio/input_controller.h
@@ -18,6 +18,7 @@
 #include "base/timer/timer.h"
 #include "build/build_config.h"
 #include "media/base/audio_parameters.h"
+#include "media/base/audio_processing.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -35,6 +36,7 @@
 }  // namespace media
 
 namespace audio {
+class AudioProcessorHandler;
 class OutputTapper;
 class DeviceOutputListener;
 class InputStreamActivityMonitor;
@@ -172,6 +174,9 @@
   void OnStreamInactive(Snoopable* snoopable) override;
 
  private:
+  // TODO(https://crbug.com/1224845): Remove after the output mixing experiment.
+  class NoopReferenceOutputListener;
+
   // Used to log the result of capture startup.
   // This was previously logged as a boolean with only the no callback and OK
   // options. The enum order is kept to ensure backwards compatibility.
@@ -245,6 +250,14 @@
   // Called once at first audio callback.
   void ReportIsAlive();
 
+#if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION)
+  // Called from the constructor. Helper to isolate logic setting up audio
+  // processing components.
+  void MaybeSetUpAudioProcessing(
+      const absl::optional<media::AudioProcessingSettings>& settings,
+      DeviceOutputListener* device_output_listener);
+#endif
+
   static StreamType ParamsToStreamType(const media::AudioParameters& params);
 
   // This class must be used on the audio manager thread.
@@ -266,6 +279,16 @@
   double max_volume_ = 0.0;
 
 #if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION)
+  // Handles audio processing effects applied to the microphone capture audio.
+  std::unique_ptr<AudioProcessorHandler> audio_processor_handler_;
+
+  // Placeholder for `audio_processor_handler_` when it is not created but
+  // `output_tapper_` still needs a ReferenceOutput::Listener to subscribe.
+  // TODO(https://crbug.com/1224845): Remove after the output mixing experiment.
+  std::unique_ptr<NoopReferenceOutputListener> noop_reference_output_listener_;
+
+  // Manages the `audio_processor_handler_` or
+  // `noop_reference_output_listener_` subscription to output audio.
   std::unique_ptr<OutputTapper> output_tapper_;
 #endif
 
diff --git a/services/audio/output_tapper.cc b/services/audio/output_tapper.cc
index 194ec0a..0822624 100644
--- a/services/audio/output_tapper.cc
+++ b/services/audio/output_tapper.cc
@@ -37,11 +37,15 @@
 };
 
 OutputTapper::OutputTapper(DeviceOutputListener* device_output_listener,
+                           ReferenceOutput::Listener* listener,
                            LogCallback log_callback)
     : device_output_listener_(device_output_listener),
+      listener_(listener),
       log_callback_(std::move(log_callback)) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
   DCHECK(device_output_listener_);
+  DCHECK(listener_);
+  DCHECK(log_callback_);
 }
 
 OutputTapper::~OutputTapper() {
@@ -74,26 +78,19 @@
 void OutputTapper::Stop() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
   DCHECK(active_);
-  device_output_listener_->StopListening(this);
+  device_output_listener_->StopListening(listener_);
   log_callback_.Run("OutputTapper: stop listening");
   active_ = false;
   uma_logger_.reset();
 }
 
-void OutputTapper::OnPlayoutData(const media::AudioBus& audio_bus,
-                                 int sample_rate,
-                                 base::TimeDelta delay) {
-  TRACE_EVENT2("audio", "OutputTapper::OnData", " this ",
-               static_cast<void*>(this), "delay", delay.InMillisecondsF());
-}
-
 void OutputTapper::StartListening() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
   DCHECK(active_);
   uma_logger_ = std::make_unique<UmaLogger>(output_device_id_);
   log_callback_.Run(base::StrCat(
       {"OutputTapper: listening to output device: ", output_device_id_}));
-  device_output_listener_->StartListening(this, output_device_id_);
+  device_output_listener_->StartListening(listener_, output_device_id_);
 }
 
 }  // namespace audio
diff --git a/services/audio/output_tapper.h b/services/audio/output_tapper.h
index e9b1594..304bbc6 100644
--- a/services/audio/output_tapper.h
+++ b/services/audio/output_tapper.h
@@ -13,22 +13,19 @@
 #include "base/strings/string_piece.h"
 #include "services/audio/reference_output.h"
 
-namespace media {
-class AudioBus;
-}  // namespace media
-
 namespace audio {
 class DeviceOutputListener;
 
-class OutputTapper final : public ReferenceOutput::Listener {
+class OutputTapper {
  public:
   using LogCallback = base::RepeatingCallback<void(base::StringPiece)>;
 
   OutputTapper(DeviceOutputListener* device_output_listener,
+               ReferenceOutput::Listener* listener,
                LogCallback log_callback);
   OutputTapper(const OutputTapper&) = delete;
   OutputTapper& operator=(const OutputTapper&) = delete;
-  ~OutputTapper() final;
+  ~OutputTapper();
 
   void SetOutputDeviceForAec(const std::string& output_device_id);
   void Start();
@@ -36,10 +33,6 @@
 
  private:
   class UmaLogger;
-  // Listener
-  void OnPlayoutData(const media::AudioBus& audio_bus,
-                     int sample_rate,
-                     base::TimeDelta delay) final;
 
   void StartListening();
 
@@ -47,6 +40,7 @@
   bool active_ = false;
   std::string output_device_id_;
   raw_ptr<DeviceOutputListener> const device_output_listener_;
+  raw_ptr<ReferenceOutput::Listener> const listener_;
   const LogCallback log_callback_;
   std::unique_ptr<UmaLogger> uma_logger_;
 };
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 5123b04..b68b674 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -13560,7 +13560,8 @@
               "name": "shard #${SHARD_INDEX} logcats"
             }
           ],
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 3
         },
         "test": "components_browsertests",
         "test_id_prefix": "ninja://components:components_browsertests/"
@@ -13663,7 +13664,7 @@
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 18
+          "shards": 25
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
diff --git a/testing/buildbot/chromium.webrtc.fyi.json b/testing/buildbot/chromium.webrtc.fyi.json
index 1969037..3ca374b 100644
--- a/testing/buildbot/chromium.webrtc.fyi.json
+++ b/testing/buildbot/chromium.webrtc.fyi.json
@@ -697,5 +697,112 @@
         "test_id_prefix": "ninja://remoting:remoting_unittests/"
       }
     ]
+  },
+  "WebRTC Chromium FYI ios-device": {},
+  "WebRTC Chromium FYI ios-simulator": {
+    "isolated_scripts": [
+      {
+        "args": [
+          "--platform",
+          "iPad Air (3rd generation)",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13a233",
+          "--xctest"
+        ],
+        "isolate_name": "ios_remoting_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_remoting_unittests_iPad Air (3rd generation) 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13a233",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/"
+      },
+      {
+        "args": [
+          "--platform",
+          "iPhone X",
+          "--version",
+          "14.5",
+          "--out-dir",
+          "${ISOLATED_OUTDIR}",
+          "--xcode-build-version",
+          "13a233",
+          "--xctest"
+        ],
+        "isolate_name": "ios_remoting_unittests",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "ios_remoting_unittests_iPhone X 14.5",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "cipd_packages": [
+            {
+              "cipd_package": "infra/tools/mac_toolchain/${platform}",
+              "location": ".",
+              "revision": "git_revision:723fc1a6c8cdf2631a57851f5610e598db0c1de1"
+            }
+          ],
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Mac-11|Mac-10.16"
+            }
+          ],
+          "named_caches": [
+            {
+              "name": "xcode_ios_13a233",
+              "path": "Xcode.app"
+            },
+            {
+              "name": "runtime_ios_14_5",
+              "path": "Runtime-ios-14.5"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test_id_prefix": "ninja://remoting/ios:ios_remoting_unittests/"
+      }
+    ]
   }
 }
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index bfa3fb8..7b58f71 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -1126,6 +1126,11 @@
           'shards': 2,
         },
       },
+      'android-asan': {
+        'swarming': {
+          'shards': 3,
+        },
+      },
       'android-marshmallow-arm64-rel': {
         'swarming': {
           'quickrun_shards': 2,
@@ -1324,7 +1329,7 @@
           '--test-launcher-filter-file=../../testing/buildbot/filters/android.asan.content_browsertests.filter',
         ],
         'swarming': {
-          'shards': 18,
+          'shards': 25,
         },
       },
       'android-marshmallow-arm64-rel': {
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 9ed502cc..c946f24f 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3896,6 +3896,10 @@
       'ios_showcase_eg2tests_module': {},
     },
 
+    'ios_remoting_fyi_unittests': {
+      'ios_remoting_unittests': {},
+    },
+
     'ios_screen_size_dependent_tests': {
       'base_unittests': {},
       'components_unittests': {},
@@ -7167,6 +7171,15 @@
       },
     },
 
+    'ios_webrtc_fyi_tests': {
+      'ios_remoting_fyi_unittests': {
+        'variants': [
+          'SIM_IPHONE_X_14_5',
+          'SIM_IPAD_AIR_3RD_GEN_14_5',
+        ]
+      },
+    },
+
     'lacros_all_tast_tests_eve': {
       'lacros_all_tast_tests': {
         'variants': [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index fb4b442..5f988fd 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -6006,6 +6006,20 @@
           'gtest_tests': 'webrtc_chromium_gtests',
         },
       },
+      'WebRTC Chromium FYI ios-device': {},
+      'WebRTC Chromium FYI ios-simulator': {
+        'mixins': [
+          'has_native_resultdb_integration',
+          'mac_11_x64',
+          'mac_toolchain',
+          'out_dir_arg',
+          'xcode_13_main',
+          'xctest',
+        ],
+        'test_suites': {
+          'isolated_scripts': 'ios_webrtc_fyi_tests',
+        },
+      },
     },
   },
   {
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 8ba2425c..f8416a2 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -6772,6 +6772,8 @@
         {
             "platforms": [
                 "android",
+                "chromeos",
+                "chromeos_lacros",
                 "ios",
                 "linux",
                 "mac",
@@ -6798,20 +6800,6 @@
                     ]
                 }
             ]
-        },
-        {
-            "platforms": [
-                "chromeos",
-                "chromeos_lacros"
-            ],
-            "experiments": [
-                {
-                    "name": "EnabledMprotect",
-                    "enable_features": [
-                        "WebAssemblyCodeProtection"
-                    ]
-                }
-            ]
         }
     ],
     "VaapiVp9kSVCEncoding": [
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 1c14d32..aa6989d 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -138,19 +138,14 @@
 #endif
 
 namespace WTF {
+
 template <>
-struct CrossThreadCopier<base::OnceCallback<void(base::TimeTicks)>>
+struct CrossThreadCopier<blink::WebFrameWidgetImpl::PromiseCallbacks>
     : public CrossThreadCopierByValuePassThrough<
-          base::OnceCallback<void(base::TimeTicks)>> {
+          blink::WebFrameWidgetImpl::PromiseCallbacks> {
   STATIC_ONLY(CrossThreadCopier);
 };
 
-template <>
-struct CrossThreadCopier<base::OnceCallback<void(gfx::CALayerResult)>>
-    : public CrossThreadCopierByValuePassThrough<
-          base::OnceCallback<void(gfx::CALayerResult)>> {
-  STATIC_ONLY(CrossThreadCopier);
-};
 }  // namespace WTF
 
 namespace blink {
@@ -2851,17 +2846,10 @@
 // swap promises.
 class ReportTimeSwapPromise : public cc::SwapPromise {
  public:
-  ReportTimeSwapPromise(
-      base::OnceCallback<void(base::TimeTicks)> swap_time_callback,
-      base::OnceCallback<void(base::TimeTicks)> presentation_time_callback,
-      base::OnceCallback<void(gfx::CALayerResult)>
-          core_animation_error_code_callback,
-      scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      WebFrameWidgetImpl* widget)
-      : swap_time_callback_(std::move(swap_time_callback)),
-        presentation_time_callback_(std::move(presentation_time_callback)),
-        core_animation_error_code_callback_(
-            std::move(core_animation_error_code_callback)),
+  ReportTimeSwapPromise(WebFrameWidgetImpl::PromiseCallbacks callbacks,
+                        scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+                        WebFrameWidgetImpl* widget)
+      : promise_callbacks_(std::move(callbacks)),
         task_runner_(std::move(task_runner)),
         widget_(widget) {}
 
@@ -2883,18 +2871,14 @@
     DCHECK_GT(frame_token_, 0u);
     PostCrossThreadTask(
         *task_runner_, FROM_HERE,
-        CrossThreadBindOnce(
-            &RunCallbackAfterSwap, widget_, base::TimeTicks::Now(),
-            std::move(swap_time_callback_),
-            std::move(presentation_time_callback_),
-            std::move(core_animation_error_code_callback_), frame_token_));
+        CrossThreadBindOnce(&RunCallbackAfterSwap, widget_,
+                            base::TimeTicks::Now(),
+                            std::move(promise_callbacks_), frame_token_));
   }
 
   DidNotSwapAction DidNotSwap(DidNotSwapReason reason) override {
     ReportSwapAndPresentationFailureOnTaskRunner(
-        task_runner_, std::move(swap_time_callback_),
-        std::move(presentation_time_callback_),
-        std::move(core_animation_error_code_callback_), base::TimeTicks::Now());
+        task_runner_, std::move(promise_callbacks_), base::TimeTicks::Now());
     return DidNotSwapAction::BREAK_PROMISE;
   }
 
@@ -2904,10 +2888,7 @@
   static void RunCallbackAfterSwap(
       WebFrameWidgetImpl* widget,
       base::TimeTicks swap_time,
-      base::OnceCallback<void(base::TimeTicks)> swap_time_callback,
-      base::OnceCallback<void(base::TimeTicks)> presentation_time_callback,
-      base::OnceCallback<void(gfx::CALayerResult)>
-          core_animation_error_code_callback,
+      WebFrameWidgetImpl::PromiseCallbacks callbacks,
       int frame_token) {
     // If the widget was collected or the widget wasn't collected yet, but
     // it was closed don't schedule a presentation callback.
@@ -2915,20 +2896,24 @@
       widget->widget_base_->AddPresentationCallback(
           frame_token,
           WTF::Bind(&RunCallbackAfterPresentation,
-                    std::move(presentation_time_callback), swap_time));
-      ReportTime(std::move(swap_time_callback), swap_time);
+                    std::move(callbacks.presentation_time_callback),
+                    swap_time));
+      ReportTime(std::move(callbacks.swap_time_callback), swap_time);
 
 #if BUILDFLAG(IS_MAC)
-      if (core_animation_error_code_callback) {
+      if (callbacks.core_animation_error_code_callback) {
         widget->widget_base_->AddCoreAnimationErrorCodeCallback(
-            frame_token, std::move(core_animation_error_code_callback));
+            frame_token,
+            std::move(callbacks.core_animation_error_code_callback));
       }
 #endif
     } else {
-      ReportTime(std::move(swap_time_callback), swap_time);
-      ReportTime(std::move(presentation_time_callback), swap_time);
-      ReportErrorCode(std::move(core_animation_error_code_callback),
+      ReportTime(std::move(callbacks.swap_time_callback), swap_time);
+      ReportTime(std::move(callbacks.presentation_time_callback), swap_time);
+#if BUILDFLAG(IS_MAC)
+      ReportErrorCode(std::move(callbacks.core_animation_error_code_callback),
                       gfx::kCALayerUnknownNoWidget);
+#endif
     }
   }
 
@@ -2956,41 +2941,38 @@
     if (callback)
       std::move(callback).Run(time);
   }
+
+#if BUILDFLAG(IS_MAC)
   static void ReportErrorCode(
       base::OnceCallback<void(gfx::CALayerResult)> callback,
       gfx::CALayerResult error_code) {
     if (callback)
       std::move(callback).Run(error_code);
   }
+#endif
 
   static void ReportSwapAndPresentationFailureOnTaskRunner(
       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-      base::OnceCallback<void(base::TimeTicks)> swap_time_callback,
-      base::OnceCallback<void(base::TimeTicks)> presentation_time_callback,
-      base::OnceCallback<void(gfx::CALayerResult)>
-          core_animation_error_code_callback,
+      WebFrameWidgetImpl::PromiseCallbacks callbacks,
       base::TimeTicks failure_time) {
     if (!task_runner->BelongsToCurrentThread()) {
       PostCrossThreadTask(
           *task_runner, FROM_HERE,
           CrossThreadBindOnce(&ReportSwapAndPresentationFailureOnTaskRunner,
-                              task_runner, std::move(swap_time_callback),
-                              std::move(presentation_time_callback),
-                              std::move(core_animation_error_code_callback),
-                              failure_time));
+                              task_runner, std::move(callbacks), failure_time));
       return;
     }
 
-    ReportTime(std::move(swap_time_callback), failure_time);
-    ReportTime(std::move(presentation_time_callback), failure_time);
-    ReportErrorCode(std::move(core_animation_error_code_callback),
+    ReportTime(std::move(callbacks.swap_time_callback), failure_time);
+    ReportTime(std::move(callbacks.presentation_time_callback), failure_time);
+#if BUILDFLAG(IS_MAC)
+    ReportErrorCode(std::move(callbacks.core_animation_error_code_callback),
                     gfx::kCALayerUnknownDidNotSwap);
+#endif
   }
 
-  base::OnceCallback<void(base::TimeTicks)> swap_time_callback_;
-  base::OnceCallback<void(base::TimeTicks)> presentation_time_callback_;
-  base::OnceCallback<void(gfx::CALayerResult)>
-      core_animation_error_code_callback_;
+  WebFrameWidgetImpl::PromiseCallbacks promise_callbacks_;
+
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   CrossThreadWeakPersistent<WebFrameWidgetImpl> widget_;
   uint32_t frame_token_ = 0;
@@ -2999,49 +2981,44 @@
 void WebFrameWidgetImpl::NotifySwapAndPresentationTimeForTesting(
     base::OnceCallback<void(base::TimeTicks)> swap_callback,
     base::OnceCallback<void(base::TimeTicks)> presentation_callback) {
-  NotifySwapAndPresentationTime(std::move(swap_callback),
-                                std::move(presentation_callback),
-                                base::NullCallback());
+  NotifySwapAndPresentationTime(
+      {.swap_time_callback = std::move(swap_callback),
+       .presentation_time_callback = std::move(presentation_callback)});
 }
 
 void WebFrameWidgetImpl::NotifyPresentationTimeInBlink(
     base::OnceCallback<void(base::TimeTicks)> presentation_callback) {
-  NotifySwapAndPresentationTime(base::NullCallback(),
-                                std::move(presentation_callback),
-                                base::NullCallback());
+  NotifySwapAndPresentationTime(
+      {.presentation_time_callback = std::move(presentation_callback)});
 }
 
 void WebFrameWidgetImpl::NotifyPresentationTime(
     base::OnceCallback<void(base::TimeTicks)> presentation_callback) {
-  NotifySwapAndPresentationTime(base::NullCallback(),
-                                std::move(presentation_callback),
-                                base::NullCallback());
+  NotifySwapAndPresentationTime(
+      {.presentation_time_callback = std::move(presentation_callback)});
 }
 
 #if BUILDFLAG(IS_MAC)
 void WebFrameWidgetImpl::NotifyCoreAnimationErrorCode(
     base::OnceCallback<void(gfx::CALayerResult)>
         core_animation_error_code_callback) {
-  NotifySwapAndPresentationTime(base::NullCallback(), base::NullCallback(),
-                                std::move(core_animation_error_code_callback));
+  NotifySwapAndPresentationTime(
+      {.core_animation_error_code_callback =
+           std::move(core_animation_error_code_callback)});
 }
 #endif
 
 void WebFrameWidgetImpl::NotifySwapAndPresentationTime(
-    base::OnceCallback<void(base::TimeTicks)> swap_time_callback,
-    base::OnceCallback<void(base::TimeTicks)> presentation_time_callback,
-    base::OnceCallback<void(gfx::CALayerResult)>
-        core_animation_error_code_callback) {
+    PromiseCallbacks callbacks) {
   if (!View()->does_composite())
     return;
+
   widget_base_->LayerTreeHost()->QueueSwapPromise(
-      std::make_unique<ReportTimeSwapPromise>(
-          std::move(swap_time_callback), std::move(presentation_time_callback),
-          std::move(core_animation_error_code_callback),
-          widget_base_->LayerTreeHost()
-              ->GetTaskRunnerProvider()
-              ->MainThreadTaskRunner(),
-          this));
+      std::make_unique<ReportTimeSwapPromise>(std::move(callbacks),
+                                              widget_base_->LayerTreeHost()
+                                                  ->GetTaskRunnerProvider()
+                                                  ->MainThreadTaskRunner(),
+                                              this));
 }
 
 scheduler::WebRenderWidgetSchedulingState*
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index a717e2c..8c6081a9 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -106,6 +106,15 @@
       public FrameWidget,
       public PageWidgetEventHandler {
  public:
+  struct PromiseCallbacks {
+    base::OnceCallback<void(base::TimeTicks)> swap_time_callback;
+    base::OnceCallback<void(base::TimeTicks)> presentation_time_callback;
+#if BUILDFLAG(IS_MAC)
+    base::OnceCallback<void(gfx::CALayerResult)>
+        core_animation_error_code_callback;
+#endif
+  };
+
   WebFrameWidgetImpl(
       base::PassKey<WebLocalFrame>,
       CrossVariantMojoAssociatedRemote<
@@ -640,11 +649,7 @@
   friend class WebViewImpl;
   friend class ReportTimeSwapPromise;
 
-  void NotifySwapAndPresentationTime(
-      base::OnceCallback<void(base::TimeTicks)> swap_callback,
-      base::OnceCallback<void(base::TimeTicks)> presentation_callback,
-      base::OnceCallback<void(gfx::CALayerResult)>
-          core_animation_error_code_callback);
+  void NotifySwapAndPresentationTime(PromiseCallbacks callbacks);
 
   // WidgetBaseClient overrides.
   void BeginCommitCompositorFrame() override;
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni
index 899707e..f69b0ca 100644
--- a/third_party/blink/renderer/core/layout/build.gni
+++ b/third_party/blink/renderer/core/layout/build.gni
@@ -589,6 +589,8 @@
   "ng/table/layout_ng_table_section_interface.h",
   "ng/table/ng_table_borders.cc",
   "ng/table/ng_table_borders.h",
+  "ng/table/ng_table_child_iterator.cc",
+  "ng/table/ng_table_child_iterator.h",
   "ng/table/ng_table_constraint_space_data.h",
   "ng/table/ng_table_fragment_data.h",
   "ng/table/ng_table_layout_algorithm.cc",
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
index 3e2a97f5..169805f 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -180,6 +180,10 @@
     return GetLayoutObject();
   }
 
+  bool IsRelayoutBoundary() const {
+    return layout_object_->IsRelayoutBoundary();
+  }
+
   wtf_size_t DeltaToNextForSameLayoutObject() const {
     return delta_to_next_for_same_layout_object_;
   }
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
index 975fd13..2c87ec9 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.cc
@@ -297,6 +297,15 @@
            line.MoveToNext()) {
         const NGFragmentItem& line_child = *line.Current().Item();
         DCHECK(line_child.CanReuse());
+#if DCHECK_IS_ON()
+        // |RebuildFragmentTreeSpine| does not rebuild spine if |NeedsLayout|.
+        // Such block needs to copy PostLayout fragment while running simplified
+        // layout.
+        absl::optional<NGPhysicalBoxFragment::AllowPostLayoutScope>
+            allow_post_layout;
+        if (line_child.IsRelayoutBoundary())
+          allow_post_layout.emplace();
+#endif
         items_.emplace_back(
             line_converter.ToLogical(
                 line_child.OffsetInContainerFragment() - line_box_bounds.offset,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.cc b/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.cc
index 56d3b83b..ae4eef5 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.cc
@@ -12,10 +12,17 @@
 namespace blink {
 
 NGBlockChildIterator::NGBlockChildIterator(NGLayoutInputNode first_child,
-                                           const NGBlockBreakToken* break_token)
+                                           const NGBlockBreakToken* break_token,
+                                           bool calculate_child_idx)
     : next_unstarted_child_(first_child),
       break_token_(break_token),
       child_token_idx_(0) {
+  if (calculate_child_idx) {
+    // If we are set up to provide the child index, we also need to visit all
+    // siblings, also when processing break tokens.
+    child_idx_.emplace(0);
+    tracked_child_ = first_child;
+  }
   if (break_token_) {
     const auto& child_break_tokens = break_token_->ChildBreakTokens();
     // If there are child break tokens, we don't yet know which one is the the
@@ -33,11 +40,13 @@
 NGBlockChildIterator::Entry NGBlockChildIterator::NextChild(
     const NGInlineBreakToken* previous_inline_break_token) {
   if (previous_inline_break_token) {
+    DCHECK(!child_idx_);
     return Entry(previous_inline_break_token->InputNode(),
-                 previous_inline_break_token);
+                 previous_inline_break_token, absl::nullopt);
   }
 
   const NGBreakToken* current_child_break_token = nullptr;
+  absl::optional<wtf_size_t> current_child_idx;
   NGLayoutInputNode current_child = next_unstarted_child_;
   if (break_token_) {
     // If we're resuming layout after a fragmentainer break, we'll first resume
@@ -45,23 +54,37 @@
     // each).
     DCHECK(!next_unstarted_child_);
     const auto& child_break_tokens = break_token_->ChildBreakTokens();
-    if (child_token_idx_ < child_break_tokens.size()) {
-      current_child_break_token = child_break_tokens[child_token_idx_++];
-      current_child = current_child_break_token->InputNode();
+    DCHECK_LT(child_token_idx_, child_break_tokens.size());
+    current_child_break_token = child_break_tokens[child_token_idx_++];
+    current_child = current_child_break_token->InputNode();
 
-      if (child_token_idx_ == child_break_tokens.size()) {
-        // We reached the last child break token. Prepare for the next unstarted
-        // sibling, and forget the parent break token.
-        if (!break_token_->HasSeenAllChildren())
-          next_unstarted_child_ = current_child.NextSibling();
-        break_token_ = nullptr;
+    if (child_idx_) {
+      while (tracked_child_ != current_child) {
+        tracked_child_ = tracked_child_.NextSibling();
+        (*child_idx_)++;
       }
+      current_child_idx = child_idx_;
+    }
+
+    if (child_token_idx_ == child_break_tokens.size()) {
+      // We reached the last child break token. Prepare for the next unstarted
+      // sibling, and forget the parent break token.
+      if (!break_token_->HasSeenAllChildren())
+        AdvanceToNextChild(current_child);
+      break_token_ = nullptr;
     }
   } else if (next_unstarted_child_) {
-    next_unstarted_child_ = next_unstarted_child_.NextSibling();
+    current_child_idx = child_idx_;
+    AdvanceToNextChild(next_unstarted_child_);
   }
 
-  return Entry(current_child, current_child_break_token);
+  return Entry(current_child, current_child_break_token, current_child_idx);
+}
+
+void NGBlockChildIterator::AdvanceToNextChild(const NGLayoutInputNode& child) {
+  next_unstarted_child_ = child.NextSibling();
+  if (child_idx_)
+    (*child_idx_)++;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h b/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h
index 05ce87b1..66d5f70 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_CHILD_ITERATOR_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_NG_BLOCK_CHILD_ITERATOR_H_
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_layout_input_node.h"
 
@@ -32,7 +33,8 @@
 
  public:
   NGBlockChildIterator(NGLayoutInputNode first_child,
-                       const NGBlockBreakToken* break_token);
+                       const NGBlockBreakToken* break_token,
+                       bool calculate_child_idx = false);
 
   // Returns the next input node which should be laid out, along with its
   // respective break token.
@@ -44,23 +46,31 @@
       const NGInlineBreakToken* previous_inline_break_token = nullptr);
 
  private:
+  void AdvanceToNextChild(const NGLayoutInputNode&);
+
   NGLayoutInputNode next_unstarted_child_;
+  NGLayoutInputNode tracked_child_ = nullptr;
   const NGBlockBreakToken* break_token_;
 
   // An index into break_token_'s ChildBreakTokens() vector. Used for keeping
   // track of the next child break token to inspect.
   wtf_size_t child_token_idx_;
+
+  absl::optional<wtf_size_t> child_idx_;
 };
 
 struct NGBlockChildIterator::Entry {
   STACK_ALLOCATED();
 
  public:
-  Entry(NGLayoutInputNode node, const NGBreakToken* token)
-      : node(node), token(token) {}
+  Entry(NGLayoutInputNode node,
+        const NGBreakToken* token,
+        absl::optional<wtf_size_t> index = absl::nullopt)
+      : node(node), token(token), index(index) {}
 
   NGLayoutInputNode node;
   const NGBreakToken* token;
+  absl::optional<wtf_size_t> index;
 
   bool operator==(const NGBlockChildIterator::Entry& other) const {
     return node == other.node && token == other.token;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
index a8ea7df..ef19181 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_block_layout_algorithm.cc
@@ -961,10 +961,6 @@
     }
   }
 
-  // At this point, perform any final table-cell adjustments needed.
-  if (ConstraintSpace().IsTableCell())
-    FinalizeForTableCell(unconstrained_intrinsic_block_size);
-
   if (UNLIKELY(InvolvedInBlockFragmentation(container_builder_))) {
     NGBreakStatus status = FinalizeForFragmentation();
     if (status != NGBreakStatus::kContinue) {
@@ -981,6 +977,10 @@
 #endif
   }
 
+  // At this point, perform any final table-cell adjustments needed.
+  if (ConstraintSpace().IsTableCell())
+    FinalizeForTableCell(unconstrained_intrinsic_block_size);
+
   NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), &container_builder_).Run();
 
   // Adjust the position of the final baseline if needed.
@@ -2224,6 +2224,11 @@
   container_builder_.SetTableCellColumnIndex(
       ConstraintSpace().TableCellColumnIndex());
 
+  // If we're resuming after a break, there'll be no alignment, since the
+  // fragment will start at the block-start edge of the fragmentainer then.
+  if (IsResumingLayout(BreakToken()))
+    return;
+
   switch (Style().VerticalAlign()) {
     case EVerticalAlign::kTop:
       // Do nothing for 'top' vertical alignment.
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_child_iterator.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_child_iterator.cc
new file mode 100644
index 0000000..64641bb
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_child_iterator.cc
@@ -0,0 +1,162 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/layout/ng/table/ng_table_child_iterator.h"
+
+#include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
+
+namespace blink {
+
+NGTableChildIterator::NGTableChildIterator(
+    const NGTableGroupedChildren& grouped_children,
+    const NGBlockBreakToken* break_token)
+    : grouped_children_(&grouped_children), break_token_(break_token) {
+  if (break_token_) {
+    const auto& child_break_tokens = break_token_->ChildBreakTokens();
+    if (child_break_tokens.empty()) {
+      // There are no nodes to resume...
+      if (break_token_->HasSeenAllChildren()) {
+        // ...and we have seen all children. This means that we have no work
+        // left to do.
+        grouped_children_ = nullptr;
+        return;
+      } else {
+        // ...but we haven't seen all children yet. This means that we need to
+        // start at the beginning.
+        break_token_ = nullptr;
+      }
+    }
+  }
+
+  if (grouped_children_->captions.size()) {
+    // Find the first top caption, if any.
+    while (caption_idx_ < grouped_children_->captions.size()) {
+      if (grouped_children_->captions[caption_idx_].Style().CaptionSide() ==
+          ECaptionSide::kTop)
+        return;
+      caption_idx_++;
+    }
+    // Didn't find a top caption. Prepare for looking for bottom captions, once
+    // we're through the section iterator.
+    caption_idx_ = 0;
+  }
+
+  // Start the section iterator.
+  section_iterator_.emplace(grouped_children_->begin());
+}
+
+NGTableChildIterator::Entry NGTableChildIterator::NextChild() {
+  const NGBlockBreakToken* current_child_break_token = nullptr;
+  NGBlockNode current_child(nullptr);
+
+  if (break_token_) {
+    const auto& child_break_tokens = break_token_->ChildBreakTokens();
+    if (child_token_idx_ < child_break_tokens.size()) {
+      current_child_break_token =
+          To<NGBlockBreakToken>(child_break_tokens[child_token_idx_++].Get());
+      current_child = To<NGBlockNode>(current_child_break_token->InputNode());
+
+      // Normally (for non-tables), when we're out of break tokens, we can
+      // just proceed to the next sibling node, but we can't do this for
+      // tables, since the captions and sections get reordered as: top
+      // captions, table header, table bodies, table footer, bottom captions.
+      // Also keep track of the section index as we advance.
+      while (CurrentChild() != current_child) {
+        AdvanceChild();
+        DCHECK(CurrentChild());
+      }
+
+      if (child_token_idx_ == child_break_tokens.size()) {
+        // We reached the last child break token. Proceed with the next
+        // unstarted child, unless we've already seen all children (in which
+        // case we're done).
+        if (break_token_->HasSeenAllChildren())
+          grouped_children_ = nullptr;
+        break_token_ = nullptr;
+      }
+    }
+  } else {
+    current_child = CurrentChild();
+  }
+
+  wtf_size_t current_section_idx = section_idx_;
+  AdvanceChild();
+
+  return Entry(current_child, current_child_break_token, current_section_idx);
+}
+
+NGBlockNode NGTableChildIterator::CurrentChild() const {
+  if (!grouped_children_)
+    return NGBlockNode(nullptr);  // We have nothing.
+
+  if (!section_iterator_) {
+    // We're at a top caption, since we have no iterator yet.
+    DCHECK_EQ(grouped_children_->captions[caption_idx_].Style().CaptionSide(),
+              ECaptionSide::kTop);
+    return grouped_children_->captions[caption_idx_];
+  }
+
+  if (*section_iterator_ != grouped_children_->end()) {
+    // We're at a table section.
+    return **section_iterator_;
+  }
+
+  if (caption_idx_ < grouped_children_->captions.size()) {
+    // We're at a bottom caption, since the iterator is at end().
+    DCHECK_EQ(grouped_children_->captions[caption_idx_].Style().CaptionSide(),
+              ECaptionSide::kBottom);
+    return grouped_children_->captions[caption_idx_];
+  }
+
+  // We're done.
+  return NGBlockNode(nullptr);
+}
+
+void NGTableChildIterator::AdvanceChild() {
+  if (!grouped_children_)
+    return;
+  if (!section_iterator_) {
+    // We're currently at a top caption. See if there are more of them.
+    caption_idx_++;
+    while (caption_idx_ < grouped_children_->captions.size()) {
+      if (grouped_children_->captions[caption_idx_].Style().CaptionSide() ==
+          ECaptionSide::kTop)
+        return;
+      caption_idx_++;
+    }
+
+    // We're done with the top captions, but we'll go through the captions
+    // vector again after the table sections, to look for bottom captions.
+    caption_idx_ = 0;
+
+    // But first we need to look for sections.
+    DCHECK(!section_iterator_);
+    section_iterator_.emplace(grouped_children_->begin());
+    if (*section_iterator_ != grouped_children_->end())
+      return;  // Found a section.
+
+    // No sections. Proceed to bottom captions.
+  } else {
+    if (*section_iterator_ != grouped_children_->end()) {
+      // Go to the next section, if any.
+      ++(*section_iterator_);
+      section_idx_++;
+      if (*section_iterator_ != grouped_children_->end())
+        return;  // Found another section.
+      // No more sections. Proceed to bottom captions.
+    } else {
+      // Go to the the next bottom caption, if any.
+      caption_idx_++;
+    }
+  }
+
+  while (caption_idx_ < grouped_children_->captions.size()) {
+    if (grouped_children_->captions[caption_idx_].Style().CaptionSide() ==
+        ECaptionSide::kBottom)
+      return;
+    caption_idx_++;
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_child_iterator.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_child_iterator.h
new file mode 100644
index 0000000..b014e62
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_child_iterator.h
@@ -0,0 +1,85 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_CHILD_ITERATOR_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_CHILD_ITERATOR_H_
+
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
+#include "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_types.h"
+
+namespace blink {
+
+class NGBlockBreakToken;
+
+// A utility class for table layout which given the first child and a break
+// token will iterate through unfinished children.
+//
+// NextChild() is used to iterate through the children. This will be done in
+// child layout order (i.e. in this order: top captions, table header, table
+// bodies, table footer, bottom captions). If there are child break tokens,
+// though, their nodes will be processed first, in break token order.
+class CORE_EXPORT NGTableChildIterator {
+  STACK_ALLOCATED();
+
+ public:
+  NGTableChildIterator(const NGTableGroupedChildren&, const NGBlockBreakToken*);
+
+  class Entry {
+    STACK_ALLOCATED();
+
+   public:
+    Entry(NGBlockNode node,
+          const NGBlockBreakToken* token,
+          wtf_size_t section_index)
+        : node(node), token(token), section_index(section_index) {}
+
+    const NGBlockNode GetNode() const { return node; }
+    const NGBlockBreakToken* GetBreakToken() const { return token; }
+    wtf_size_t GetSectionIndex() const {
+      DCHECK(!node.IsTableCaption());
+      return section_index;
+    }
+    explicit operator bool() const { return !!node; }
+
+   private:
+    NGBlockNode node;
+    const NGBlockBreakToken* token;
+    wtf_size_t section_index;
+  };
+
+  // Returns the next node which should be laid out, along with its
+  // respective break token.
+  Entry NextChild();
+
+ private:
+  NGBlockNode CurrentChild() const;
+  void AdvanceChild();
+
+  const NGTableGroupedChildren* grouped_children_;
+  const NGBlockBreakToken* break_token_;
+
+  // The sections iterator is used to walk through the table sections in layout
+  // order, i.e. table header, table bodies, table footer. If it is unset, it
+  // means that we're processing top captions. If it's at end(), it means that
+  // we should look for bottom captions.
+  absl::optional<NGTableGroupedChildrenIterator> section_iterator_;
+
+  // An index into break_token_'s ChildBreakTokens() vector. Used for keeping
+  // track of the next child break token to inspect.
+  wtf_size_t child_token_idx_ = 0;
+
+  // An index into the current table caption. We're walking through the captions
+  // twice. First we look for top captions. Then we walk through the sections
+  // iterator. Then we walk through the captions again, looking for bottom
+  // captions.
+  wtf_size_t caption_idx_ = 0;
+
+  wtf_size_t section_idx_ = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_NG_TABLE_NG_TABLE_CHILD_ITERATOR_H_
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
index 044d601..2dfd4d8 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm.cc
@@ -23,6 +23,7 @@
 #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_column_visitor.h"
 #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_section.h"
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_borders.h"
+#include "third_party/blink/renderer/core/layout/ng/table/ng_table_child_iterator.h"
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_constraint_space_data.h"
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_fragment_data.h"
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_helpers.h"
@@ -58,6 +59,48 @@
   return caption_min_max;
 }
 
+NGConstraintSpace CreateCaptionConstraintSpace(
+    const NGConstraintSpace& table_constraint_space,
+    const ComputedStyle& table_style,
+    const NGBlockNode& caption,
+    LogicalSize available_size,
+    absl::optional<LayoutUnit> block_offset = absl::nullopt) {
+  NGConstraintSpaceBuilder builder(table_constraint_space,
+                                   caption.Style().GetWritingDirection(),
+                                   /* is_new_fc */ true);
+  SetOrthogonalFallbackInlineSizeIfNeeded(table_style, caption, &builder);
+  builder.SetAvailableSize(available_size);
+  builder.SetPercentageResolutionSize(available_size);
+  builder.SetInlineAutoBehavior(NGAutoBehavior::kStretchImplicit);
+
+  if (block_offset) {
+    SetupSpaceBuilderForFragmentation(table_constraint_space, caption,
+                                      *block_offset, &builder,
+                                      /* is_new_fc */ true, false);
+  }
+
+  return builder.ToConstraintSpace();
+}
+
+NGTableLayoutAlgorithm::CaptionResult LayoutCaption(
+    const NGConstraintSpace& table_constraint_space,
+    const ComputedStyle& table_style,
+    LayoutUnit table_inline_size,
+    const NGConstraintSpace& caption_constraint_space,
+    const NGBlockNode& caption,
+    const NGBlockBreakToken* break_token = nullptr) {
+  scoped_refptr<const NGLayoutResult> layout_result =
+      caption.Layout(caption_constraint_space, break_token);
+  NGFragment fragment(table_constraint_space.GetWritingDirection(),
+                      layout_result->PhysicalFragment());
+  NGBoxStrut margins = ComputeMarginsFor(
+      caption_constraint_space, caption.Style(), table_constraint_space);
+  ResolveInlineMargins(caption.Style(), table_style, table_inline_size,
+                       fragment.InlineSize(), &margins);
+
+  return {caption, std::move(layout_result), margins};
+}
+
 void ComputeCaptionFragments(
     const NGConstraintSpace& table_constraint_space,
     const ComputedStyle& table_style,
@@ -67,37 +110,31 @@
     LayoutUnit& captions_block_size) {
   const LogicalSize available_size = {table_inline_size, kIndefiniteSize};
   for (NGBlockNode caption : grouped_children.captions) {
-    const auto& caption_style = caption.Style();
-
-    NGConstraintSpaceBuilder builder(table_constraint_space,
-                                     caption_style.GetWritingDirection(),
-                                     /* is_new_fc */ true);
-    SetOrthogonalFallbackInlineSizeIfNeeded(table_style, caption, &builder);
-    builder.SetAvailableSize(available_size);
-    builder.SetPercentageResolutionSize(available_size);
-    builder.SetInlineAutoBehavior(NGAutoBehavior::kStretchImplicit);
-    NGConstraintSpace caption_constraint_space = builder.ToConstraintSpace();
+    NGConstraintSpace caption_constraint_space = CreateCaptionConstraintSpace(
+        table_constraint_space, table_style, caption, available_size);
 
     // If we are discarding the results (compute-only) and we are after layout
-    // (|!NeedsLayout|,) make sure not to update the cached layout results.
+    // (|!NeedsLayout|), or if we are in block fragmentation, make sure not to
+    // update the cached layout results. If we are block fragmented, a node may
+    // generate multiple fragments, so make sure that we keep the fragments
+    // generated and stored in the actual layout pass.
+    //
+    // TODO(mstensho): We can remove this if we only perform this operation once
+    // per table node (and e.g. store the table data in the break tokens).
     absl::optional<NGDisableSideEffectsScope> disable_side_effects;
-    if (!captions && !caption.GetLayoutBox()->NeedsLayout())
+    if ((!captions && !caption.GetLayoutBox()->NeedsLayout()) ||
+        table_constraint_space.HasBlockFragmentation())
       disable_side_effects.emplace();
 
-    scoped_refptr<const NGLayoutResult> caption_result =
-        caption.Layout(caption_constraint_space);
+    NGTableLayoutAlgorithm::CaptionResult caption_result =
+        LayoutCaption(table_constraint_space, table_style, table_inline_size,
+                      caption_constraint_space, caption);
     NGFragment fragment(table_constraint_space.GetWritingDirection(),
-                        caption_result->PhysicalFragment());
-    NGBoxStrut margins = ComputeMarginsFor(
-        caption_constraint_space, caption_style, table_constraint_space);
-    ResolveInlineMargins(caption_style, table_style, table_inline_size,
-                         fragment.InlineSize(), &margins);
-
-    captions_block_size += fragment.BlockSize() + margins.BlockSum();
-    if (captions) {
-      captions->push_back(NGTableLayoutAlgorithm::CaptionResult{
-          caption, std::move(caption_result), margins});
-    }
+                        caption_result.layout_result->PhysicalFragment());
+    captions_block_size +=
+        fragment.BlockSize() + caption_result.margins.BlockSum();
+    if (captions)
+      captions->push_back(caption_result);
   }
 }
 
@@ -453,8 +490,6 @@
 }
 
 scoped_refptr<const NGLayoutResult> NGTableLayoutAlgorithm::Layout() {
-  DCHECK(!IsResumingLayout(BreakToken()));
-
   const bool is_fixed_layout = Style().IsFixedTableLayout();
   const LogicalSize border_spacing = Style().TableBorderSpacing();
   NGTableGroupedChildren grouped_children(Node());
@@ -767,9 +802,11 @@
                                 cell_block_constraints, border_spacing);
 
   const NGBoxStrut border_padding = container_builder_.BorderPadding();
-  LayoutUnit block_offset;
+  LayoutUnit child_block_offset;
+  bool needs_end_border_spacing = false;
+  bool has_processed_first_child = false;
 
-  auto AddCaptionResult = [&](const auto& caption,
+  auto AddCaptionResult = [&](const CaptionResult& caption,
                               LayoutUnit* block_offset) -> void {
     NGBlockNode node = caption.node;
     node.StoreMargins(
@@ -786,10 +823,18 @@
                      caption.margins.block_end;
   };
 
+  // We have already laid out the captions, in order to calculate the table grid
+  // size. We can re-use these results now, unless we're in block fragmentation.
+  // In that case we need to lay them out again now, so that they fragment and
+  // resume properly.
+  const bool relayout_captions = ConstraintSpace().HasBlockFragmentation();
+
   // Add all the top captions.
-  for (const auto& caption : captions) {
-    if (caption.node.Style().CaptionSide() == ECaptionSide::kTop)
-      AddCaptionResult(caption, &block_offset);
+  if (!relayout_captions) {
+    for (const auto& caption : captions) {
+      if (caption.node.Style().CaptionSide() == ECaptionSide::kTop)
+        AddCaptionResult(caption, &child_block_offset);
+    }
   }
 
   // Section setup.
@@ -800,8 +845,10 @@
 
   auto CreateSectionConstraintSpace = [&table_writing_direction,
                                        &section_available_inline_size,
-                                       &constraint_space_data,
-                                       &sections](wtf_size_t section_index) {
+                                       &constraint_space_data, &sections,
+                                       this](const NGBlockNode& section,
+                                             LayoutUnit block_offset,
+                                             wtf_size_t section_index) {
     NGConstraintSpaceBuilder section_space_builder(
         table_writing_direction.GetWritingMode(), table_writing_direction,
         /* is_new_fc */ true);
@@ -821,35 +868,154 @@
         {section_available_inline_size, kIndefiniteSize});
     section_space_builder.SetTableSectionData(constraint_space_data,
                                               section_index);
+
+    if (ConstraintSpace().HasBlockFragmentation()) {
+      SetupSpaceBuilderForFragmentation(
+          ConstraintSpace(), section, block_offset, &section_space_builder,
+          /* is_new_fc */ true,
+          container_builder_.RequiresContentBeforeBreaking());
+    }
+
     return section_space_builder.ToConstraintSpace();
   };
 
-  // Generate section fragments.
+  auto GridBlockSize = [&border_padding, &minimal_table_grid_block_size, this](
+                           LayoutUnit start_offset,
+                           LayoutUnit end_offset) -> LayoutUnit {
+    DCHECK_GE(end_offset, start_offset);
+    LayoutUnit grid_block_size = end_offset - start_offset;
+    grid_block_size += border_padding.block_end;
+    if (!IsResumingLayout(BreakToken())) {
+      grid_block_size =
+          std::max(grid_block_size, minimal_table_grid_block_size);
+    }
+    return grid_block_size;
+  };
+
+  // Generate section fragments, and also caption fragments, if we need to
+  // regenerate them (block fragmentation).
   LogicalOffset section_offset;
   section_offset.inline_offset =
       border_padding.inline_start + border_spacing.inline_size;
-  section_offset.block_offset = block_offset + border_padding.block_start;
+  section_offset.block_offset = child_block_offset + border_padding.block_start;
 
   absl::optional<LayoutUnit> table_baseline;
-  wtf_size_t section_index = 0;
-  bool needs_end_border_spacing = false;
-  for (NGBlockNode section : grouped_children) {
-    scoped_refptr<const NGLayoutResult> section_result =
-        section.Layout(CreateSectionConstraintSpace(section_index++));
-    const NGPhysicalBoxFragment& physical_fragment =
-        To<NGPhysicalBoxFragment>(section_result->PhysicalFragment());
+
+  LayoutUnit first_section_block_offset = child_block_offset;
+  LayoutUnit grid_block_size;
+  bool broke_inside = false;
+  bool is_past_first_section_start = false;
+  bool is_past_last_section_end = false;
+  NGTableChildIterator child_iterator(grouped_children, BreakToken());
+  for (auto entry = child_iterator.NextChild();
+       NGBlockNode child = entry.GetNode();
+       entry = child_iterator.NextChild()) {
+    const NGBlockBreakToken* child_break_token = entry.GetBreakToken();
+    scoped_refptr<const NGLayoutResult> child_result;
+    LayoutUnit child_inline_offset;
+    if (child.IsTableCaption()) {
+      if (!relayout_captions)
+        continue;
+      if (child.Style().CaptionSide() == ECaptionSide::kBottom &&
+          !is_past_last_section_end) {
+        // We found a bottom caption, which means that we're done with all the
+        // sections. We need to calculate the grid size now, so that we set the
+        // block-offset for the caption correctly.
+        is_past_last_section_end = true;
+        if (needs_end_border_spacing)
+          section_offset.block_offset += border_spacing.block_size;
+        grid_block_size = GridBlockSize(first_section_block_offset,
+                                        section_offset.block_offset);
+        child_block_offset = first_section_block_offset + grid_block_size;
+      }
+
+      LogicalSize available_size(container_builder_.InlineSize(),
+                                 kIndefiniteSize);
+      NGConstraintSpace child_space =
+          CreateCaptionConstraintSpace(ConstraintSpace(), Style(), child,
+                                       available_size, child_block_offset);
+      CaptionResult caption = LayoutCaption(
+          ConstraintSpace(), Style(), container_builder_.InlineSize(),
+          child_space, child, child_break_token);
+      child_result = caption.layout_result;
+      child_block_offset += caption.margins.block_start;
+      child_inline_offset = caption.margins.inline_start;
+    } else {
+      if (!is_past_first_section_start) {
+        is_past_first_section_start = true;
+        first_section_block_offset = child_block_offset;
+        child_block_offset += border_padding.block_start;
+      }
+
+      if (ConstraintSpace().HasBlockFragmentation()) {
+        // TODO(mstensho): Border-spacing should only be included if there are
+        // table parts inside, but currently we need to lay out before we can
+        // check that. Always assume that we need border spacing for now (as
+        // long as we're not resuming inside the section).
+        if (!IsResumingLayout(child_break_token))
+          child_block_offset += border_spacing.block_size;
+      }
+
+      NGConstraintSpace child_space = CreateSectionConstraintSpace(
+          child, child_block_offset, entry.GetSectionIndex());
+      child_result = child.Layout(child_space, child_break_token);
+      child_inline_offset = section_offset.inline_offset;
+    }
+    if (ConstraintSpace().HasBlockFragmentation()) {
+      LayoutUnit fragmentainer_block_offset =
+          ConstraintSpace().FragmentainerOffsetAtBfc() + child_block_offset;
+      NGBreakStatus break_status = BreakBeforeChildIfNeeded(
+          ConstraintSpace(), child, *child_result, fragmentainer_block_offset,
+          has_processed_first_child, &container_builder_);
+      if (break_status != NGBreakStatus::kContinue) {
+        broke_inside = true;
+        break;
+      }
+    }
+
+    const auto& physical_fragment =
+        To<NGPhysicalBoxFragment>(child_result->PhysicalFragment());
     NGBoxFragment fragment(table_writing_direction, physical_fragment);
-    if (fragment.HasDescendantsForTablePart()) {
-      section_offset.block_offset += border_spacing.block_size;
-      needs_end_border_spacing = true;
+    if (!child.IsTableCaption()) {
+      if (fragment.HasDescendantsForTablePart()) {
+        // Border-spacing has pre-emptively been added if we're participating in
+        // block fragmentation. Otherwise add it now.
+        if (!ConstraintSpace().HasBlockFragmentation())
+          child_block_offset += border_spacing.block_size;
+        // We want to add border-spacing after this section, but not if the
+        // current fragment is past the block-end of the section. This might
+        // happen if there are overflowing descendants, and this section should
+        // just create an zero-sized fragment.
+        needs_end_border_spacing =
+            !child_break_token || !child_break_token->IsAtBlockEnd();
+      }
+      if (!table_baseline) {
+        if (const auto& section_baseline = fragment.Baseline())
+          table_baseline = *section_baseline + child_block_offset;
+      }
     }
-    container_builder_.AddResult(*section_result, section_offset);
-    if (!table_baseline) {
-      if (const auto& section_baseline = fragment.Baseline())
-        table_baseline = *section_baseline + section_offset.block_offset;
+
+    container_builder_.AddResult(
+        *child_result, LogicalOffset(child_inline_offset, child_block_offset));
+    child_block_offset += fragment.BlockSize();
+
+    if (!child.IsTableCaption())
+      section_offset.block_offset = child_block_offset;
+
+    if (ConstraintSpace().HasBlockFragmentation()) {
+      has_processed_first_child = true;
+      if (container_builder_.HasInflowChildBreakInside()) {
+        broke_inside = true;
+        break;
+      }
     }
-    section_offset.block_offset += fragment.BlockSize();
   }
+
+  if (!child_iterator.NextChild())
+    container_builder_.SetHasSeenAllChildren();
+  if (broke_inside)
+    needs_end_border_spacing = false;
+
   if (needs_end_border_spacing)
     section_offset.block_offset += border_spacing.block_size;
   LayoutUnit column_block_size =
@@ -857,28 +1023,36 @@
   if (needs_end_border_spacing)
     column_block_size -= border_spacing.block_size * 2;
 
-  const LayoutUnit grid_block_size = std::max(
-      section_offset.block_offset - block_offset + border_padding.block_end,
-      minimal_table_grid_block_size);
-  const LogicalRect table_grid_rect(LayoutUnit(), block_offset,
+  if (!is_past_last_section_end) {
+    // If we haven't already calculated the grid size, do so now.
+    grid_block_size =
+        GridBlockSize(first_section_block_offset, section_offset.block_offset);
+    child_block_offset = first_section_block_offset + grid_block_size;
+  }
+
+  const LogicalRect table_grid_rect(LayoutUnit(), first_section_block_offset,
                                     container_builder_.InlineSize(),
                                     grid_block_size);
-  block_offset += grid_block_size;
 
   // Add all the bottom captions.
-  for (const auto& caption : captions) {
-    if (caption.node.Style().CaptionSide() == ECaptionSide::kBottom)
-      AddCaptionResult(caption, &block_offset);
+  if (!relayout_captions) {
+    for (const auto& caption : captions) {
+      if (caption.node.Style().CaptionSide() == ECaptionSide::kBottom)
+        AddCaptionResult(caption, &child_block_offset);
+    }
   }
 
-  LayoutUnit block_size = std::max(grid_block_size, block_offset);
-  if (ConstraintSpace().IsFixedBlockSize()) {
-    container_builder_.SetFragmentBlockSize(
-        ConstraintSpace().AvailableSize().block_size);
-  } else {
-    container_builder_.SetFragmentBlockSize(block_size);
-  }
+  LayoutUnit previously_consumed_block_size;
+  if (UNLIKELY(BreakToken()))
+    previously_consumed_block_size = BreakToken()->ConsumedBlockSize();
+
+  LayoutUnit block_size = std::max(
+      grid_block_size, child_block_offset + previously_consumed_block_size);
   container_builder_.SetIntrinsicBlockSize(block_size);
+  if (ConstraintSpace().IsFixedBlockSize())
+    block_size = ConstraintSpace().AvailableSize().block_size;
+
+  container_builder_.SetFragmentsTotalBlockSize(block_size);
 
   const WritingModeConverter grid_converter(
       Style().GetWritingDirection(),
@@ -892,11 +1066,22 @@
 
   if (Node().GetDOMNode() &&
       Node().GetDOMNode()->HasTagName(mathml_names::kMtableTag))
-    table_baseline = MathTableBaseline(Style(), block_offset);
+    table_baseline = MathTableBaseline(Style(), child_block_offset);
   if (table_baseline)
     container_builder_.SetBaseline(*table_baseline);
 
   container_builder_.SetIsTableNGPart();
+
+  if (UNLIKELY(InvolvedInBlockFragmentation(container_builder_))) {
+    NGBreakStatus status = FinishFragmentation(
+        Node(), ConstraintSpace(), BorderPadding().block_end,
+        FragmentainerSpaceAtBfcStart(ConstraintSpace()), &container_builder_);
+    if (status == NGBreakStatus::kDisableFragmentation)
+      return container_builder_.Abort(NGLayoutResult::kDisableFragmentation);
+    // TODO(mstensho): Deal with early-breaks.
+    DCHECK_EQ(status, NGBreakStatus::kContinue);
+  }
+
   NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), &container_builder_).Run();
   return container_builder_.ToBoxFragment();
 }
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
index 91fc871..fe6acee 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.cc
@@ -191,14 +191,15 @@
     // possible. The one exception is "is_hidden_for_paint". This is set to
     // true if a cell should be hidden within a collapsed column. If this is
     // the case, the size is almost certainly different causing a second layout.
-    return NGTableAlgorithmUtils::CreateTableCellConstraintSpace(
-        table_writing_direction, cell, cell_borders,
-        {cell_inline_size, kIndefiniteSize}, cell_percentage_inline_size,
-        /* alignment_baseline */ absl::nullopt, start_column,
-        /* is_initial_block_size_indefinite */ true,
-        is_table_block_size_specified,
-        /* is_hidden_for_paint */ false, has_collapsed_borders,
-        NGCacheSlot::kMeasure);
+    return NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder(
+               table_writing_direction, cell, cell_borders,
+               {cell_inline_size, kIndefiniteSize}, cell_percentage_inline_size,
+               /* alignment_baseline */ absl::nullopt, start_column,
+               /* is_initial_block_size_indefinite */ true,
+               is_table_block_size_specified,
+               /* is_hidden_for_paint */ false, has_collapsed_borders,
+               NGCacheSlot::kMeasure)
+        .ToConstraintSpace();
   };
 
   // TODO(layout-ng) Scrollbars should be frozen when computing row sizes.
@@ -450,7 +451,8 @@
 }  // namespace
 
 // static
-NGConstraintSpace NGTableAlgorithmUtils::CreateTableCellConstraintSpace(
+NGConstraintSpaceBuilder
+NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder(
     const WritingDirectionMode table_writing_direction,
     const NGBlockNode cell,
     const NGBoxStrut& cell_borders,
@@ -500,7 +502,7 @@
       !has_collapsed_borders && cell_style.EmptyCells() == EEmptyCells::kHide);
   builder.SetCacheSlot(cache_slot);
 
-  return builder.ToConstraintSpace();
+  return builder;
 }
 
 // Computes maximum possible number of non-mergeable columns.
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
index e16ef709..44c8ef0a 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_layout_algorithm_utils.h
@@ -13,7 +13,7 @@
 
 class NGBlockNode;
 class NGBoxFragment;
-class NGConstraintSpace;
+class NGConstraintSpaceBuilder;
 class NGTableBorders;
 enum class NGCacheSlot;
 struct LogicalSize;
@@ -30,11 +30,11 @@
            align == EVerticalAlign::kLength;
   }
 
-  // Creates a constraint-space for a table-cell.
+  // Creates a constraint space builder for a table-cell.
   //
   // In order to make the cache as effective as possible, we try and keep
   // creating the constraint-space for table-cells as consistent as possible.
-  static NGConstraintSpace CreateTableCellConstraintSpace(
+  static NGConstraintSpaceBuilder CreateTableCellConstraintSpaceBuilder(
       const WritingDirectionMode table_writing_direction,
       const NGBlockNode cell,
       const NGBoxStrut& cell_borders,
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
index 514dc132..cb212e3 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.cc
@@ -5,8 +5,10 @@
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_row_layout_algorithm.h"
 
 #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/ng/table/layout_ng_table_cell.h"
@@ -25,8 +27,8 @@
   auto CreateCellConstraintSpace = [this, &row, &table_data](
                                        NGBlockNode cell, wtf_size_t cell_index,
                                        absl::optional<LayoutUnit> row_baseline,
-                                       LayoutUnit* cell_inline_offset =
-                                           nullptr) {
+                                       LayoutUnit* cell_inline_offset = nullptr,
+                                       bool use_block_fragmentation = false) {
     const wtf_size_t start_column = table_data.cells[cell_index].start_column;
     const wtf_size_t end_column =
         std::min(start_column + cell.TableCellColspan() - 1,
@@ -74,12 +76,24 @@
         table_data.column_locations[cell_location_start_column].is_collapsed &&
         cell_location_start_column == cell_location_end_column;
 
-    return NGTableAlgorithmUtils::CreateTableCellConstraintSpace(
-        table_data.table_writing_direction, cell, cell_data.borders,
-        {cell_inline_size, cell_block_size}, container_builder_.InlineSize(),
-        row_baseline, start_column, !is_initial_block_size_definite,
-        table_data.is_table_block_size_specified, is_hidden_for_paint,
-        table_data.has_collapsed_borders, NGCacheSlot::kLayout);
+    NGConstraintSpaceBuilder builder =
+        NGTableAlgorithmUtils::CreateTableCellConstraintSpaceBuilder(
+            table_data.table_writing_direction, cell, cell_data.borders,
+            {cell_inline_size, cell_block_size},
+            container_builder_.InlineSize(), row_baseline, start_column,
+            !is_initial_block_size_definite,
+            table_data.is_table_block_size_specified, is_hidden_for_paint,
+            table_data.has_collapsed_borders, NGCacheSlot::kLayout);
+
+    if (use_block_fragmentation) {
+      SetupSpaceBuilderForFragmentation(
+          ConstraintSpace(), cell,
+          /* fragmentainer_offset_delta */ LayoutUnit(), &builder,
+          /* is_new_fc */ true,
+          container_builder_.RequiresContentBeforeBreaking());
+    }
+
+    return builder.ToConstraintSpace();
   };
 
   // A cell with perecentage block-size descendants can layout with size that
@@ -87,8 +101,8 @@
   // cell was baseline-aligned.
   // To compute correct baseline, we need to do an initial layout pass.
   LayoutUnit row_baseline = row.baseline;
-  wtf_size_t cell_index = row.start_cell_index;
   if (row.has_baseline_aligned_percentage_block_size_descendants) {
+    wtf_size_t cell_index = row.start_cell_index;
     NGRowBaselineTabulator row_baseline_tabulator;
     for (NGBlockNode cell = To<NGBlockNode>(Node().FirstChild()); cell;
          cell = To<NGBlockNode>(cell.NextSibling()), ++cell_index) {
@@ -109,15 +123,21 @@
   }
 
   // Generate cell fragments.
-  cell_index = row.start_cell_index;
   NGRowBaselineTabulator row_baseline_tabulator;
-  for (NGBlockNode cell = To<NGBlockNode>(Node().FirstChild()); cell;
-       cell = To<NGBlockNode>(cell.NextSibling()), ++cell_index) {
+  NGBlockChildIterator child_iterator(Node().FirstChild(), BreakToken(),
+                                      /* calculate_child_idx */ true);
+  for (auto entry = child_iterator.NextChild();
+       NGBlockNode cell = To<NGBlockNode>(entry.node);
+       entry = child_iterator.NextChild()) {
+    const auto* cell_break_token = To<NGBlockBreakToken>(entry.token);
+    wtf_size_t cell_index = row.start_cell_index + *entry.index;
     LayoutUnit cell_inline_offset;
     NGConstraintSpace cell_constraint_space = CreateCellConstraintSpace(
-        cell, cell_index, row_baseline, &cell_inline_offset);
+        cell, cell_index, row_baseline, &cell_inline_offset,
+        ConstraintSpace().HasBlockFragmentation());
     scoped_refptr<const NGLayoutResult> cell_result =
-        cell.Layout(cell_constraint_space);
+        cell.Layout(cell_constraint_space, cell_break_token);
+    // TODO(mstensho): Propagate break-before and break-after values to the row.
     container_builder_.AddResult(
         *cell_result,
         {cell_inline_offset - table_data.table_border_spacing.inline_size,
@@ -131,12 +151,28 @@
         cell.TableCellRowspan() > 1,
         cell_result->HasDescendantThatDependsOnPercentageBlockSize());
   }
-  container_builder_.SetFragmentBlockSize(row.block_size);
+
+  // Since we always visit all cells in a row (cannot break halfway through;
+  // each cell establishes a parallel flows that needs to be examined
+  // separately), we have seen all children by now.
+  container_builder_.SetHasSeenAllChildren();
+
+  container_builder_.SetFragmentsTotalBlockSize(row.block_size);
+
   container_builder_.SetBaseline(
       row_baseline_tabulator.ComputeBaseline(row.block_size));
   if (row.is_collapsed)
     container_builder_.SetIsHiddenForPaint(true);
   container_builder_.SetIsTableNGPart();
+
+  if (UNLIKELY(InvolvedInBlockFragmentation(container_builder_))) {
+    NGBreakStatus status = FinishFragmentation(
+        Node(), ConstraintSpace(), BorderPadding().block_end,
+        FragmentainerSpaceAtBfcStart(ConstraintSpace()), &container_builder_);
+    // TODO(mstensho): Deal with early-breaks.
+    DCHECK_EQ(status, NGBreakStatus::kContinue);
+  }
+
   NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), &container_builder_).Run();
   return container_builder_.ToBoxFragment();
 }
diff --git a/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc b/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc
index 45cc0d2..ee3368f 100644
--- a/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.cc
@@ -5,7 +5,9 @@
 #include "third_party/blink/renderer/core/layout/ng/table/ng_table_section_layout_algorithm.h"
 
 #include "third_party/blink/renderer/core/layout/ng/ng_block_break_token.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_block_child_iterator.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_constraint_space_builder.h"
+#include "third_party/blink/renderer/core/layout/ng/ng_fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_out_of_flow_layout_part.h"
 #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
 
@@ -37,11 +39,21 @@
                                       kIndefiniteSize};
   LogicalOffset offset;
   bool is_first_row = true;
-  wtf_size_t row_index = table_data.sections[section_index].start_row_index;
-  for (NGBlockNode row = To<NGBlockNode>(Node().FirstChild()); row;
-       row = To<NGBlockNode>(row.NextSibling())) {
+  const wtf_size_t start_row_index =
+      table_data.sections[section_index].start_row_index;
+  NGBlockChildIterator child_iterator(Node().FirstChild(), BreakToken(),
+                                      /* calculate_child_idx */ true);
+  for (auto entry = child_iterator.NextChild();
+       NGBlockNode row = To<NGBlockNode>(entry.node);
+       entry = child_iterator.NextChild()) {
+    const auto* row_break_token = To<NGBlockBreakToken>(entry.token);
+    wtf_size_t row_index = start_row_index + *entry.index;
     DCHECK_LT(row_index, table_data.sections[section_index].start_row_index +
                              table_data.sections[section_index].row_count);
+
+    if (!is_first_row && !table_data.rows[row_index].is_collapsed)
+      offset.block_offset += table_data.table_border_spacing.block_size;
+
     NGConstraintSpaceBuilder row_space_builder(
         table_data.table_writing_direction.GetWritingMode(),
         table_data.table_writing_direction,
@@ -50,33 +62,75 @@
     row_space_builder.SetPercentageResolutionSize(available_size);
     row_space_builder.SetIsFixedInlineSize(true);
     row_space_builder.SetTableRowData(&table_data, row_index);
+
+    if (ConstraintSpace().HasBlockFragmentation()) {
+      SetupSpaceBuilderForFragmentation(
+          ConstraintSpace(), row, offset.block_offset, &row_space_builder,
+          /* is_new_fc */ true,
+          container_builder_.RequiresContentBeforeBreaking());
+    }
+
     NGConstraintSpace row_space = row_space_builder.ToConstraintSpace();
-    scoped_refptr<const NGLayoutResult> row_result = row.Layout(row_space);
+    scoped_refptr<const NGLayoutResult> row_result =
+        row.Layout(row_space, row_break_token);
+
+    LayoutUnit previously_consumed_row_block_size;
+    if (ConstraintSpace().HasBlockFragmentation()) {
+      if (row_break_token) {
+        previously_consumed_row_block_size =
+            row_break_token->ConsumedBlockSize();
+      }
+      LayoutUnit fragmentainer_block_offset =
+          ConstraintSpace().FragmentainerOffsetAtBfc() + offset.block_offset;
+      NGBreakStatus break_status = BreakBeforeChildIfNeeded(
+          ConstraintSpace(), row, *row_result, fragmentainer_block_offset,
+          !is_first_row, &container_builder_);
+      if (break_status != NGBreakStatus::kContinue)
+        break;
+    }
+
     if (is_first_row) {
       const NGPhysicalBoxFragment& physical_fragment =
           To<NGPhysicalBoxFragment>(row_result->PhysicalFragment());
       DCHECK(physical_fragment.Baseline());
       section_baseline = physical_fragment.Baseline();
-    } else if (!table_data.rows[row_index].is_collapsed) {
-      offset.block_offset += table_data.table_border_spacing.block_size;
     }
     container_builder_.AddResult(*row_result, offset);
-    offset.block_offset += table_data.rows[row_index].block_size;
+    offset.block_offset += table_data.rows[row_index].block_size -
+                           previously_consumed_row_block_size;
     is_first_row = false;
-    row_index++;
+
+    if (container_builder_.HasInflowChildBreakInside())
+      break;
   }
+
+  if (!child_iterator.NextChild().node)
+    container_builder_.SetHasSeenAllChildren();
+
+  LayoutUnit block_size;
   if (ConstraintSpace().IsFixedBlockSize()) {
     // A fixed block-size should only occur for a section without children.
     DCHECK_EQ(table_data.sections[section_index].row_count, 0u);
-    container_builder_.SetFragmentBlockSize(
-        ConstraintSpace().AvailableSize().block_size);
+    block_size = ConstraintSpace().AvailableSize().block_size;
   } else {
-    container_builder_.SetFragmentBlockSize(offset.block_offset);
+    block_size = offset.block_offset;
+    if (BreakToken())
+      block_size += BreakToken()->ConsumedBlockSize();
   }
+  container_builder_.SetFragmentsTotalBlockSize(block_size);
+
   if (section_baseline)
     container_builder_.SetBaseline(*section_baseline);
   container_builder_.SetIsTableNGPart();
 
+  if (UNLIKELY(InvolvedInBlockFragmentation(container_builder_))) {
+    NGBreakStatus status = FinishFragmentation(
+        Node(), ConstraintSpace(), BorderPadding().block_end,
+        FragmentainerSpaceAtBfcStart(ConstraintSpace()), &container_builder_);
+    // TODO(mstensho): Deal with early-breaks.
+    DCHECK_EQ(status, NGBreakStatus::kContinue);
+  }
+
   NGOutOfFlowLayoutPart(Node(), ConstraintSpace(), &container_builder_).Run();
   return container_builder_.ToBoxFragment();
 }
diff --git a/third_party/blink/renderer/core/svg/svg_animation_element.cc b/third_party/blink/renderer/core/svg/svg_animation_element.cc
index 53ccaef..146c83c4 100644
--- a/third_party/blink/renderer/core/svg/svg_animation_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_animation_element.cc
@@ -80,7 +80,7 @@
 }
 
 static bool ParseKeyTimes(const String& string,
-                          Vector<float>& result,
+                          HeapVector<float>& result,
                           bool verify_order) {
   result.clear();
   Vector<String> parse_list;
@@ -163,6 +163,13 @@
   return true;
 }
 
+void SVGAnimationElement::Trace(Visitor* visitor) const {
+  visitor->Trace(key_times_from_attribute_);
+  visitor->Trace(key_times_for_paced_);
+  visitor->Trace(key_points_);
+  SVGSMILElement::Trace(visitor);
+}
+
 void SVGAnimationElement::ParseAttribute(
     const AttributeModificationParams& params) {
   const QualifiedName& name = params.name;
@@ -372,7 +379,7 @@
   use_paced_key_times_ = true;
   key_times_for_paced_.clear();
 
-  Vector<float> calculated_key_times;
+  HeapVector<float> calculated_key_times;
   float total_distance = 0;
   calculated_key_times.push_back(0);
   for (unsigned n = 0; n < values_count - 1; ++n) {
diff --git a/third_party/blink/renderer/core/svg/svg_animation_element.h b/third_party/blink/renderer/core/svg/svg_animation_element.h
index 779b21a..635dc6e 100644
--- a/third_party/blink/renderer/core/svg/svg_animation_element.h
+++ b/third_party/blink/renderer/core/svg/svg_animation_element.h
@@ -81,6 +81,8 @@
   // previous animations are rendered useless.
   bool OverwritesUnderlyingAnimationValue() const;
 
+  void Trace(Visitor* visitor) const override;
+
  protected:
   SVGAnimationElement(const QualifiedName&, Document&);
 
@@ -146,7 +148,7 @@
   // or key_times_for_paced_ by toggling the flag use_paced_key_times_.
   void CalculateKeyTimesForCalcModePaced();
 
-  Vector<float> const& KeyTimes() const {
+  HeapVector<float> const& KeyTimes() const {
     return use_paced_key_times_ ? key_times_for_paced_
                                 : key_times_from_attribute_;
   }
@@ -179,10 +181,10 @@
   // Storing two sets of values to avoid overwriting (or reparsing) when
   // calc-mode changes. Fix for Issue 231525. What list to use is
   // decided in CalculateKeyTimesForCalcModePaced.
-  Vector<float> key_times_from_attribute_;
-  Vector<float> key_times_for_paced_;
+  HeapVector<float> key_times_from_attribute_;
+  HeapVector<float> key_times_for_paced_;
 
-  Vector<float> key_points_;
+  HeapVector<float> key_points_;
   Vector<gfx::CubicBezier> key_splines_;
   String last_values_animation_from_;
   String last_values_animation_to_;
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
index ec231e6..6725599 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.cc
@@ -19,6 +19,25 @@
 #include "third_party/blink/renderer/platform/wtf/cross_thread_functional.h"
 
 namespace blink {
+// Subscribes a sink to the playout data source for the duration of the
+// PlayoutListener lifetime.
+class MediaStreamAudioProcessor::PlayoutListener {
+ public:
+  PlayoutListener(scoped_refptr<WebRtcAudioDeviceImpl> playout_data_source,
+                  WebRtcPlayoutDataSource::Sink* sink)
+      : playout_data_source_(std::move(playout_data_source)), sink_(sink) {
+    DCHECK(playout_data_source_);
+    DCHECK(sink_);
+    playout_data_source_->AddPlayoutSink(sink_);
+  }
+
+  ~PlayoutListener() { playout_data_source_->RemovePlayoutSink(sink_); }
+
+ private:
+  // TODO(crbug.com/704136): Replace with Member at some point.
+  scoped_refptr<WebRtcAudioDeviceImpl> const playout_data_source_;
+  WebRtcPlayoutDataSource::Sink* const sink_;
+};
 
 MediaStreamAudioProcessor::MediaStreamAudioProcessor(
     DeliverProcessedAudioCallback deliver_processed_audio_callback,
@@ -34,15 +53,15 @@
                        media::AudioProcessor::GetDefaultOutputFormat(
                            capture_data_source_params,
                            settings)),
-      playout_data_source_(std::move(playout_data_source)),
       main_thread_runner_(base::ThreadTaskRunnerHandle::Get()),
       aec_dump_agent_impl_(AecDumpAgentImpl::Create(this)),
       stopped_(false) {
   DCHECK(main_thread_runner_);
-  // Register as a listener for the playout reference signal. Used for echo
-  // cancellation and gain control.
-  if (audio_processor_.RequiresPlayoutReference() && playout_data_source_) {
-    playout_data_source_->AddPlayoutSink(this);
+  // Register as a listener for the playout reference signal. Used for e.g. echo
+  // cancellation.
+  if (settings.NeedPlayoutReference() && playout_data_source) {
+    playout_listener_ =
+        std::make_unique<PlayoutListener>(std::move(playout_data_source), this);
   }
   DETACH_FROM_THREAD(capture_thread_checker_);
   DETACH_FROM_THREAD(render_thread_checker_);
@@ -76,10 +95,7 @@
 
   aec_dump_agent_impl_.reset();
   audio_processor_.OnStopDump();
-  if (audio_processor_.RequiresPlayoutReference() && playout_data_source_) {
-    playout_data_source_->RemovePlayoutSink(this);
-    playout_data_source_ = nullptr;
-  }
+  playout_listener_.reset();
 }
 
 const media::AudioParameters&
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
index 716997b..9254913 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_audio_processor.h
@@ -108,6 +108,7 @@
   ~MediaStreamAudioProcessor() override;
 
  private:
+  class PlayoutListener;
   friend class MediaStreamAudioProcessorTest;
 
   // Format of input to ProcessCapturedAudio().
@@ -134,8 +135,9 @@
   // Handles audio processing, rebuffering, and input/output formatting.
   media::AudioProcessor audio_processor_;
 
-  // TODO(crbug.com/704136): Replace with Member at some point.
-  scoped_refptr<WebRtcAudioDeviceImpl> playout_data_source_;
+  // Manages subscription to the playout reference audio. Must be outlived by
+  // |audio_processor_|.
+  std::unique_ptr<PlayoutListener> playout_listener_;
 
   // Task runner for the main render thread.
   const scoped_refptr<base::SingleThreadTaskRunner> main_thread_runner_;
diff --git a/third_party/blink/renderer/modules/serial/serial_port.cc b/third_party/blink/renderer/modules/serial/serial_port.cc
index 6fd2807..2c16efb 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/serial/serial_port.h"
 
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/bindings/modules/v8/v8_serial_input_signals.h"
@@ -13,6 +14,8 @@
 #include "third_party/blink/renderer/bindings/modules/v8/v8_serial_port_info.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
+#include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
 #include "third_party/blink/renderer/core/streams/writable_stream.h"
 #include "third_party/blink/renderer/modules/event_target_modules_names.h"
@@ -249,8 +252,9 @@
   }
   buffer_size_ = options->bufferSize();
 
+  hardware_flow_control_ = options->flowControl() == "hardware";
   mojo_options->has_cts_flow_control = true;
-  mojo_options->cts_flow_control = options->flowControl() == "hardware";
+  mojo_options->cts_flow_control = hardware_flow_control_;
 
   mojo::PendingRemote<device::mojom::blink::SerialPortClient> client;
   open_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
@@ -343,6 +347,13 @@
 ScriptPromise SerialPort::setSignals(ScriptState* script_state,
                                      const SerialOutputSignals* signals,
                                      ExceptionState& exception_state) {
+  ExecutionContext* context = GetExecutionContext();
+  if (!context) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kNotSupportedError,
+                                      "Script context has shut down.");
+    return ScriptPromise();
+  }
+
   if (!port_.is_bound()) {
     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                       kPortClosed);
@@ -363,6 +374,17 @@
   if (signals->hasRequestToSend()) {
     mojo_signals->has_rts = true;
     mojo_signals->rts = signals->requestToSend();
+
+    if (hardware_flow_control_) {
+      // This combination may be deprecated in the future but generate a console
+      // warning for now: https://github.com/WICG/serial/issues/158
+      context->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
+          mojom::blink::ConsoleMessageSource::kRecommendation,
+          mojom::blink::ConsoleMessageLevel::kInfo,
+          "The RTS (request to send) signal should not be configured manually "
+          "when using hardware flow control. This combination may not be "
+          "supported on all platforms."));
+    }
   }
   if (signals->hasBrk()) {
     mojo_signals->has_brk = true;
diff --git a/third_party/blink/renderer/modules/serial/serial_port.h b/third_party/blink/renderer/modules/serial/serial_port.h
index 27d01e8..7690f7f6 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.h
+++ b/third_party/blink/renderer/modules/serial/serial_port.h
@@ -120,6 +120,9 @@
   // reopened on demand.
   bool closing_ = false;
 
+  // The port was opened with { flowControl: "hardware" }.
+  bool hardware_flow_control_ = false;
+
   // Resolver for the Promise returned by open().
   Member<ScriptPromiseResolver> open_resolver_;
   // Resolvers for the Promises returned by getSignals() and setSignals() to
diff --git a/third_party/blink/renderer/platform/heap/collection_support/heap_vector_backing.h b/third_party/blink/renderer/platform/heap/collection_support/heap_vector_backing.h
index 46e8d26d..d982c04 100644
--- a/third_party/blink/renderer/platform/heap/collection_support/heap_vector_backing.h
+++ b/third_party/blink/renderer/platform/heap/collection_support/heap_vector_backing.h
@@ -144,17 +144,14 @@
     //   This is fine because the fact that the object can be initialized
     //   with memset indicates that it is safe to treat the zerod slot
     //   as a valid object.
-    static_assert(!IsTraceableInCollectionTrait<Traits>::value ||
-                      Traits::kCanClearUnusedSlotsWithMemset ||
-                      std::is_polymorphic<T>::value,
-                  "HeapVectorBacking doesn't support objects that cannot be "
-                  "cleared as unused with memset.");
+    static_assert(
+        Traits::kCanClearUnusedSlotsWithMemset || std::is_polymorphic<T>::value,
+        "HeapVectorBacking doesn't support objects that cannot be "
+        "cleared as unused with memset.");
 
-    // This trace method is instantiated for vectors where
-    // IsTraceableInCollectionTrait<Traits>::value is false, but the trace
-    // method should not be called. Thus we cannot static-assert
-    // IsTraceableInCollectionTrait<Traits>::value but should runtime-assert it.
-    DCHECK(IsTraceableInCollectionTrait<Traits>::value);
+    // Bail out early if the contents are not actually traceable.
+    if constexpr (!IsTraceableInCollectionTrait<Traits>::value)
+      return;
 
     const T* array = reinterpret_cast<const T*>(self);
     const size_t length =
diff --git a/third_party/blink/renderer/platform/heap/test/heap_test.cc b/third_party/blink/renderer/platform/heap/test/heap_test.cc
index b93c5806..9b01ae5 100644
--- a/third_party/blink/renderer/platform/heap/test/heap_test.cc
+++ b/third_party/blink/renderer/platform/heap/test/heap_test.cc
@@ -3249,4 +3249,20 @@
   EXPECT_LE(css_bytes_before + sizeof(FakeCSSValue), css_bytes_after);
 }
 
+TEST_F(HeapTest, ContainerAnnotationOnTinyBacking) {
+  // Regression test: https://crbug.com/1292392
+  //
+  // This test aims to check that ASAN container annotations work for backing
+  // with sizeof(T) < 8 (which is smaller than ASAN's shadow granularity), size
+  // =1, and capacity = 1.
+  HeapVector<uint32_t> vector;
+  DCHECK_EQ(0u, vector.capacity());
+  vector.ReserveCapacity(1);
+  DCHECK_EQ(1u, vector.capacity());
+  // The following push_back() should not crash, even with container
+  // annotations. The critical path expands the backing without allocating a new
+  // one.
+  vector.ReserveCapacity(2);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/wtf/container_annotations.h b/third_party/blink/renderer/platform/wtf/container_annotations.h
index 3f64ff12..e2493a1 100644
--- a/third_party/blink/renderer/platform/wtf/container_annotations.h
+++ b/third_party/blink/renderer/platform/wtf/container_annotations.h
@@ -14,36 +14,39 @@
 #if defined(ADDRESS_SANITIZER) &&                      \
     (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && \
     defined(ARCH_CPU_X86_64)
+
+// Annotations require buffers to begin on an 8-byte boundary. See
+// documentation:
+//   https://github.com/llvm-mirror/compiler-rt/blob/master/include/sanitizer/common_interface_defs.h#L154
+
 #define ANNOTATE_CONTIGUOUS_CONTAINER
+
 #define ANNOTATE_NEW_BUFFER(buffer, capacity, newSize)                       \
   if (buffer) {                                                              \
     __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
                                               (buffer) + (capacity),         \
                                               (buffer) + (newSize));         \
   }
+
 #define ANNOTATE_DELETE_BUFFER(buffer, capacity, oldSize)                    \
   if (buffer) {                                                              \
     __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
                                               (buffer) + (oldSize),          \
                                               (buffer) + (capacity));        \
   }
+
 #define ANNOTATE_CHANGE_SIZE(buffer, capacity, oldSize, newSize)             \
   if (buffer) {                                                              \
     __sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
                                               (buffer) + (oldSize),          \
                                               (buffer) + (newSize));         \
   }
-#define ANNOTATE_CHANGE_CAPACITY(buffer, oldCapacity, bufferSize, newCapacity) \
-  ANNOTATE_DELETE_BUFFER(buffer, oldCapacity, bufferSize);                     \
-  ANNOTATE_NEW_BUFFER(buffer, newCapacity, bufferSize);
-// Annotations require buffers to begin on an 8-byte boundary.
 
 #else
 
 #define ANNOTATE_NEW_BUFFER(buffer, capacity, newSize)
 #define ANNOTATE_DELETE_BUFFER(buffer, capacity, oldSize)
 #define ANNOTATE_CHANGE_SIZE(buffer, capacity, oldSize, newSize)
-#define ANNOTATE_CHANGE_CAPACITY(buffer, oldCapacity, bufferSize, newCapacity)
 
 #endif
 
diff --git a/third_party/blink/renderer/platform/wtf/vector.h b/third_party/blink/renderer/platform/wtf/vector.h
index 4622d27..0d69812 100644
--- a/third_party/blink/renderer/platform/wtf/vector.h
+++ b/third_party/blink/renderer/platform/wtf/vector.h
@@ -340,25 +340,23 @@
   const T* Buffer() const { return buffer_; }
   wtf_size_t capacity() const { return capacity_; }
 
+  static constexpr bool NeedsToClearUnusedSlots() {
+    // Tracing and finalization access all slots of a vector backing. In case
+    // there's work to be done there unused slots should be cleared.
+    return Allocator::kIsGarbageCollected &&
+           (IsTraceableInCollectionTrait<VectorTraits<T>>::value ||
+            VectorTraits<T>::kNeedsDestruction);
+  }
+
   void ClearUnusedSlots(T* from, T* to) {
-    // If the vector backing is garbage-collected and needs tracing or
-    // finalizing, we clear out the unused slots so that the visitor or the
-    // finalizer does not cause a problem when visiting the unused slots.
-    static_assert(
-        !Allocator::kIsGarbageCollected ||
-            IsTraceableInCollectionTrait<VectorTraits<T>>::value,
-        "Type in garbage collected vectors should be traceable in collection");
-    if constexpr (Allocator::kIsGarbageCollected)
+    if constexpr (NeedsToClearUnusedSlots()) {
       AtomicMemzero(reinterpret_cast<void*>(from), sizeof(T) * (to - from));
+    }
   }
 
   void CheckUnusedSlots(const T* from, const T* to) {
 #if DCHECK_IS_ON() && !defined(ANNOTATE_CONTIGUOUS_CONTAINER)
-    static_assert(
-        !Allocator::kIsGarbageCollected ||
-            IsTraceableInCollectionTrait<VectorTraits<T>>::value,
-        "Type in garbage collected vectors should be traceable in collection");
-    if constexpr (Allocator::kIsGarbageCollected) {
+    if constexpr (NeedsToClearUnusedSlots()) {
       const unsigned char* unused_area =
           reinterpret_cast<const unsigned char*>(from);
       const unsigned char* end_address =
@@ -1672,15 +1670,31 @@
     Base::AllocateBuffer(new_capacity);
     return;
   }
-  wtf_size_t old_capacity = capacity();
-  // The Allocator::isGarbageCollected check is not needed.  The check is just
-  // a static hint for a compiler to indicate that Base::expandBuffer returns
-  // false if Allocator is a PartitionAllocator.
-  if (Allocator::kIsGarbageCollected && Base::ExpandBuffer(new_capacity)) {
-    DCHECK_LE(old_capacity, capacity());
-    ANNOTATE_CHANGE_CAPACITY(begin(), old_capacity, size_, capacity());
-    return;
+
+  if constexpr (Allocator::kIsGarbageCollected) {
+    wtf_size_t old_capacity = capacity();
+    // Unpoison container annotations. Note that in the case of sizeof(T) < 8,
+    // size_ = 1, old_capacity = 1, this may leave behind state in ASAN's shadow
+    // memory. The additional transition after expanding ensures that this state
+    // is cleared.
+    //
+    // Details see
+    //   https://github.com/llvm-mirror/compiler-rt/blob/master/lib/asan/asan_poisoning.cpp#L354
+    MARKING_AWARE_ANNOTATE_CHANGE_SIZE(Allocator, begin(), old_capacity, size_,
+                                       old_capacity);
+    if (Base::ExpandBuffer(new_capacity)) {
+      // The following transition clears out old ASAN shadow memory state in the
+      // case mentioned above.
+      new_capacity = capacity();
+      DCHECK_LE(old_capacity, new_capacity);
+      ANNOTATE_CHANGE_SIZE(begin(), new_capacity, old_capacity, new_capacity);
+      // Finally, assuming new capacity, re-poison with the used size.
+      ANNOTATE_CHANGE_SIZE(begin(), new_capacity, new_capacity, size_);
+    }
+    // In case expansion failed, there's no need to adjust container
+    // annotations, as the buffer is freed right away.
   }
+
   // Reallocating a backing buffer may resurrect a dead object.
   CHECK(Allocator::IsAllocationAllowed());
 
@@ -2034,8 +2048,6 @@
 Vector<T, inlineCapacity, Allocator>::Trace(VisitorDispatcher visitor) const {
   static_assert(Allocator::kIsGarbageCollected,
                 "Garbage collector must be enabled.");
-  static_assert(IsTraceableInCollectionTrait<VectorTraits<T>>::value,
-                "Type must be traceable in collection");
 
   const T* buffer = BufferSafe();
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 401d81b..4ad4ea2 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -241,13 +241,6 @@
 crbug.com/1126305 http/tests/inspector-protocol/prerender/* [ Skip ]
 crbug.com/1126305 virtual/prerender/http/tests/inspector-protocol/prerender/* [ Pass ]
 
-# These tests require BFCache, which is currently disabled by default on Desktop.
-# Keep this in sync with VirtualTestSuites.
-crbug.com/1171298 http/tests/inspector-protocol/bfcache/* [ Skip ]
-crbug.com/1171298 http/tests/devtools/bfcache/* [ Skip ]
-crbug.com/1171298 virtual/bfcache/http/tests/inspector-protocol/bfcache/* [ Pass ]
-crbug.com/1171298 virtual/bfcache/http/tests/devtools/bfcache/* [ Pass ]
-
 # These tests require LazyEmbed, which is currently disabled by default.
 crbug.com/1247131 virtual/automatic-lazy-frame-loading/wpt_internal/lazyembed/* [ Pass ]
 crbug.com/1247131 wpt_internal/lazyembed/* [ Skip ]
@@ -1607,11 +1600,8 @@
 
 ### Tests failing with LayoutNGTableFragmentation enabled:
 crbug.com/1078927 virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/balance-table-with-border-spacing.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/balance-table-with-fractional-height-row.html [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/multicol-table-cell-height-002.xht [ Failure ]
 crbug.com/1078927 virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/multicol-table-cell-vertical-align-001.xht [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/table-cell-content-change-000.html [ Failure ]
-crbug.com/1078927 virtual/layout_ng_table_frag/external/wpt/css/css-multicol/table/table-cell-content-change-001.html [ Failure ]
 
 ### With LayoutNGPrinting enabled:
 
@@ -3728,7 +3718,6 @@
 crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-ondatachannel.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-close.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11-arm64 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCDataChannel-close.html [ Skip Timeout ]
-crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCPeerConnection-perfect-negotiation.https.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/promises-call.html [ Timeout ]
 crbug.com/626703 [ Mac11 ] virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-createDataChannel.html [ Skip Timeout ]
 crbug.com/626703 [ Mac11 ] external/wpt/webrtc/RTCPeerConnection-addIceCandidate-connectionSetup.html [ Skip Timeout ]
@@ -6676,7 +6665,7 @@
 crbug.com/1185121 fast/scroll-snap/animate-fling-to-snap-points-1.html [ Failure Pass ]
 crbug.com/1176162 http/tests/devtools/screen-orientation-override.js [ Failure Pass ]
 crbug.com/1215949 external/wpt/pointerevents/pointerevent_iframe-touch-action-none_touch.html [ Pass Timeout ]
-crbug.com/1216139 virtual/bfcache/http/tests/devtools/bfcache/bfcache-elements-update.js [ Failure Pass ]
+crbug.com/1216139 http/tests/devtools/bfcache/bfcache-elements-update.js [ Failure Pass ]
 
 # Sheriff 2021-06-10
 crbug.com/1177996 [ Mac10.15 ] storage/websql/database-lock-after-reload.html [ Failure Pass ]
@@ -6773,6 +6762,17 @@
 crbug.com/1220114 external/wpt/webrtc-encoded-transform/RTCPeerConnection-insertable-streams-simulcast.https.html [ Failure Pass Timeout ]
 crbug.com/1228959 [ Linux ] virtual/scroll-unification/fast/scroll-snap/snaps-after-wheel-scrolling-single-tick.html [ Failure Pass ]
 
+# Temporarily disabled to unblock crrev.com/c/3345001
+crbug.com/1270316 http/tests/inspector-protocol/service-worker/service-worker-fetch-async-stacks.js [ Skip ]
+crbug.com/1270316 http/tests/inspector-protocol/target/target-auction-worklet.js [ Skip ]
+crbug.com/1270316 inspector-protocol/debugger/async-stacks-for-load-and-error-events.js [ Skip ]
+crbug.com/1270316 inspector-protocol/debugger/domdebugger-webgl-breakpoint.js [ Skip ]
+crbug.com/1270316 inspector-protocol/debugger/message-channel-async-stack.js [ Skip ]
+crbug.com/1270316 inspector-protocol/debugger/wasm-compiled-streaming-breaks.js [ Skip ]
+crbug.com/1270316 inspector-protocol/worker/worker-constructor-async-stacks.js [ Skip ]
+crbug.com/1270316 inspector-protocol/worker/worker-on-message-async-stacks.js [ Skip ]
+crbug.com/1270316 inspector-protocol/worker/worker-post-message-async-stacks.js [ Skip ]
+
 # A number of http/tests/inspector-protocol/network tests are flaky.
 crbug.com/1228246 http/tests/inspector-protocol/network/disable-interception-midway.js [ Crash Failure Pass Skip Timeout ]
 crbug.com/1228246 http/tests/inspector-protocol/network/request-interception-frame-id.js [ Crash Failure Pass Skip Timeout ]
@@ -7505,7 +7505,6 @@
 crbug.com/1286944 [ Mac10.15 ] virtual/off-main-thread-css-paint/external/wpt/css/css-paint-api/registered-property-value-014.https.html [ Failure Pass ]
 
 # Sheriff 2022-01-17
-crbug.com/1287928 external/wpt/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare.https.html [ Skip ]
 crbug.com/1233938 http/tests/notifications/click-shared-worker.html [ Pass Timeout ]
 # Temporarily disable test to allow fixing of devtools path escaping
 crbug.com/1094436 http/tests/devtools/overrides/project-added-with-existing-files-bind.js [ Failure Skip Timeout ]
@@ -7561,14 +7560,11 @@
 # Sheriff 2022-01-27 Flaky test
 crbug.com/1291488 virtual/shared_array_buffer_on_desktop/fast/peerconnection/RTCPeerConnection-addMultipleTracks.html [ Skip ]
 
-# Sheriff 2022-01-28
-crbug.com/1287563 [ Linux ] external/wpt/webrtc/RTCPeerConnection-perfect-negotiation-stress-glare-linear.https.html [ Crash Failure Pass ]
-
 # Now flaky due to a timing issue.
 crbug.com/1292296 http/tests/inspector-protocol/accessibility/accessibility-getRootNode.js [ Failure Pass ]
 
 # Flaky due to timing issue.
-crbug.com/1293515 wpt_internal/resource-timing/initiator-type-early-hints-preload.h2.window.html [ Pass Failure ]
+crbug.com/1293515 wpt_internal/resource-timing/initiator-type-early-hints-preload.h2.window.html [ Failure Pass ]
 
 # Sheriff 2022-02-01
 # Flaky test on Mac
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index b951a67c..d45aabb0e 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -783,12 +783,6 @@
     "args": [ "--enable-features=BackForwardCacheABExperimentControl" ]
   },
   {
-    "prefix": "bfcache",
-    "bases": [ "http/tests/inspector-protocol/bfcache",
-               "http/tests/devtools/bfcache" ],
-    "args": [ "--enable-features=BackForwardCache" ]
-  },
-  {
     "prefix": "interest-cohort-api-origin-trial",
     "bases": [ "http/tests/origin_trials/webexposed/interest-cohort-origin-trial-interfaces.html" ],
     "args": [ "--enable-features=InterestCohortAPIOriginTrial" ]
diff --git a/third_party/blink/web_tests/external/Version b/third_party/blink/web_tests/external/Version
index 1a13d119..1a505b9 100644
--- a/third_party/blink/web_tests/external/Version
+++ b/third_party/blink/web_tests/external/Version
@@ -1 +1 @@
-Version: 0598a5629ea4f8d408e6df0fdca7eab23cc1fbf0
+Version: 598790f0cfcbfdbbbdbd1c5636310c4890c8f7e7
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 84b6027..0fa87a5 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -87,6 +87,13 @@
        {}
       ]
      ],
+     "displaylocked-serialize.html": [
+      "76784291b441566da93071ed92b959829a59c846",
+      [
+       null,
+       {}
+      ]
+     ],
      "in-page-link-with-aria-hidden.html": [
       "237707ba074c6943a3922c8a49fa81acb173a566",
       [
@@ -1341,6 +1348,13 @@
        {}
       ]
      ],
+     "remove-spanner-beside-spanner-in-inline-crash.html": [
+      "969d54a9b6d82e7ad460d3a37de02bc047e9bc57",
+      [
+       null,
+       {}
+      ]
+     ],
      "spanning-legend-000-crash.html": [
       "407697e8433e87835d9d5101825302d3a360e53d",
       [
@@ -78644,6 +78658,32 @@
        {}
       ]
      ],
+     "break-between-avoid-009.html": [
+      "d2e84f210a5d1030588c9da5d45e467c934893e8",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
+     "break-between-force-000.html": [
+      "9bccf7395e5a83f52bd743cb6021577a85833b5e",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square.xht",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "break-float-dynamic-001.html": [
       "f849b148fb6f9253c6f32dda72116a8066c287f8",
       [
@@ -82172,6 +82212,97 @@
       ]
      ],
      "table": {
+      "break-before-second-row.html": [
+       "3a8d307aa504e106b0756f03e0a9e4e79ab5e922",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "break-inside-cell-000.html": [
+       "2982eed1e86be6f24e29ee69a0375f9cd2452334",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "break-inside-cell-001.html": [
+       "cd55f46889976c06d813753ad7f75c78c42399da",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "section-with-overflow-000.html": [
+       "909052765b6cc616389f52cb4e21857caeab38fd",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "section-with-overflow-001.html": [
+       "665f5247ad2012bc96c44e1534339c3bfdea1446",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "section-with-overflow-002.html": [
+       "c6c63abe0e4b75de709f869f747ae67f2467e22e",
+       [
+        null,
+        [
+         [
+          "/css/reference/ref-filled-green-100px-square.xht",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "sections-and-captions-mixed-order.html": [
+       "67d20e3a464fb64baabb12bdac431fa71044bab3",
+       [
+        null,
+        [
+         [
+          "/css/css-break/table/sections-and-captions-mixed-order-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "table-caption-and-cells-fixed-width.html": [
        "11984c3ea3f74e4506aaa41162451d2be72229b2",
        [
@@ -233667,10 +233798,6 @@
      "aa5ff90d486e8bc15134b797be7b40928466e133",
      []
     ],
-    "access-control-expose-headers-parsing.window-expected.txt": [
-     "0fe27f2fdca8ae7eeb0f92e9bef2717a6d8ac1d1",
-     []
-    ],
     "resources": {
      ".gitignore": [
       "7b987d03655fd023ff65cbe22c1dce3dca9737ac",
@@ -243487,6 +243614,10 @@
       []
      ],
      "table": {
+      "sections-and-captions-mixed-order-ref.html": [
+       "b3265c92c6a47f8bbcb86312a85734a0d16a5075",
+       []
+      ],
       "table-caption-and-cells-fixed-width-ref.html": [
        "c41a4a515a829fcea236c3d242c0c629d312a307",
        []
@@ -263292,10 +263423,6 @@
       "28b00184c2ef5f33f6a2e8927233f6f7f42b1973",
       []
      ],
-     "scroll-target-margin-005-expected.txt": [
-      "f8f1c989e7dcd039882589794407aefc73f320f2",
-      []
-     ],
      "selection-target-expected.txt": [
       "fdb8d675d45efd056f5721fecadc205bc12e320b",
       []
@@ -290863,7 +290990,7 @@
         []
        ],
        "test-render-blocking.js": [
-        "3cd3b7d39045ff8c81ef599bdff46324c2dec5a9",
+        "bc87aef65023971510b2ffa5460b44bebaeaf1a1",
         []
        ]
       }
@@ -296596,14 +296723,6 @@
         "2b43c54e2f2e885b8d41e9d39b90be2fe8e1f8c6",
         []
        ],
-       "iframe_sandbox_allow_top_navigation-1-expected.txt": [
-        "17ffcf3969e3f15a3050a964e155fe287abfe5b2",
-        []
-       ],
-       "iframe_sandbox_allow_top_navigation-3-expected.txt": [
-        "c7156339bfb3989c1b02366d0bbcee8b20fb076d",
-        []
-       ],
        "iframe_sandbox_popups_helper-1.html": [
         "6b120f15d0d7e6cd2fe6a2753ff4592b05056034",
         []
@@ -296672,7 +296791,7 @@
          []
         ],
         "iframe-that-performs-top-navigation-on-popup.html": [
-         "0468278424f7e166753746013ec4cdf31978b0b2",
+         "9b9eae8a7234471aaa57b8c99735018ab008734e",
          []
         ],
         "iframe-that-performs-top-navigation-without-user-gesture-failed.html": [
@@ -296683,6 +296802,10 @@
          "c855ca3bab214e38945c3e23e7281ddcde7b8f2d",
          []
         ],
+        "iframe-that-send-message-to-the-opener.html": [
+         "0dc3b1122a9e03b190bae393d23c4e80945ad0aa",
+         []
+        ],
         "iframe-that-tries-to-navigate-parent-and-sends-result-to-grandparent.html": [
          "4b8930de42ff5455a17185afc9d7923025a066a7",
          []
@@ -314946,6 +315069,10 @@
       "77dca75a46c79940415200891d71d413489db7bd",
       []
      ],
+     "global-expected.txt": [
+      "8fe1616d6f418193104188a79187815c3f64ac36",
+      []
+     ],
      "patched-global.any-expected.txt": [
       "70f78cb71882cbccb0724a543a6bd79ae7a09066",
       []
@@ -318702,6 +318829,20 @@
      ]
     },
     "the-audio-api": {
+     "processing-model": {
+      "cycle-without-delay-expected.txt": [
+       "a46ff8fb07babec9521c6880bd92f6633c8ee630",
+       []
+      ],
+      "delay-time-clamping-expected.txt": [
+       "6ad8fe5179f6f7372ee9c9ffa3cea5106976b3b1",
+       []
+      ],
+      "feedback-delay-time-expected.txt": [
+       "6ad8fe5179f6f7372ee9c9ffa3cea5106976b3b1",
+       []
+      ]
+     },
      "the-audiobuffer-interface": {
       "acquire-the-content-expected.txt": [
        "74faed557c78367661d2024d85dc5ec4b5c36c85",
@@ -318709,6 +318850,10 @@
       ]
      },
      "the-audiobuffersourcenode-interface": {
+      "active-processing.https-expected.txt": [
+       "432473d71dcaedf9dbf45d583b5e9f249dd3d4e8",
+       []
+      ],
       "resources": {
        "audiobuffersource-multi-channels-expected.wav": [
         "ab9d5fe5a9dbd736a079f0cfd7966d5e064ed7ef",
@@ -318878,6 +319023,18 @@
        ]
       }
      },
+     "the-channelmergernode-interface": {
+      "active-processing.https-expected.txt": [
+       "4961c5ce8fcd295f71132e4eece807195d4a86f6",
+       []
+      ]
+     },
+     "the-convolvernode-interface": {
+      "active-processing.https-expected.txt": [
+       "876a03082e20290216afe5fbb562b36d16ca65e4",
+       []
+      ]
+     },
      "the-delaynode-interface": {
       "delaynode-channel-count-1-expected.txt": [
        "0bc5ce3b8dd6612b7af44007276819c9b1276e91",
@@ -388576,7 +388733,7 @@
    "dom": {
     "abort": {
      "AbortSignal.any.js": [
-      "1d7d7678eb1a6e27adabbe0e31afe574a31bb0ea",
+      "3bbdc11a92f90d4e2d4dc8a57ca5774b43dac556",
       [
        "dom/abort/AbortSignal.any.html",
        {}
@@ -388586,6 +388743,13 @@
        {}
       ]
      ],
+     "abort-signal-timeout.html": [
+      "2a9c13d61434b43c5c47e426c3f6df94fbb67370",
+      [
+       null,
+       {}
+      ]
+     ],
      "event.any.js": [
       "34af8ee5c560ae23aea4bb61ecf7420049fa411e",
       [
@@ -447464,6 +447628,24 @@
         null,
         {}
        ]
+      ],
+      "render-blocked-apis-by-preload-link.tentative.html": [
+       "cbc7a9a5104db4f6289694f2f3208599b269a868",
+       [
+        null,
+        {
+         "testdriver": true
+        }
+       ]
+      ],
+      "render-blocked-apis-by-stylesheet-link.tentative.html": [
+       "1be31b031d0c995d0a869406bc448f75c4adffae",
+       [
+        null,
+        {
+         "testdriver": true
+        }
+       ]
       ]
      },
      "self-origin.any.js": [
@@ -507887,6 +508069,13 @@
        }
       ]
      ],
+     "global.html": [
+      "08665d318eac889dfba85abd0efeb0a3168913df",
+      [
+       null,
+       {}
+      ]
+     ],
      "patched-global.any.js": [
       "576a39f6777e2b4970eaec74b88e6ee7f82d3b70",
       [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/break-before-second-row.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/break-before-second-row.html
new file mode 100644
index 0000000..3a8d307a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/break-before-second-row.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#unforced-breaks">
+<link rel="help" href="https://www.w3.org/TR/CSS22/tables.html#table-display">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; column-gap:0; column-fill:auto; line-height:20px; width:100px; height:100px; background:green;">
+  <div style="height:40px;"></div>
+  <div style="position:relative; z-index:-1; display:table; border-spacing:0 20px; width:100%; background:red;">
+    <div style="display:table-row;">
+      <br>
+    </div>
+    <div style="display:table-row;">
+      <div style="height:80px; background:red;">
+        <br>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/break-inside-cell-000.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/break-inside-cell-000.html
new file mode 100644
index 0000000..2982eed
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/break-inside-cell-000.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#unforced-breaks">
+<link rel="help" href="https://www.w3.org/TR/CSS22/tables.html#table-display">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; column-gap:0; column-fill:auto; line-height:20px; width:100px; height:100px; background:red;">
+  <div style="height:40px; background:green;"></div>
+  <div style="display:table; width:100%; background:red;">
+    <div style="display:table-cell; vertical-align:top; background:green;">
+      <br>
+    </div>
+    <div style="display:table-cell; vertical-align:top; background:green;">
+      <br>
+      <br>
+      <br>
+      <br>
+      <br>
+      <br>
+      <br>
+      <br>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/break-inside-cell-001.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/break-inside-cell-001.html
new file mode 100644
index 0000000..cd55f468
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/break-inside-cell-001.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#unforced-breaks">
+<link rel="help" href="https://www.w3.org/TR/CSS22/tables.html#table-display">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; column-gap:0; column-fill:auto; line-height:20px; width:100px; height:100px; background:red;">
+  <div style="display:table; width:100%; background:red;">
+    <div style="display:table-row;">
+      <div style="display:table-cell; vertical-align:top; background:green;">
+        <br>
+        <br>
+      </div>
+      <div style="display:table-cell; vertical-align:top; background:green;"></div>
+    </div>
+    <div style="display:table-row;">
+      <div style="display:table-cell; vertical-align:top; background:green;">
+        <br>
+      </div>
+      <div style="display:table-cell; vertical-align:top; background:green;">
+        <br>
+        <br>
+        <br>
+        <br>
+        <br>
+        <br>
+        <br>
+        <br>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/section-with-overflow-000.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/section-with-overflow-000.html
new file mode 100644
index 0000000..9090527
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/section-with-overflow-000.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#parallel-flows">
+<style>
+  .halfsquare {
+    margin: -10px; /* Cover the border-spacing. */
+    width: 50px;
+    height: 100px;
+    background: green;
+  }
+</style>
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; column-gap:0; column-fill:auto; width:100px; height:200px;">
+  <div style="display:table; border-spacing:10px; background:red;">
+    <div style="display:table-cell; vertical-align:top;">
+      <div style="height:50px;">
+        <div class="halfsquare"></div>
+        <div style="height:120px;"></div>
+        <div class="halfsquare" style="position:relative; z-index:-1;"></div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/section-with-overflow-001.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/section-with-overflow-001.html
new file mode 100644
index 0000000..665f524
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/section-with-overflow-001.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#parallel-flows">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; column-gap:0; column-fill:auto; width:100px; height:100px; background:red;">
+  <div style="display:table; width:100%;">
+    <div style="display:table-footer-group;">
+      <div style="display:table-cell; vertical-align:top;">
+        <div style="height:30px; background:green;"></div>
+      </div>
+    </div>
+    <div style="display:table-row-group;">
+      <div style="display:table-cell; vertical-align:top;">
+        <div style="height:40px; background:green;">
+          <div style="height:70px;"></div>
+          <div style="height:130px; background:green;"></div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/section-with-overflow-002.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/section-with-overflow-002.html
new file mode 100644
index 0000000..c6c63abe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/section-with-overflow-002.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#parallel-flows">
+<link rel="help" href="https://www.w3.org/TR/CSS22/tables.html#table-display">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; column-gap:0; column-fill:auto; width:100px; height:100px; background:red;">
+  <div style="display:table; width:100%;">
+    <div style="display:table-caption; caption-side:bottom;">
+      <div style="width:25px; height:40px; background:green;">
+      </div>
+    </div>
+    <div style="display:table-footer-group;">
+      <div style="display:table-cell; vertical-align:top;">
+        <div style="height:240px;"></div>
+      </div>
+    </div>
+    <div style="display:table-caption;">
+      <div style="width:25px; height:40px; background:green;">
+        <div style="height:220px;"></div>
+        <div style="height:60px; background:green;"></div>
+      </div>
+    </div>
+    <div style="display:table-row-group;">
+      <div style="display:table-cell; vertical-align:top;">
+        <div style="height:40px; background:green;">
+          <div style="height:40px;"></div>
+          <div style="height:100px; background:green;"></div>
+        </div>
+      </div>
+    </div>
+    <div style="display:table-header-group;">
+      <div style="display:table-cell; vertical-align:top;">
+        <div style="height:40px; background:green;">
+          <div style="height:240px;"></div>
+          <div style="height:80px; background:green;"></div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/sections-and-captions-mixed-order-ref.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/sections-and-captions-mixed-order-ref.html
new file mode 100644
index 0000000..b3265c92
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/sections-and-captions-mixed-order-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<style>
+  .fakecolumn {
+    float: left;
+    width: 50px;
+    height: 100px;
+    background: green;
+  }
+</style>
+<p>There should be four columns with the numbers from 1 to 20 in ascending
+  order. No red should be seen.</p>
+<div style="width:200px; line-height:20px;">
+  <div class="fakecolumn">
+    1<br>
+    2<br>
+    3<br>
+    4<br>
+    5<br>
+  </div>
+  <div class="fakecolumn">
+    6<br>
+    7<br>
+    8<br>
+    9<br>
+    10<br>
+  </div>
+  <div class="fakecolumn">
+    11<br>
+    12<br>
+    13<br>
+    14<br>
+    15<br>
+  </div>
+  <div class="fakecolumn">
+    16<br>
+    17<br>
+    18<br>
+    19<br>
+    20<br>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/table/sections-and-captions-mixed-order.html b/third_party/blink/web_tests/external/wpt/css/css-break/table/sections-and-captions-mixed-order.html
new file mode 100644
index 0000000..67d20e3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/table/sections-and-captions-mixed-order.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/css-break-3/#parallel-flows">
+<link rel="help" href="https://www.w3.org/TR/CSS22/tables.html#table-display">
+<style>
+  #table > div {
+    background: green;
+  }
+</style>
+<link rel="match" href="sections-and-captions-mixed-order-ref.html">
+<p>There should be four columns with the numbers from 1 to 20 in ascending
+  order. No red should be seen.</p>
+<div style="columns:4; column-gap:0; column-fill:auto; width:200px; line-height:20px; height:100px; orphans:1; widows:1; background:red;">
+  <div id="table" style="display:table; width:100%;">
+    <div style="display:table-caption; caption-side:bottom;">14</div>
+    <div style="display:table-caption;">1</div>
+    <div style="display:table-footer-group;">8<br>9<br>10<br>11<br>12<br>13</div>
+    <div style="display:table-row-group;">4</div>
+    <div style="display:table-row-group;">5<br>6</div>
+    <div style="display:table-footer-group;">7</div>
+    <div style="display:table-header-group;">3</div>
+    <div style="display:table-caption; caption-side:bottom;">15<br>16<br>17<br>18<br>19<br>20</div>
+    <div style="display:table-caption;">2</div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/streams/readable-streams/global-expected.txt b/third_party/blink/web_tests/external/wpt/streams/readable-streams/global-expected.txt
new file mode 100644
index 0000000..8fe1616d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/streams/readable-streams/global-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+PASS Stream objects created in expected globals
+FAIL Cancel promise is created in same global as stream assert_equals: Cancel promise comes from the same global as the stream expected true but got false
+PASS Closed Promise in correct global
+PASS Reader objects in correct global
+PASS Desired size can be grafted from one prototype to another
+PASS Closing errored stream throws object in appropriate global
+PASS Can enqueue chunks from multiple globals
+PASS Correct errors and globals for closed streams
+PASS Tee Branches in correct global
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/streams/readable-streams/global.html b/third_party/blink/web_tests/external/wpt/streams/readable-streams/global.html
new file mode 100644
index 0000000..08665d31
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/streams/readable-streams/global.html
@@ -0,0 +1,162 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Ensure Stream objects are created in expected globals. </title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<body></body>
+<script>
+// These tests are loosely derived from Gecko's readable-stream-globals.js,
+// which is a test case designed around the JS Streams implementation.
+//
+// Unlike in JS Streams, where function calls switch realms and change
+// the resulting global of the resulting objects, in WebIDL streams,
+// the global of an object is (currently underspecified, but) intended
+// to be the "Relevant Global" of the 'this' object.
+//
+// See:
+// https://html.spec.whatwg.org/multipage/webappapis.html#relevant
+// https://github.com/whatwg/streams/issues/1213
+"use strict"
+
+const iframe = document.createElement("iframe")
+document.body.append(iframe)
+
+const otherGlobal = iframe.contentWindow;
+const OtherReadableStream = otherGlobal.ReadableStream
+const OtherReadableStreamDefaultReader = otherGlobal.ReadableStreamDefaultReader;
+const OtherReadableStreamDefaultController = otherGlobal.ReadableStreamDefaultController;
+
+promise_test(async () => {
+
+    // Controllers
+    let controller;
+    let otherController;
+
+    // Get Stream Prototypes and controllers.
+    let streamController;
+    let stream = new ReadableStream({start(c) { streamController = c; }});
+
+    const callReaderThisGlobal = OtherReadableStream.prototype.getReader.call(stream);
+    const newReaderOtherGlobal = new OtherReadableStreamDefaultReader(new ReadableStream());
+
+    // Relevant Global Checking.
+    assert_equals(callReaderThisGlobal instanceof ReadableStreamDefaultReader, true, "reader was created in this global (.call)");
+    assert_equals(newReaderOtherGlobal instanceof ReadableStreamDefaultReader, false, "reader was created in other global (new)");
+
+    assert_equals(callReaderThisGlobal instanceof OtherReadableStreamDefaultReader, false, "reader isn't coming from other global (.call)" );
+    assert_equals(newReaderOtherGlobal instanceof OtherReadableStreamDefaultReader, true, "reader isn't coming from other global (new)");
+
+    assert_equals(otherController instanceof ReadableStreamDefaultController, false, "otherController should come from other gloal")
+
+
+    const request = callReaderThisGlobal.read();
+    assert_equals(request instanceof Promise, true, "Promise comes from this global");
+
+    streamController.close();
+    const requestResult = await request;
+    assert_equals(requestResult instanceof Object, true, "returned object comes from this global");
+}, "Stream objects created in expected globals")
+
+promise_test(async () => {
+    const stream = new ReadableStream();
+    const otherReader = new OtherReadableStreamDefaultReader(stream);
+    const cancelPromise = ReadableStreamDefaultReader.prototype.cancel.call(otherReader);
+    assert_equals(cancelPromise instanceof Promise, true, "Cancel promise comes from the same global as the stream");
+    assert_equals(await cancelPromise, undefined, "Cancel promise resolves to undefined");
+}, "Cancel promise is created in same global as stream")
+
+// Refresh the streams and controllers.
+function getFreshInstances() {
+    let controller;
+    let otherController;
+    let stream = new ReadableStream({
+        start(c) {
+            controller = c;
+        }
+    });
+
+    new OtherReadableStream({
+        start(c) {
+            otherController = c;
+        }
+    });
+
+    return {stream, controller, otherController}
+}
+
+
+promise_test(async () => {
+    // Test closed promise on reader from another global (connected to a this-global stream)
+    const {stream, controller, otherController} = getFreshInstances();
+
+    const otherReader = new OtherReadableStreamDefaultReader(stream);
+    const closedPromise = otherReader.closed;
+    assert_equals(closedPromise instanceof otherGlobal.Promise, true, "Closed promise in other global.");
+}, "Closed Promise in correct global");
+
+promise_test(async () => {
+    const {stream, controller, otherController} = getFreshInstances();
+
+    const otherReader = OtherReadableStream.prototype.getReader.call(stream);
+    assert_equals(otherReader instanceof ReadableStreamDefaultReader, true, "Reader comes from this global")
+    const request = otherReader.read();
+    assert_equals(request instanceof Promise, true, "Promise still comes from stream's realm (this realm)");
+    otherController.close.call(controller);
+    assert_equals((await request) instanceof otherGlobal.Object, true, "Object comes from other realm");
+}, "Reader objects in correct global");
+
+
+promise_test(async () => {
+    const {stream, controller, otherController} = getFreshInstances();
+    assert_equals(controller.desiredSize, 1, "Desired size is expected");
+    Object.defineProperty(controller, "desiredSize",
+        Object.getOwnPropertyDescriptor(OtherReadableStreamDefaultController.prototype, "desiredSize"));
+    assert_equals(controller.desiredSize, 1, "Grafting getter from other prototype still returns desired size");
+}, "Desired size can be grafted from one prototype to another");
+
+promise_test(async () => {
+    const {stream, controller, otherController} = getFreshInstances();
+
+    // Make sure the controller close method returns the correct TypeError
+    const enqueuedError = { name: "enqueuedError" };
+    controller.error(enqueuedError);
+
+    assert_throws_js(TypeError, () => controller.close(),  "Current Global controller");
+    assert_throws_js(otherGlobal.TypeError, () => otherController.close.call(controller),  "Other global controller");
+}, "Closing errored stream throws object in appropriate global")
+
+promise_test(async () => {
+    const {otherController} = getFreshInstances();
+    // We can enqueue chunks from multiple globals
+    const chunk = { name: "chunk" };
+
+    let controller;
+    const stream = new ReadableStream({ start(c) { controller = c; } }, { size() {return 1} });
+    otherController.enqueue.call(controller, chunk);
+    otherController.enqueue.call(controller, new otherGlobal.Uint8Array(10));
+    controller.enqueue(new otherGlobal.Uint8Array(10));
+}, "Can enqueue chunks from multiple globals")
+
+promise_test(async () => {
+    const {stream, controller, otherController} = getFreshInstances();
+    const chunk = { name: "chunk" };
+
+    // We get the correct type errors out of a closed stream.
+    controller.close();
+    assert_throws_js(TypeError, () => controller.enqueue(new otherGlobal.Uint8Array(10)));
+    assert_throws_js(otherGlobal.TypeError, () => otherController.enqueue.call(controller, chunk));
+    assert_throws_js(otherGlobal.TypeError, () => otherController.enqueue.call(controller, new otherGlobal.Uint8Array(10)));
+}, "Correct errors and globals for closed streams");
+
+
+promise_test(async () => {
+    const {stream, controller, otherController} = getFreshInstances();
+    // Branches out of tee are in the correct global
+
+    const [branch1, branch2] = otherGlobal.ReadableStream.prototype.tee.call(stream);
+    assert_equals(branch1 instanceof ReadableStream, true, "Branch created in this global (as stream is in this global)");
+    assert_equals(branch2 instanceof ReadableStream, true, "Branch created in this global (as stream is in this global)");
+}, "Tee Branches in correct global");
+</script>
diff --git a/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/streams/readable-streams/global-expected.txt b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/streams/readable-streams/global-expected.txt
new file mode 100644
index 0000000..8fe1616d
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/highdpi/external/wpt/streams/readable-streams/global-expected.txt
@@ -0,0 +1,12 @@
+This is a testharness.js-based test.
+PASS Stream objects created in expected globals
+FAIL Cancel promise is created in same global as stream assert_equals: Cancel promise comes from the same global as the stream expected true but got false
+PASS Closed Promise in correct global
+PASS Reader objects in correct global
+PASS Desired size can be grafted from one prototype to another
+PASS Closing errored stream throws object in appropriate global
+PASS Can enqueue chunks from multiple globals
+PASS Correct errors and globals for closed streams
+PASS Tee Branches in correct global
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/bfcache/README.md b/third_party/blink/web_tests/virtual/bfcache/README.md
deleted file mode 100644
index 4068c98..0000000
--- a/third_party/blink/web_tests/virtual/bfcache/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Tests run with back-forward cache enabled (`--enable-features=BackForwardCache`).
diff --git a/third_party/blink/web_tests/virtual/bfcache/http/tests/bfcache/report-back-forward-cache-status-blocklisted-features-expected.txt b/third_party/blink/web_tests/virtual/bfcache/http/tests/bfcache/report-back-forward-cache-status-blocklisted-features-expected.txt
deleted file mode 100644
index e12fecb41..0000000
--- a/third_party/blink/web_tests/virtual/bfcache/http/tests/bfcache/report-back-forward-cache-status-blocklisted-features-expected.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-Test that back/forward navigations report the bfcache status
-{
-    frameId : <string>
-    loaderId : <string>
-    notRestoredExplanations : [
-        [0] : {
-            reason : IdleManager
-            type : PageSupportNeeded
-        }
-    ]
-}
-{
-    frameId : <string>
-    loaderId : <string>
-    notRestoredExplanations : [
-        [0] : {
-            reason : HTTPStatusNotOK
-            type : Circumstantial
-        }
-    ]
-}
-
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-contain/crashtests/contain-nested-crash-002.html b/third_party/blink/web_tests/wpt_internal/css/css-contain/crashtests/contain-nested-crash-002.html
new file mode 100644
index 0000000..2cbb648
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-contain/crashtests/contain-nested-crash-002.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<style>
+html {
+  -webkit-user-modify: read-write-plaintext-only
+}
+
+* {
+  contain: strict;
+}
+</style>
+<script>
+function jsfuzzer() {
+  htmlvar00014.appendChild(htmlvar00017);
+}
+</script>
+<body onload=jsfuzzer()>
+  <button>
+    <span id="htmlvar00014"></span>
+  </button>
+  <span></span>
+  <span>
+    <button id="htmlvar00017"></button>
+  </span>
+</body>
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index 31d0c2cb..1e577b5 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -712,6 +712,8 @@
       'WebRTC Chromium FYI Mac Builder (dbg)': 'debug_bot',
       'WebRTC Chromium FYI Win Builder': 'release_bot_x86_minimal_symbols_no_com_init_hooks_with_codecs',
       'WebRTC Chromium FYI Win Builder (dbg)': 'debug_bot_x86_no_com_init_hooks_with_codecs',
+      'WebRTC Chromium FYI ios-device': 'ios_device_release_compile_only',
+      'WebRTC Chromium FYI ios-simulator': 'ios_simulator_debug_static_bot_xctest',
     },
 
     'chromium.win': {
diff --git a/tools/mb/mb_config_expectations/chromium.webrtc.fyi.json b/tools/mb/mb_config_expectations/chromium.webrtc.fyi.json
index 31713fd09..ee1693b 100644
--- a/tools/mb/mb_config_expectations/chromium.webrtc.fyi.json
+++ b/tools/mb/mb_config_expectations/chromium.webrtc.fyi.json
@@ -95,5 +95,33 @@
       "target_cpu": "x86",
       "use_goma": true
     }
+  },
+  "WebRTC Chromium FYI ios-device": {
+    "gn_args": {
+      "dcheck_always_on": false,
+      "ios_code_signing_identity_description": "Apple Development",
+      "ios_enable_code_signing": false,
+      "ios_set_attributes_for_xcode_project_generation": false,
+      "is_component_build": false,
+      "is_debug": false,
+      "symbol_level": 0,
+      "target_cpu": "arm64",
+      "target_environment": "device",
+      "target_os": "ios",
+      "use_goma": true
+    }
+  },
+  "WebRTC Chromium FYI ios-simulator": {
+    "gn_args": {
+      "enable_run_ios_unittests_with_xctest": true,
+      "ios_set_attributes_for_xcode_project_generation": false,
+      "is_component_build": false,
+      "is_debug": true,
+      "symbol_level": 1,
+      "target_cpu": "x64",
+      "target_environment": "simulator",
+      "target_os": "ios",
+      "use_goma": true
+    }
   }
 }
\ No newline at end of file
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 87710e6..07c9229 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -25686,6 +25686,70 @@
   </description>
 </action>
 
+<action name="Settings.PrivacyGuide.ChangeCookiesBlock3P">
+  <owner>harrisonsean@chromium.org</owner>
+  <owner>rainhard@chromium.org</owner>
+  <owner>chrome-friendly-settings@google.com</owner>
+  <description>
+    User changes cookies to block 3rd party in Privacy guide.
+  </description>
+</action>
+
+<action name="Settings.PrivacyGuide.ChangeCookiesBlock3PIngonito">
+  <owner>harrisonsean@chromium.org</owner>
+  <owner>rainhard@chromium.org</owner>
+  <owner>chrome-friendly-settings@google.com</owner>
+  <description>
+    User changes cookies to block 3rd party in Incognito in Privacy guide.
+  </description>
+</action>
+
+<action name="Settings.PrivacyGuide.ChangeHistorySyncOff">
+  <owner>harrisonsean@chromium.org</owner>
+  <owner>rainhard@chromium.org</owner>
+  <owner>chrome-friendly-settings@google.com</owner>
+  <description>User changes history sync to off in Privacy guide.</description>
+</action>
+
+<action name="Settings.PrivacyGuide.ChangeHistorySyncOn">
+  <owner>harrisonsean@chromium.org</owner>
+  <owner>rainhard@chromium.org</owner>
+  <owner>chrome-friendly-settings@google.com</owner>
+  <description>User changes history sync to on in Privacy guide.</description>
+</action>
+
+<action name="Settings.PrivacyGuide.ChangeMSBBOff">
+  <owner>harrisonsean@chromium.org</owner>
+  <owner>rainhard@chromium.org</owner>
+  <owner>chrome-friendly-settings@google.com</owner>
+  <description>User changes MSBB to off in Privacy guide.</description>
+</action>
+
+<action name="Settings.PrivacyGuide.ChangeMSBBOn">
+  <owner>harrisonsean@chromium.org</owner>
+  <owner>rainhard@chromium.org</owner>
+  <owner>chrome-friendly-settings@google.com</owner>
+  <description>User changes MSBB to on in Privacy guide.</description>
+</action>
+
+<action name="Settings.PrivacyGuide.ChangeSafeBrowsingEnhanced">
+  <owner>harrisonsean@chromium.org</owner>
+  <owner>rainhard@chromium.org</owner>
+  <owner>chrome-friendly-settings@google.com</owner>
+  <description>
+    User changes Safe Browsing to enhanced in Privacy guide.
+  </description>
+</action>
+
+<action name="Settings.PrivacyGuide.ChangeSafeBrowsingStandard">
+  <owner>harrisonsean@chromium.org</owner>
+  <owner>rainhard@chromium.org</owner>
+  <owner>chrome-friendly-settings@google.com</owner>
+  <description>
+    User changes Safe Browsing to standard in Privacy guide.
+  </description>
+</action>
+
 <action name="Settings.PrivacyGuide.CompletionPSClick">
   <owner>harrisonsean@chromium.org</owner>
   <owner>rainhard@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index bbbd7b1..98fc71d 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -1771,9 +1771,9 @@
 </histogram>
 
 <histogram name="Enterprise.PublicSession.ExtensionPermissions"
-    enum="ExtensionPermission3" expires_after="2022-03-01">
-  <owner>anqing@chromium.org</owner>
-  <owner>apotapchuk@chromium.org</owner>
+    enum="ExtensionPermission3" expires_after="2023-03-01">
+  <owner>bfranz@chromium.org</owner>
+  <owner>chromeos-kiosk-eng@google.com</owner>
   <summary>
     Extensions (and apps) are quite restricted in public sessions for security
     and privacy concerns - some permissions are blocked which means the
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index 211d57e3..266e8a9 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -310,7 +310,8 @@
   <owner>blundell@chromium.org</owner>
   <summary>
     Records the time duration that a check for the reputation status of a site
-    spent in getting the domain info of the site. Recorded on every check.
+    spent in getting the domain info of the site on the main thread, or 0 if the
+    domain info was obtained off of the main thread. Recorded on every check.
   </summary>
 </histogram>
 
@@ -320,8 +321,8 @@
   <owner>estark@chromium.org</owner>
   <owner>blundell@chromium.org</owner>
   <summary>
-    Records the time duration of a check for the reputation status of a site.
-    Recorded on every check.
+    Records the time duration of a check for the reputation status of a site on
+    the main thread. Recorded on every check.
   </summary>
 </histogram>
 
diff --git a/tools/perf/chrome-health-presets.yaml b/tools/perf/chrome-health-presets.yaml
index adbcc71..f91bdd3 100644
--- a/tools/perf/chrome-health-presets.yaml
+++ b/tools/perf/chrome-health-presets.yaml
@@ -7,165 +7,6 @@
 presets:
   chrome_health:
     telemetry_batch_experiment:
-      - benchmark: loading.desktop
-        configs:
-          - mac-10_12_laptop_low_end-perf
-          - mac-m1_mini_2020-perf
-          - linux-perf
-          - win-10-perf
-        stories:
-          - 24h_cold
-          - 24h_warm
-          - AirBnB_cold
-          - AirBnB_warm
-          - Aljayyash_cold
-          - Aljayyash_warm
-          - AllRecipes_cold
-          - AllRecipes_warm
-          - ArsTechnica_cold
-          - ArsTechnica_warm
-          - Baidu_cold
-          - Baidu_warm
-          - Bhaskar_cold
-          - Bhaskar_warm
-          - Chosun_cold
-          - Chosun_warm
-          - Colorado.edu_cold
-          - Colorado.edu_warm
-          - Danawa_cold
-          - Danawa_warm
-          - Daum_cold
-          - Daum_warm
-          - Donga_cold
-          - Donga_warm
-          - Economist_cold
-          - Economist_warm
-          - Elmundo_cold
-          - Elmundo_warm
-          - FC2Blog_cold
-          - FC2Blog_warm
-          - FIFA_cold
-          - FIFA_warm
-          - FarsNews_cold
-          - FarsNews_warm
-          - Flickr_cold
-          - Flickr_warm
-          - FlipKart_cold
-          - FlipKart_warm
-          - Free.fr_cold
-          - Free.fr_warm
-          - HTML5Rocks_cold
-          - HTML5Rocks_warm
-          - Haraj_cold
-          - Haraj_warm
-          - HatenaBookmark_cold
-          - HatenaBookmark_warm
-          - IGN_cold
-          - IGN_warm
-          - IMDB_cold
-          - IMDB_warm
-          - IndiaTimes_cold
-          - IndiaTimes_warm
-          - Kakaku_cold
-          - Kakaku_warm
-          - Kenh14_cold
-          - Kenh14_warm
-          - Mercadolivre_cold
-          - Mercadolivre_warm
-          - Naver_cold
-          - Naver_warm
-          - Orange_cold
-          - Orange_warm
-          - Pantip_cold
-          - Pantip_warm
-          - PremierLeague_cold
-          - PremierLeague_warm
-          - QQ_cold
-          - QQ_warm
-          - REI_cold
-          - REI_warm
-          - Ruten_cold
-          - Ruten_warm
-          - Sina_cold
-          - Sina_warm
-          - Taobao_cold
-          - Taobao_warm
-          - TheOnion_cold
-          - TheOnion_warm
-          - TheVerge_cold
-          - TheVerge_warm
-          - TicketMaster_cold
-          - TicketMaster_warm
-          - Vietnamnet_cold
-          - Vietnamnet_warm
-          - Vnexpress_cold
-          - Vnexpress_warm
-          - Yandex_cold
-          - Yandex_warm
-          - amazon.co.jp_cold
-          - amazon.co.jp_warm
-          - ja.wikipedia_cold
-          - ja.wikipedia_warm
-          - money.cnn_cold
-          - money.cnn_warm
-          - ru.wikipedia_cold
-          - ru.wikipedia_warm
-          - uol.com.br_cold
-          - uol.com.br_warm
-          - yahoo.co.jp_cold
-          - yahoo.co.jp_warm
-      - benchmark: loading.mobile
-        configs:
-          - android-pixel2-perf
-          - android-pixel4-perf
-        stories:
-          - 58Pic
-          - Amazon
-          - BOLNoticias
-          - Baidu
-          - Bradesco
-          - Dailymotion
-          - Dawn
-          - DevOpera_cold
-          - DevOpera_hot
-          - DevOpera_warm
-          - Dramaq
-          - EnquiryIndianRail
-          - Facebook
-          - FlipBoard_cold
-          - FlipBoard_hot
-          - FlipBoard_warm
-          - FlipKart_cold
-          - FlipKart_hot
-          - FlipKart_warm
-          - FranceTVInfo
-          - G1
-          - GSShop
-          - GoogleBrazil
-          - GoogleIndia
-          - GoogleIndonesia
-          - GoogleRedirectToGoogleJapan
-          - Hongkiat
-          - KapanLagi
-          - Kaskus
-          - LocalMoxie
-          - Locanto
-          - OLX
-          - QQNews
-          - SlideShare
-          - Suumo_cold
-          - Suumo_hot
-          - Suumo_warm
-          - Thairath
-          - TheStar
-          - TribunNews
-          - Twitter
-          - VoiceMemos_cold
-          - VoiceMemos_hot
-          - VoiceMemos_warm
-          - Wikipedia
-          - YahooNews
-          - Youtube
       - benchmark: speedometer2-chrome-health
         configs:
           - mac-10_12_laptop_low_end-perf
@@ -179,46 +20,84 @@
           - Speedometer2
           - Speedometer2
           - Speedometer2
-  chrome_health_limited:
+      - benchmark: rendering.desktop
+        configs:
+          - mac-10_12_laptop_low_end-perf
+          - mac-m1_mini_2020-perf
+          - linux-perf
+          - win-10-perf
+        stories:
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+      - benchmark: rendering.mobile
+        configs:
+          - android-pixel2-perf
+          - android-pixel4-perf
+        stories:
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+  speedometer2:
     telemetry_batch_experiment:
-      - benchmark: loading.desktop
-        configs:
-          - mac-10_12_laptop_low_end-perf
-          - mac-m1_mini_2020-perf
-          - linux-perf
-          - win-10-perf
-        stories:
-          - 24h_cold
-          - 24h_warm
-          - AirBnB_cold
-          - Aljayyash_warm
-          - Daum_warm
-          - Donga_cold
-          - Economist_cold
-          - FlipKart_warm
-          - QQ_cold
-          - Ruten_cold
-          - Ruten_warm
-          - Vietnamnet_cold
-          - yahoo.co.jp_cold
-          - yahoo.co.jp_warm
-      - benchmark: loading.mobile
-        configs:
-          - android-pixel2-perf
-          - android-pixel4-perf
-        stories:
-          - 58Pic
-          - Amazon
-          - BOLNoticias
-          - Baidu
-          - Bradesco
-          - Dailymotion
-          - Facebook
-          - FlipBoard_cold
-          - FlipBoard_hot
-          - LocalMoxie
-          - SlideShare
-          - Youtube
       - benchmark: speedometer2-chrome-health
         configs:
           - mac-10_12_laptop_low_end-perf
@@ -232,37 +111,81 @@
           - Speedometer2
           - Speedometer2
           - Speedometer2
-  chrome_health_summary_report:
-    batch_summary_report_spec:
-      loading.desktop:
-        metrics:
-          - name: largestContentfulPaint
-          - name: timeToFirstContentfulPaint
-          - name: overallCumulativeLayoutShift
-          - name: totalBlockingTime
-      loading.mobile:
-        metrics:
-          - name: largestContentfulPaint
-          - name: timeToFirstContentfulPaint
-          - name: overallCumulativeLayoutShift
-          - name: totalBlockingTime
-      speedometer2:
-        metrics:
-          - name: Angular2-TypeScript-TodoMVC
-          - name: AngularJS-TodoMVC
-          - name: BackboneJS-TodoMVC
-          - name: Elm-TodoMVC
-          - name: EmberJS-Debug-TodoMVC
-          - name: EmberJS-TodoMVC
-          - name: Flight-TodoMVC
-          - name: Inferno-TodoMVC
-          - name: jQuery-TodoMVC
-          - name: Preact-TodoMVC
-          - name: React-Redux-TodoMVC
-          - name: React-TodoMVC
-          - name: RunsPerMinute
-          - name: Total
-          - name: Vanilla-ES2015-Babel-Webpack-TodoMVC
-          - name: Vanilla-ES2015-TodoMVC
-          - name: VanillaJS-TodoMVC
-          - name: VueJS-TodoMVC
\ No newline at end of file
+  motionmark:
+    telemetry_batch_experiment:
+      - benchmark: rendering.desktop
+        configs:
+          - mac-laptop_low_end-perf
+          - mac-m1_mini_2020-perf
+          - linux-perf
+          - win-10-perf
+        stories:
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+      - benchmark: rendering.mobile
+        configs:
+          - android-pixel2-perf
+          - android-pixel4-perf
+        stories:
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_arcs
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_canvas_lines
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_design
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_images
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_leaves
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_multiply
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_paths
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
+          - motionmark_ramp_suits
\ No newline at end of file
diff --git a/ui/accessibility/ax_node_position.cc b/ui/accessibility/ax_node_position.cc
index e41106c..6af347c4 100644
--- a/ui/accessibility/ax_node_position.cc
+++ b/ui/accessibility/ax_node_position.cc
@@ -36,6 +36,19 @@
 
 }  // namespace testing
 
+std::string ToString(const AXPositionKind kind) {
+  static constexpr auto kKindToString =
+      base::MakeFixedFlatMap<AXPositionKind, const char*>(
+          {{AXPositionKind::NULL_POSITION, "NullPosition"},
+           {AXPositionKind::TREE_POSITION, "TreePosition"},
+           {AXPositionKind::TEXT_POSITION, "TextPosition"}});
+
+  const auto* iter = kKindToString.find(kind);
+  if (iter == std::end(kKindToString))
+    return std::string();
+  return iter->second;
+}
+
 // static
 AXNodePosition::AXPositionInstance AXNodePosition::CreatePosition(
     const AXNode& node,
diff --git a/ui/accessibility/ax_node_position.h b/ui/accessibility/ax_node_position.h
index cd2b0b07..83d92ff 100644
--- a/ui/accessibility/ax_node_position.h
+++ b/ui/accessibility/ax_node_position.h
@@ -12,6 +12,9 @@
 
 namespace ui {
 
+// Returns a human-readable representation of `AXPositionKind`.
+AX_EXPORT std::string ToString(const AXPositionKind kind);
+
 // AXNodePosition includes implementations of AXPosition methods which require
 // knowledge of the AXPosition AXNodeType (which is unknown by AXPosition).
 class AX_EXPORT AXNodePosition : public AXPosition<AXNodePosition, AXNode> {
diff --git a/ui/accessibility/ax_node_position_unittest.cc b/ui/accessibility/ax_node_position_unittest.cc
index 5848d0e..5cbda14 100644
--- a/ui/accessibility/ax_node_position_unittest.cc
+++ b/ui/accessibility/ax_node_position_unittest.cc
@@ -3448,12 +3448,12 @@
   ASSERT_NE(nullptr, inline_box2_position);
   ASSERT_TRUE(inline_box2_position->IsTextPosition());
 
-  TestPositionType test_position = root_position->LowestCommonAncestor(
+  TestPositionType test_position = root_position->LowestCommonAncestorPosition(
       *null_position.get(), ax::mojom::MoveDirection::kForward);
   EXPECT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsNullPosition());
 
-  test_position = root_position->LowestCommonAncestor(
+  test_position = root_position->LowestCommonAncestorPosition(
       *root_position.get(), ax::mojom::MoveDirection::kForward);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
@@ -3462,7 +3462,7 @@
   // be unchanged.
   EXPECT_EQ(3, test_position->child_index());
 
-  test_position = button_position->LowestCommonAncestor(
+  test_position = button_position->LowestCommonAncestorPosition(
       *text_field_position.get(), ax::mojom::MoveDirection::kForward);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
@@ -3470,7 +3470,7 @@
   // The child index should point to the button.
   EXPECT_EQ(0, test_position->child_index());
 
-  test_position = static_text2_position->LowestCommonAncestor(
+  test_position = static_text2_position->LowestCommonAncestorPosition(
       *static_text1_position.get(), ax::mojom::MoveDirection::kForward);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
@@ -3478,7 +3478,7 @@
   // The child index should point to the second static text node.
   EXPECT_EQ(2, test_position->child_index());
 
-  test_position = static_text1_position->LowestCommonAncestor(
+  test_position = static_text1_position->LowestCommonAncestorPosition(
       *text_field_position.get(), ax::mojom::MoveDirection::kForward);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTreePosition());
@@ -3486,14 +3486,14 @@
   // The child index should point to the first static text node.
   EXPECT_EQ(0, test_position->child_index());
 
-  test_position = inline_box1_position->LowestCommonAncestor(
+  test_position = inline_box1_position->LowestCommonAncestorPosition(
       *inline_box2_position.get(), ax::mojom::MoveDirection::kForward);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
   EXPECT_EQ(text_field_.id, test_position->anchor_id());
   EXPECT_EQ(0, test_position->text_offset());
 
-  test_position = inline_box2_position->LowestCommonAncestor(
+  test_position = inline_box2_position->LowestCommonAncestorPosition(
       *inline_box1_position.get(), ax::mojom::MoveDirection::kForward);
   ASSERT_NE(nullptr, test_position);
   EXPECT_TRUE(test_position->IsTextPosition());
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 5b1095c..9aec585 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -17,6 +17,7 @@
 #include <vector>
 
 #include "base/containers/contains.h"
+#include "base/containers/fixed_flat_map.h"
 #include "base/containers/stack.h"
 #include "base/i18n/break_iterator.h"
 #include "base/no_destructor.h"
@@ -381,6 +382,7 @@
     return str + " annotated_text=" + base::UTF16ToUTF8(annotated_text);
   }
 
+  AXPositionKind kind() const { return kind_; }
   AXTreeID tree_id() const { return tree_id_; }
   AXNodeID anchor_id() const { return anchor_id_; }
 
@@ -408,7 +410,6 @@
     return 0;
   }
 
-  AXPositionKind kind() const { return kind_; }
   int child_index() const { return child_index_; }
   int text_offset() const { return text_offset_; }
   ax::mojom::TextAffinity affinity() const { return affinity_; }
@@ -490,24 +491,23 @@
 
   bool IsLeafTextPosition() const { return IsTextPosition() && IsLeaf(); }
 
-  bool IsUnignoredTextListMarker() const {
-    return GetAnchorRole() == ax::mojom::Role::kListMarker &&
-           !GetAnchor()->IsIgnored() && !AnchorUnignoredChildCount();
-  }
-
   bool IsLeaf() const {
     if (IsNullPosition())
       return false;
     // Unignored text list markers expose text on their own, and all their
     // descendants are ignored. Make sure they are treated as leaves, not empty
     // containers.
-    if (IsUnignoredTextListMarker())
+    if (IsInUnignoredTextListMarker())
       return true;
     return !AnchorChildCount() || IsInEmptyObject();
   }
 
   // Returns true if this is a valid position, e.g. the child_index_ or
   // text_offset_ is within a valid range.
+  //
+  // A position is always valid at creation time, but could become invalid after
+  // a tree update. For performance reasons, we don't check for validity every
+  // time a position is used, expecting clients to use this method instead.
   bool IsValid() const {
     switch (kind_) {
       case AXPositionKind::NULL_POSITION:
@@ -630,64 +630,6 @@
     }
   }
 
-  bool AtStartOfSentence() const {
-    AXPositionInstance text_position;
-    if (!AtEndOfAnchor()) {
-      // We could get a leaf text position at the end of its anchor, where
-      // sentence start offsets would surely not be present. In such cases, we
-      // need to normalize to the start of the next leaf anchor. We avoid making
-      // this change when we are at the end of our anchor because this could
-      // effectively shift the position forward.
-      text_position = AsLeafTextPositionBeforeCharacter();
-    } else {
-      text_position = AsLeafTextPosition();
-    }
-
-    switch (text_position->kind_) {
-      case AXPositionKind::NULL_POSITION:
-        return false;
-      case AXPositionKind::TREE_POSITION:
-        NOTREACHED();
-        return false;
-      case AXPositionKind::TEXT_POSITION: {
-        const std::vector<int32_t>& sentence_starts =
-            text_position->GetAnchor()->GetIntListAttribute(
-                ax::mojom::IntListAttribute::kSentenceStarts);
-        return base::Contains(sentence_starts,
-                              int32_t{text_position->text_offset_});
-      }
-    }
-  }
-
-  bool AtEndOfSentence() const {
-    AXPositionInstance text_position;
-    if (!AtStartOfAnchor()) {
-      // We could get a leaf text position at the start of its anchor, where
-      // sentence end offsets would surely not be present. In such cases, we
-      // need to normalize to the end of the previous leaf anchor. We avoid
-      // making this change when we are at the start of our anchor because this
-      // could effectively shift the position backward.
-      text_position = AsLeafTextPositionAfterCharacter();
-    } else {
-      text_position = AsLeafTextPosition();
-    }
-
-    switch (text_position->kind_) {
-      case AXPositionKind::NULL_POSITION:
-        return false;
-      case AXPositionKind::TREE_POSITION:
-        NOTREACHED();
-        return false;
-      case AXPositionKind::TEXT_POSITION: {
-        const std::vector<int32_t>& sentence_ends =
-            text_position->GetAnchor()->GetIntListAttribute(
-                ax::mojom::IntListAttribute::kSentenceEnds);
-        return base::Contains(sentence_ends,
-                              int32_t{text_position->text_offset_});
-      }
-    }
-  }
-
   bool AtStartOfLine() const {
     AXPositionInstance text_position = AsLeafTextPosition();
     switch (text_position->kind_) {
@@ -798,6 +740,135 @@
     }
   }
 
+  AXBoundaryType GetFormatStartBoundaryType() const {
+    // Since formats are stored on text anchors, the start of a format boundary
+    // must be at the start of an anchor.
+    if (IsNullPosition() || !AtStartOfAnchor())
+      return AXBoundaryType::kNone;
+
+    // Treat the first iterable node as a format boundary.
+    if (CreatePreviousLeafTreePosition(
+            base::BindRepeating(&AbortMoveAtRootBoundary))
+            ->IsNullPosition()) {
+      return AXBoundaryType::kContentStart;
+    }
+
+    // Ignored positions cannot be format boundaries.
+    if (IsIgnored())
+      return AXBoundaryType::kNone;
+
+    // Iterate over anchors until a format boundary is found. This will return a
+    // null position upon crossing a boundary. Make sure the previous position
+    // is not on an ignored node.
+    AXPositionInstance previous_position = Clone();
+    do {
+      previous_position = previous_position->CreatePreviousLeafTreePosition(
+          base::BindRepeating(&AbortMoveAtFormatBoundary));
+    } while (previous_position->IsIgnored());
+
+    if (previous_position->IsNullPosition())
+      return AXBoundaryType::kUnitBoundary;
+
+    return AXBoundaryType::kNone;
+  }
+
+  bool AtStartOfFormat() const {
+    return GetFormatStartBoundaryType() != AXBoundaryType::kNone;
+  }
+
+  AXBoundaryType GetFormatEndBoundaryType() const {
+    // Since formats are stored on text anchors, the end of a format break must
+    // be at the end of an anchor.
+    if (IsNullPosition() || !AtEndOfAnchor())
+      return AXBoundaryType::kNone;
+
+    // Treat the last iterable node as a format boundary
+    if (CreateNextLeafTreePosition(
+            base::BindRepeating(&AbortMoveAtRootBoundary))
+            ->IsNullPosition())
+      return AXBoundaryType::kContentEnd;
+
+    // Ignored positions cannot be format boundaries.
+    if (IsIgnored())
+      return AXBoundaryType::kNone;
+
+    // Iterate over anchors until a format boundary is found. This will return a
+    // null position upon crossing a boundary. Make sure the next position is
+    // not on an ignored node.
+    AXPositionInstance next_position = Clone();
+    do {
+      next_position = next_position->CreateNextLeafTreePosition(
+          base::BindRepeating(&AbortMoveAtFormatBoundary));
+    } while (next_position->IsIgnored());
+
+    if (next_position->IsNullPosition())
+      return AXBoundaryType::kUnitBoundary;
+
+    return AXBoundaryType::kNone;
+  }
+
+  bool AtEndOfFormat() const {
+    return GetFormatEndBoundaryType() != AXBoundaryType::kNone;
+  }
+
+  bool AtStartOfSentence() const {
+    AXPositionInstance text_position;
+    if (!AtEndOfAnchor()) {
+      // We could get a leaf text position at the end of its anchor, where
+      // sentence start offsets would surely not be present. In such cases, we
+      // need to normalize to the start of the next leaf anchor. We avoid making
+      // this change when we are at the end of our anchor because this could
+      // effectively shift the position forward.
+      text_position = AsLeafTextPositionBeforeCharacter();
+    } else {
+      text_position = AsLeafTextPosition();
+    }
+
+    switch (text_position->kind_) {
+      case AXPositionKind::NULL_POSITION:
+        return false;
+      case AXPositionKind::TREE_POSITION:
+        NOTREACHED();
+        return false;
+      case AXPositionKind::TEXT_POSITION: {
+        const std::vector<int32_t>& sentence_starts =
+            text_position->GetAnchor()->GetIntListAttribute(
+                ax::mojom::IntListAttribute::kSentenceStarts);
+        return base::Contains(sentence_starts,
+                              int32_t{text_position->text_offset_});
+      }
+    }
+  }
+
+  bool AtEndOfSentence() const {
+    AXPositionInstance text_position;
+    if (!AtStartOfAnchor()) {
+      // We could get a leaf text position at the start of its anchor, where
+      // sentence end offsets would surely not be present. In such cases, we
+      // need to normalize to the end of the previous leaf anchor. We avoid
+      // making this change when we are at the start of our anchor because this
+      // could effectively shift the position backward.
+      text_position = AsLeafTextPositionAfterCharacter();
+    } else {
+      text_position = AsLeafTextPosition();
+    }
+
+    switch (text_position->kind_) {
+      case AXPositionKind::NULL_POSITION:
+        return false;
+      case AXPositionKind::TREE_POSITION:
+        NOTREACHED();
+        return false;
+      case AXPositionKind::TEXT_POSITION: {
+        const std::vector<int32_t>& sentence_ends =
+            text_position->GetAnchor()->GetIntListAttribute(
+                ax::mojom::IntListAttribute::kSentenceEnds);
+        return base::Contains(sentence_ends,
+                              int32_t{text_position->text_offset_});
+      }
+    }
+  }
+
   // `AtStartOfParagraph` is asymmetric from `AtEndOfParagraph` because line
   // breaks could be present between paragraphs. The end of the paragraph is
   // always before all such breaks, whilst the start of paragraph is always
@@ -936,6 +1007,52 @@
     }
   }
 
+  // Returns true if this position is at the start or right before content that
+  // is laid out using "display: inline-block".
+  bool AtStartOfInlineBlock() const {
+    AXPositionInstance text_position = AsLeafTextPosition();
+    switch (text_position->kind_) {
+      case AXPositionKind::NULL_POSITION:
+        return false;
+      case AXPositionKind::TREE_POSITION:
+        NOTREACHED();
+        return false;
+      case AXPositionKind::TEXT_POSITION: {
+        if (text_position->AtStartOfAnchor()) {
+          AXPositionInstance previous_position =
+              text_position->CreatePreviousLeafTreePosition();
+
+          // Check that this position is not the start of the first anchor.
+          if (!previous_position->IsNullPosition()) {
+            previous_position = text_position->CreatePreviousLeafTreePosition(
+                base::BindRepeating(&AbortMoveAtStartOfInlineBlock));
+
+            // If we get a null position here it means we have crossed an inline
+            // block's start, thus this position is located at such start.
+            if (previous_position->IsNullPosition())
+              return true;
+          }
+        }
+        if (text_position->AtEndOfAnchor()) {
+          AXPositionInstance next_position =
+              text_position->CreateNextLeafTreePosition();
+
+          // Check that this position is not the end of the last anchor.
+          if (!next_position->IsNullPosition()) {
+            next_position = text_position->CreateNextLeafTreePosition(
+                base::BindRepeating(&AbortMoveAtStartOfInlineBlock));
+
+            // If we get a null position here it means we have crossed an inline
+            // block's start, thus this position is located at such start.
+            if (next_position->IsNullPosition())
+              return true;
+          }
+        }
+        return false;
+      }
+    }
+  }
+
   // Page boundaries are only supported in certain content types, e.g. PDF
   // documents.
   bool AtStartOfPage() const {
@@ -1025,121 +1142,6 @@
     return *CreatePositionAtEndOfAXTree() == *this;
   }
 
-  AXBoundaryType GetFormatStartBoundaryType() const {
-    // Since formats are stored on text anchors, the start of a format boundary
-    // must be at the start of an anchor.
-    if (IsNullPosition() || !AtStartOfAnchor())
-      return AXBoundaryType::kNone;
-
-    // Treat the first iterable node as a format boundary.
-    if (CreatePreviousLeafTreePosition(
-            base::BindRepeating(&AbortMoveAtRootBoundary))
-            ->IsNullPosition()) {
-      return AXBoundaryType::kContentStart;
-    }
-
-    // Ignored positions cannot be format boundaries.
-    if (IsIgnored())
-      return AXBoundaryType::kNone;
-
-    // Iterate over anchors until a format boundary is found. This will return a
-    // null position upon crossing a boundary. Make sure the previous position
-    // is not on an ignored node.
-    AXPositionInstance previous_position = Clone();
-    do {
-      previous_position = previous_position->CreatePreviousLeafTreePosition(
-          base::BindRepeating(&AbortMoveAtFormatBoundary));
-    } while (previous_position->IsIgnored());
-
-    if (previous_position->IsNullPosition())
-      return AXBoundaryType::kUnitBoundary;
-
-    return AXBoundaryType::kNone;
-  }
-
-  bool AtStartOfFormat() const {
-    return GetFormatStartBoundaryType() != AXBoundaryType::kNone;
-  }
-
-  AXBoundaryType GetFormatEndBoundaryType() const {
-    // Since formats are stored on text anchors, the end of a format break must
-    // be at the end of an anchor.
-    if (IsNullPosition() || !AtEndOfAnchor())
-      return AXBoundaryType::kNone;
-
-    // Treat the last iterable node as a format boundary
-    if (CreateNextLeafTreePosition(
-            base::BindRepeating(&AbortMoveAtRootBoundary))
-            ->IsNullPosition())
-      return AXBoundaryType::kContentEnd;
-
-    // Ignored positions cannot be format boundaries.
-    if (IsIgnored())
-      return AXBoundaryType::kNone;
-
-    // Iterate over anchors until a format boundary is found. This will return a
-    // null position upon crossing a boundary. Make sure the next position is
-    // not on an ignored node.
-    AXPositionInstance next_position = Clone();
-    do {
-      next_position = next_position->CreateNextLeafTreePosition(
-          base::BindRepeating(&AbortMoveAtFormatBoundary));
-    } while (next_position->IsIgnored());
-
-    if (next_position->IsNullPosition())
-      return AXBoundaryType::kUnitBoundary;
-
-    return AXBoundaryType::kNone;
-  }
-
-  bool AtEndOfFormat() const {
-    return GetFormatEndBoundaryType() != AXBoundaryType::kNone;
-  }
-
-  bool AtStartOfInlineBlock() const {
-    AXPositionInstance text_position = AsLeafTextPosition();
-    switch (text_position->kind_) {
-      case AXPositionKind::NULL_POSITION:
-        return false;
-      case AXPositionKind::TREE_POSITION:
-        NOTREACHED();
-        return false;
-      case AXPositionKind::TEXT_POSITION: {
-        if (text_position->AtStartOfAnchor()) {
-          AXPositionInstance previous_position =
-              text_position->CreatePreviousLeafTreePosition();
-
-          // Check that this position is not the start of the first anchor.
-          if (!previous_position->IsNullPosition()) {
-            previous_position = text_position->CreatePreviousLeafTreePosition(
-                base::BindRepeating(&AbortMoveAtStartOfInlineBlock));
-
-            // If we get a null position here it means we have crossed an inline
-            // block's start, thus this position is located at such start.
-            if (previous_position->IsNullPosition())
-              return true;
-          }
-        }
-        if (text_position->AtEndOfAnchor()) {
-          AXPositionInstance next_position =
-              text_position->CreateNextLeafTreePosition();
-
-          // Check that this position is not the end of the last anchor.
-          if (!next_position->IsNullPosition()) {
-            next_position = text_position->CreateNextLeafTreePosition(
-                base::BindRepeating(&AbortMoveAtStartOfInlineBlock));
-
-            // If we get a null position here it means we have crossed an inline
-            // block's start, thus this position is located at such start.
-            if (next_position->IsNullPosition())
-              return true;
-          }
-        }
-        return false;
-      }
-    }
-  }
-
   // Returns true if this position is at the start of all content. This might
   // refer to e.g. a single webpage (made up of multiple iframes), or a PDF
   // document. Note that the current webpage could be made up of multiple
@@ -1193,7 +1195,7 @@
   // Also, this method uses position instead of tree logic to traverse the tree,
   // because positions can handle moving across multiple trees, while trees
   // cannot.
-  AXPositionInstance LowestCommonAncestor(
+  AXPositionInstance LowestCommonAncestorPosition(
       const AXPosition& other,
       ax::mojom::MoveDirection move_direction) const {
     return CreateAncestorPosition(LowestCommonAnchor(other), move_direction);
@@ -1634,8 +1636,9 @@
     return text_position;
   }
 
-  // Converts to a text position that is suitable for passing into renderer
-  // as a selection endpoint.
+  // Converts to a text position that is suitable for passing into the renderer
+  // as a selection endpoint. In other words, converts to a position that is
+  // suitable for setting as a DOM selection range endpoint.
   //
   // When blink is asked to set selection, it expects a text position to be
   // anchored to the text node (otherwise a generic tree position is assumed
@@ -1645,10 +1648,11 @@
   // text-fields: an attempt to select the text beyond the first line results
   // in a wrong selection which looks as if the text offset was counted through
   // the first line only.
-  AXPositionInstance AsTextSelectionPosition() const {
-    if (IsNullPosition()) {
+  // TODO(nektar): Make this work in plain text fields too.
+  AXPositionInstance AsDomSelectionPosition() const {
+    if (IsNullPosition())
       return Clone();
-    }
+
     AXPositionInstance text_position = AsLeafTextPosition();
     if (text_position->GetAnchor() && text_position->GetAnchor()->GetRole() ==
                                           ax::mojom::Role::kInlineTextBox) {
@@ -2892,6 +2896,14 @@
         base::BindRepeating(&AtEndOfLinePredicate));
   }
 
+  AXPositionInstance CreateNextFormatStartPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreateBoundaryStartPosition(
+        boundary_behavior, ax::mojom::MoveDirection::kForward,
+        base::BindRepeating(&AtStartOfFormatPredicate),
+        base::BindRepeating(&AtEndOfFormatPredicate));
+  }
+
   AXPositionInstance CreatePreviousFormatStartPosition(
       AXBoundaryBehavior boundary_behavior) const {
     return CreateBoundaryStartPosition(
@@ -2900,6 +2912,14 @@
         base::BindRepeating(&AtEndOfFormatPredicate));
   }
 
+  AXPositionInstance CreateNextFormatEndPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreateBoundaryEndPosition(
+        boundary_behavior, ax::mojom::MoveDirection::kForward,
+        base::BindRepeating(&AtStartOfFormatPredicate),
+        base::BindRepeating(&AtEndOfFormatPredicate));
+  }
+
   AXPositionInstance CreatePreviousFormatEndPosition(
       AXBoundaryBehavior boundary_behavior) const {
     return CreateBoundaryEndPosition(
@@ -2908,20 +2928,40 @@
         base::BindRepeating(&AtEndOfFormatPredicate));
   }
 
-  AXPositionInstance CreateNextFormatStartPosition(
+  AXPositionInstance CreateNextSentenceStartPosition(
       AXBoundaryBehavior boundary_behavior) const {
     return CreateBoundaryStartPosition(
         boundary_behavior, ax::mojom::MoveDirection::kForward,
-        base::BindRepeating(&AtStartOfFormatPredicate),
-        base::BindRepeating(&AtEndOfFormatPredicate));
+        base::BindRepeating(&AtStartOfSentencePredicate),
+        base::BindRepeating(&AtEndOfSentencePredicate),
+        base::BindRepeating(&GetSentenceStartOffsetsFunc));
   }
 
-  AXPositionInstance CreateNextFormatEndPosition(
+  AXPositionInstance CreatePreviousSentenceStartPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreateBoundaryStartPosition(
+        boundary_behavior, ax::mojom::MoveDirection::kBackward,
+        base::BindRepeating(&AtStartOfSentencePredicate),
+        base::BindRepeating(&AtEndOfSentencePredicate),
+        base::BindRepeating(&GetSentenceStartOffsetsFunc));
+  }
+
+  AXPositionInstance CreateNextSentenceEndPosition(
       AXBoundaryBehavior boundary_behavior) const {
     return CreateBoundaryEndPosition(
         boundary_behavior, ax::mojom::MoveDirection::kForward,
-        base::BindRepeating(&AtStartOfFormatPredicate),
-        base::BindRepeating(&AtEndOfFormatPredicate));
+        base::BindRepeating(&AtStartOfSentencePredicate),
+        base::BindRepeating(&AtEndOfSentencePredicate),
+        base::BindRepeating(&GetSentenceEndOffsetsFunc));
+  }
+
+  AXPositionInstance CreatePreviousSentenceEndPosition(
+      AXBoundaryBehavior boundary_behavior) const {
+    return CreateBoundaryEndPosition(
+        boundary_behavior, ax::mojom::MoveDirection::kBackward,
+        base::BindRepeating(&AtStartOfSentencePredicate),
+        base::BindRepeating(&AtEndOfSentencePredicate),
+        base::BindRepeating(&GetSentenceEndOffsetsFunc));
   }
 
   AXPositionInstance CreateNextParagraphStartPosition(
@@ -3335,42 +3375,6 @@
     return unignored_position;
   }
 
-  AXPositionInstance CreateNextSentenceStartPosition(
-      AXBoundaryBehavior boundary_behavior) const {
-    return CreateBoundaryStartPosition(
-        boundary_behavior, ax::mojom::MoveDirection::kForward,
-        base::BindRepeating(&AtStartOfSentencePredicate),
-        base::BindRepeating(&AtEndOfSentencePredicate),
-        base::BindRepeating(&GetSentenceStartOffsetsFunc));
-  }
-
-  AXPositionInstance CreatePreviousSentenceStartPosition(
-      AXBoundaryBehavior boundary_behavior) const {
-    return CreateBoundaryStartPosition(
-        boundary_behavior, ax::mojom::MoveDirection::kBackward,
-        base::BindRepeating(&AtStartOfSentencePredicate),
-        base::BindRepeating(&AtEndOfSentencePredicate),
-        base::BindRepeating(&GetSentenceStartOffsetsFunc));
-  }
-
-  AXPositionInstance CreateNextSentenceEndPosition(
-      AXBoundaryBehavior boundary_behavior) const {
-    return CreateBoundaryEndPosition(
-        boundary_behavior, ax::mojom::MoveDirection::kForward,
-        base::BindRepeating(&AtStartOfSentencePredicate),
-        base::BindRepeating(&AtEndOfSentencePredicate),
-        base::BindRepeating(&GetSentenceEndOffsetsFunc));
-  }
-
-  AXPositionInstance CreatePreviousSentenceEndPosition(
-      AXBoundaryBehavior boundary_behavior) const {
-    return CreateBoundaryEndPosition(
-        boundary_behavior, ax::mojom::MoveDirection::kBackward,
-        base::BindRepeating(&AtStartOfSentencePredicate),
-        base::BindRepeating(&AtEndOfSentencePredicate),
-        base::BindRepeating(&GetSentenceEndOffsetsFunc));
-  }
-
   // Uses depth-first pre-order traversal.
   AXPositionInstance CreateNextAnchorPosition() const {
     return CreateNextAnchorPosition(
@@ -3635,8 +3639,9 @@
     // equivalent positions going to erroneously have the same lowest common
     // ancestor position when converted to tree positions as the ones they had
     // before the conversion?" In other words, when will
-    // "this->AsTreePosition()->LowestCommonAncestor(*other.AsTreePosition()) ==
-    // other.AsTreePosition()->LowestCommonAncestor(*this->AsTreePosition())"?
+    // "this->AsTreePosition()->LowestCommonAncestorPosition(*other.AsTreePosition())
+    // ==
+    // other.AsTreePosition()->LowestCommonAncestorPosition(*this->AsTreePosition())"?
     // The answer is either when they have the same anchor and at least one is a
     // text position, (a case that was dealt with in the previous block), or
     // when at least one is a text position and one is an ancestor position of
@@ -3781,10 +3786,11 @@
         // would create a kDownstream position.
 
         AXPositionInstance this_text_position_ancestor =
-            LowestCommonAncestor(other, ax::mojom::MoveDirection::kBackward);
+            LowestCommonAncestorPosition(other,
+                                         ax::mojom::MoveDirection::kBackward);
         AXPositionInstance other_text_position_ancestor =
-            other.LowestCommonAncestor(*this,
-                                       ax::mojom::MoveDirection::kBackward);
+            other.LowestCommonAncestorPosition(
+                *this, ax::mojom::MoveDirection::kBackward);
         DCHECK(this_text_position_ancestor->IsTextPosition());
         DCHECK(other_text_position_ancestor->IsTextPosition());
 
@@ -4462,6 +4468,11 @@
                                    const AXMoveType type,
                                    const AXMoveDirection direction)>;
 
+  bool IsInUnignoredTextListMarker() const {
+    return GetAnchorRole() == ax::mojom::Role::kListMarker &&
+           !GetAnchor()->IsIgnored() && !AnchorUnignoredChildCount();
+  }
+
   // A text span is defined by a series of inline text boxes that make up a
   // single static text object.
   bool AtEndOfTextSpan() const {
diff --git a/ui/accessibility/platform/BUILD.gn b/ui/accessibility/platform/BUILD.gn
index 3952198..d259526 100644
--- a/ui/accessibility/platform/BUILD.gn
+++ b/ui/accessibility/platform/BUILD.gn
@@ -173,6 +173,8 @@
         "ax_utils_mac.mm",
         "inspect/ax_call_statement_invoker_mac.h",
         "inspect/ax_call_statement_invoker_mac.mm",
+        "inspect/ax_event_recorder_mac.h",
+        "inspect/ax_event_recorder_mac.mm",
         "inspect/ax_inspect_utils_mac.h",
         "inspect/ax_inspect_utils_mac.mm",
         "inspect/ax_transform_mac.h",
diff --git a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
index 36c1d79..27d7113 100644
--- a/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_textrangeprovider_win.cc
@@ -903,10 +903,11 @@
   UIA_VALIDATE_TEXTRANGEPROVIDER_CALL();
 
   const AXPositionInstance start_common_ancestor =
-      start()->LowestCommonAncestor(*end(),
-                                    ax::mojom::MoveDirection::kBackward);
+      start()->LowestCommonAncestorPosition(
+          *end(), ax::mojom::MoveDirection::kBackward);
   const AXPositionInstance end_common_ancestor =
-      end()->LowestCommonAncestor(*start(), ax::mojom::MoveDirection::kForward);
+      end()->LowestCommonAncestorPosition(*start(),
+                                          ax::mojom::MoveDirection::kForward);
   if (start_common_ancestor->IsNullPosition() ||
       end_common_ancestor->IsNullPosition()) {
     return E_INVALIDARG;
@@ -1187,7 +1188,7 @@
   // Note that the "ax::mojom::MoveDirection" should not matter when calculating
   // the ancestor position for use when navigating by page or document, so we
   // use a backward direction as the default.
-  AXPositionInstance common_ancestor = start()->LowestCommonAncestor(
+  AXPositionInstance common_ancestor = start()->LowestCommonAncestorPosition(
       *end(), ax::mojom::MoveDirection::kBackward);
   if (!common_ancestor->GetAnchor()->tree()->HasPaginationSupport())
     return MoveEndpointByDocument(std::move(endpoint), count, units_moved);
diff --git a/ui/accessibility/platform/inspect/ax_event_recorder_mac.h b/ui/accessibility/platform/inspect/ax_event_recorder_mac.h
new file mode 100644
index 0000000..1cf1ba7
--- /dev/null
+++ b/ui/accessibility/platform/inspect/ax_event_recorder_mac.h
@@ -0,0 +1,50 @@
+// Copyright 2022 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_EVENT_RECORDER_MAC_H_
+#define UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_EVENT_RECORDER_MAC_H_
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/mac/scoped_cftyperef.h"
+#include "base/process/process_handle.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder.h"
+#include "ui/accessibility/platform/inspect/ax_inspect.h"
+
+namespace ui {
+
+// Implementation of AXEventRecorder that uses AXObserver to watch for
+// NSAccessibility events.
+class AX_EXPORT AXEventRecorderMac : public AXEventRecorder {
+ public:
+  AXEventRecorderMac(base::ProcessId pid, const AXTreeSelector& selector);
+
+  AXEventRecorderMac(const AXEventRecorderMac&) = delete;
+  AXEventRecorderMac& operator=(const AXEventRecorderMac&) = delete;
+
+  ~AXEventRecorderMac() override;
+
+  // Callback executed every time we receive an event notification.
+  void EventReceived(AXUIElementRef element,
+                     CFStringRef notification,
+                     CFDictionaryRef user_info);
+  static std::string SerializeTextSelectionChangedProperties(
+      CFDictionaryRef user_info);
+
+ private:
+  // Add one notification to the list of notifications monitored by our
+  // observer.
+  void AddNotification(NSString* notification);
+
+  // The AXUIElement for the application.
+  base::ScopedCFTypeRef<AXUIElementRef> application_;
+
+  // The AXObserver we use to monitor AX notifications.
+  base::ScopedCFTypeRef<AXObserverRef> observer_ref_;
+  CFRunLoopSourceRef observer_run_loop_source_;
+};
+
+}  // namespace ui
+
+#endif  // UI_ACCESSIBILITY_PLATFORM_INSPECT_AX_EVENT_RECORDER_MAC_H_
diff --git a/content/browser/accessibility/accessibility_event_recorder_mac.mm b/ui/accessibility/platform/inspect/ax_event_recorder_mac.mm
similarity index 79%
rename from content/browser/accessibility/accessibility_event_recorder_mac.mm
rename to ui/accessibility/platform/inspect/ax_event_recorder_mac.mm
index da1ba0b..0d17b45 100644
--- a/content/browser/accessibility/accessibility_event_recorder_mac.mm
+++ b/ui/accessibility/platform/inspect/ax_event_recorder_mac.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/accessibility/accessibility_event_recorder_mac.h"
+#include "ui/accessibility/platform/inspect/ax_event_recorder_mac.h"
 
 #import <Cocoa/Cocoa.h>
 
@@ -12,14 +12,14 @@
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/scoped_cftyperef.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/sys_string_conversions.h"
-#include "content/browser/accessibility/browser_accessibility_manager.h"
 #include "ui/accessibility/platform/ax_private_webkit_constants_mac.h"
 #include "ui/accessibility/platform/inspect/ax_inspect_utils_mac.h"
 #include "ui/accessibility/platform/inspect/ax_tree_formatter_mac.h"
 
-namespace content {
+namespace ui {
 
 // Callback function registered using AXObserverCreate.
 static void EventReceivedThunk(AXObserverRef observer_ref,
@@ -27,14 +27,12 @@
                                CFStringRef notification,
                                CFDictionaryRef user_info,
                                void* refcon) {
-  AccessibilityEventRecorderMac* this_ptr =
-      static_cast<AccessibilityEventRecorderMac*>(refcon);
+  AXEventRecorderMac* this_ptr = static_cast<AXEventRecorderMac*>(refcon);
   this_ptr->EventReceived(element, notification, user_info);
 }
 
-AccessibilityEventRecorderMac::AccessibilityEventRecorderMac(
-    base::ProcessId pid,
-    const AXTreeSelector& selector)
+AXEventRecorderMac::AXEventRecorderMac(base::ProcessId pid,
+                                       const AXTreeSelector& selector)
     : observer_run_loop_source_(nullptr) {
   AXUIElementRef node = nil;
   if (pid) {
@@ -43,7 +41,7 @@
       LOG(FATAL) << "Failed to get AXUIElement for pid " << pid;
     }
   } else {
-    std::tie(node, pid) = ui::FindAXUIElement(selector);
+    std::tie(node, pid) = FindAXUIElement(selector);
     if (!node) {
       LOG(FATAL) << "Failed to get AXUIElement for selector";
     }
@@ -116,24 +114,24 @@
                      kCFRunLoopDefaultMode);
 }
 
-AccessibilityEventRecorderMac::~AccessibilityEventRecorderMac() {
+AXEventRecorderMac::~AXEventRecorderMac() {
   CFRunLoopRemoveSource(CFRunLoopGetCurrent(), observer_run_loop_source_,
                         kCFRunLoopDefaultMode);
 }
 
-void AccessibilityEventRecorderMac::AddNotification(NSString* notification) {
+void AXEventRecorderMac::AddNotification(NSString* notification) {
   AXObserverAddNotification(observer_ref_, application_,
                             base::mac::NSToCFCast(notification), this);
 }
 
-void AccessibilityEventRecorderMac::EventReceived(AXUIElementRef element,
-                                                  CFStringRef notification,
-                                                  CFDictionaryRef user_info) {
+void AXEventRecorderMac::EventReceived(AXUIElementRef element,
+                                       CFStringRef notification,
+                                       CFDictionaryRef user_info) {
   std::string notification_str = base::SysCFStringRefToUTF8(notification);
 
   auto formatter = ui::AXTreeFormatterMac();
   formatter.SetPropertyFilters(property_filters_,
-                               ui::AXTreeFormatter::kFiltersDefaultSet);
+                               AXTreeFormatter::kFiltersDefaultSet);
 
   std::string element_str =
       formatter.FormatTree(formatter.BuildNode(static_cast<id>(element)));
@@ -153,8 +151,7 @@
   OnEvent(log);
 }
 
-std::string
-AccessibilityEventRecorderMac::SerializeTextSelectionChangedProperties(
+std::string AXEventRecorderMac::SerializeTextSelectionChangedProperties(
     CFDictionaryRef user_info) {
   if (user_info == nil)
     return {};
@@ -167,17 +164,17 @@
         auto* value = static_cast<NSObject*>(raw_value);
         auto* serialized_info = static_cast<std::vector<std::string>*>(context);
         std::string value_string;
-        if ([key isEqual:ui::NSAccessibilityTextStateChangeTypeKey]) {
-          value_string = ToString(static_cast<ui::AXTextStateChangeType>(
+        if ([key isEqual:NSAccessibilityTextStateChangeTypeKey]) {
+          value_string = ToString(static_cast<AXTextStateChangeType>(
               [static_cast<NSNumber*>(value) intValue]));
-        } else if ([key isEqual:ui::NSAccessibilityTextSelectionDirection]) {
-          value_string = ToString(static_cast<ui::AXTextSelectionDirection>(
+        } else if ([key isEqual:NSAccessibilityTextSelectionDirection]) {
+          value_string = ToString(static_cast<AXTextSelectionDirection>(
               [static_cast<NSNumber*>(value) intValue]));
-        } else if ([key isEqual:ui::NSAccessibilityTextSelectionGranularity]) {
-          value_string = ToString(static_cast<ui::AXTextSelectionGranularity>(
+        } else if ([key isEqual:NSAccessibilityTextSelectionGranularity]) {
+          value_string = ToString(static_cast<AXTextSelectionGranularity>(
               [static_cast<NSNumber*>(value) intValue]));
-        } else if ([key isEqual:ui::NSAccessibilityTextEditType]) {
-          value_string = ToString(static_cast<ui::AXTextEditType>(
+        } else if ([key isEqual:NSAccessibilityTextEditType]) {
+          value_string = ToString(static_cast<AXTextEditType>(
               [static_cast<NSNumber*>(value) intValue]));
         } else {
           return;
@@ -194,4 +191,4 @@
   return base::JoinString(serialized_info, " ");
 }
 
-}  // namespace content
+}  // namespace ui
diff --git a/ui/file_manager/file_manager/foreground/css/file_manager.css b/ui/file_manager/file_manager/foreground/css/file_manager.css
index 84b9ce0d..2126ab0 100644
--- a/ui/file_manager/file_manager/foreground/css/file_manager.css
+++ b/ui/file_manager/file_manager/foreground/css/file_manager.css
@@ -69,7 +69,7 @@
 /* Main part of the dialog between header and footer. */
 .dialog-container {
   align-items: stretch;
-  background-color: rgb(250, 250, 250);  /* Makes #drag-container invisible. */
+  background-color: var(--cros-bg-color);
   display: flex;
   flex: auto;
   flex-direction: row;
@@ -77,27 +77,19 @@
   position: relative;
 }
 
-body.files-ng .dialog-container {
-  background-color: var(--cros-bg-color);
-}
-
 /* List and grid are inside this container. */
 .dialog-main {
   align-items: stretch;
-  background: rgb(245, 245, 245);
+  background-color: inherit;
   display: flex;
   flex: auto;
   flex-direction: column;
 }
 
-body.files-ng .dialog-main {
-  background-color: inherit;
-}
-
 /* Directory tree at the left. */
 .dialog-navigation-list {
-  background-color: rgb(250, 250, 250);
-  border-inline-end: 1px solid rgba(0, 0, 0, 15%);
+  background-color: inherit;
+  border-inline-end: none;
   display: flex;
   flex: none;
   flex-direction: column;
@@ -108,11 +100,6 @@
   width: 240px;
 }
 
-body.files-ng .dialog-navigation-list {
-  background-color: inherit;
-  border-inline-end: none;
-}
-
 .dialog-navigation-list-contents {
   display: flex;
   flex: 1 1 auto;
@@ -350,7 +337,7 @@
 
 #directory-tree .tree-row > .file-row > .root-eject {
   --text-color: currentColor;
-  --hover-bg-color: rgba(0, 0, 0, 4%);
+  --hover-bg-color: var(--cros-ripple-color);
   min-width: 32px;
   padding: 0;
 }
@@ -364,11 +351,11 @@
 }
 
 #directory-tree .tree-row > .root-eject:focus {
-  outline: 1px auto rgb(77, 144, 254);
+  outline: 1px auto var(--cros-focus-ring-color);
 }
 
 #directory-tree .tree-row > .file-row > .root-eject:focus {
-  border: 1px solid var(--google-blue-600);
+  border: 1px solid var(--cros-focus-ring-color);
 }
 
 #directory-tree .tree-row > .root-eject:active {
@@ -377,7 +364,7 @@
 }
 
 #directory-tree .tree-row > .file-row > .root-eject:active {
-  --ink-color: rgba(0, 0, 0, 88%);
+  --ink-color: var(--cros-ripple-color);
   border-width: 0;
 }
 
@@ -1057,7 +1044,7 @@
 }
 
 html:not(.pointer-active) .dialog-header.files-ng #search-box .clear:hover {
-  background-color: rgba(0, 0, 0, 4%);
+  background-color: var(--cros-ripple-color);
 }
 
 .dialog-header.files-ng #search-box .clear > .icon {
@@ -1362,20 +1349,14 @@
 /* The toolbar indicator that means the current directory is read only. */
 #read-only-indicator {
   align-items: center;
-  background-color: rgba(0, 0, 0, 20%);
+  background-color: var(--cros-highlight-color-hover);
   border-radius: 16px;
   display: flex;
   flex: none;
   height: 32px;
-  margin-inline-start: 4px;
-  padding: 0 16px;
-}
-
-body.files-ng #read-only-indicator {
-  background-color: var(--cros-highlight-color-hover);
   margin-inline-start: 12px;
-  padding-inline-end: 16px;
-  padding-inline-start: 12px;
+  padding: 0 16px;
+  padding-inline: 16px 12px;
 }
 
 body.check-select #read-only-indicator {
@@ -1467,40 +1448,6 @@
   visibility: visible;
 }
 
-body.files-ng .downloads-warning a:link,
-body.files-ng .volume-warning a:link {
-  color: var(--google-blue-600);
-}
-
-body.files-ng .downloads-warning .warning-icon {
-  -webkit-mask-image: url(../images/files/ui/warning.svg);
-  background-color: var(--google-grey-700);
-  background-size: 20px 20px;
-  flex: none;
-  height: 20px;
-  margin-inline-end: 12px;
-  margin-inline-start: 8px;
-  width: 20px;
-}
-
-body.files-ng .warning-message .link-wrapper {
-  display: inline;
-}
-
-body.files-ng .warning-message .link-wrapper > a {
-  display: inline-block;
-  outline-offset: -2px;
-  padding: 0 4px;
-  text-decoration: none;
-}
-
-body.files-ng .downloads-warning[hidden] {
-  display: flex !important;  /* Overrides [hidden] for animation. */
-  height: 0;
-  opacity: 0;
-  visibility: hidden;
-}
-
 @keyframes heightAnimation {
   0% {
     display: flex;
@@ -1999,18 +1946,23 @@
 
 /* Container for a table header. */
 .table-header {
-  border-bottom: 1px solid rgb(210, 210, 210);
+  border-bottom: 1px solid var(--cros-separator-color);
   box-sizing: border-box;
+  color: var(--cros-text-color-secondary);
   flex: none;
-  height: 40px;
+  font-family: 'Roboto Medium';
+  height: 48px;
+  line-height: 20px;
 }
 
 /* Text label in a table header. */
 .table-header-label {
-  color: rgb(100, 100, 100);
-  font-weight: 500;
-  line-height: 40px;
+  color: inherit;
+  font-weight: normal;
+  line-height: 20px;
   margin: 0 10px;
+  margin-inline-end: 0;
+  margin-inline-start: 32px;
 }
 
 .table-row-cell > * {
@@ -2021,7 +1973,7 @@
 }
 
 .table-row-cell {
-  color: rgb(100, 100, 100);
+  color: var(--cros-text-color-secondary);
 }
 
 .table-row-cell > .detail-name {
@@ -2029,7 +1981,7 @@
 }
 
 .table-row-cell > .detail-name {
-  color: rgb(74, 74, 74);
+  color: var(--cros-text-color-primary);
 }
 
 .table-row-cell {
@@ -2064,7 +2016,7 @@
 #list-container li .detail-checkmark {
   background-position: center;
   background-repeat: no-repeat;
-  color: var(--google-blue-600);
+  color: var(--cros-icon-color-selection);
   height: 28px;
   isolation: isolate;
   opacity: 0;
@@ -2236,21 +2188,6 @@
   font-size: 0;
 }
 
-div.shade {
-  /* transition: opacity 1000ms linear; */
-  background-color: rgba(255, 255, 255, 80%);
-  bottom: 0;
-  left: 0;
-  opacity: 0;
-  position: absolute;
-  right: 0;
-  top: 0;
-}
-
-div.shade[fadein] {
-  opacity: 1;
-}
-
 /* Message panel for unmounted Drive */
 #format-panel {
   bottom: 0;
@@ -2291,27 +2228,10 @@
   display: flex;
 }
 
-.plain-link {
-  color: rgb(17, 85, 204);
-  cursor: pointer;
-  text-decoration: none;
-}
-
 .buttonbar > * {
   position: relative;
 }
 
-#volume-space-info-contents {
-  align-items: center;
-  display: flex;
-}
-
-#volume-space-info-contents > div {
-  display: flex;
-  flex: auto;
-  margin-inline-start: 15px;
-}
-
 #list-container .table-header-inner {
   height: 100%;
 }
@@ -2329,22 +2249,6 @@
   padding-inline-start: 0;
 }
 
-body.files-ng .table-header {
-  border-bottom: 1px solid var(--cr-separator-color, rgb(224, 224, 224));
-  color: var(--cros-text-color-secondary);
-  font-family: 'Roboto Medium';
-  height: 48px;
-  line-height: 20px;
-}
-
-body.files-ng .table-header-label {
-  color: inherit;
-  font-weight: normal;
-  line-height: 20px;
-  margin-inline-end: 0;
-  margin-inline-start: 32px;
-}
-
 body.files-ng .table-header-cell {
   padding-bottom: 16px;
 }
@@ -2479,7 +2383,6 @@
 
 body.files-ng .table-row-cell .filename-label {
   color: var(--cros-text-color-primary);
-
   padding-top: 0;
 }
 
@@ -2539,7 +2442,7 @@
 }
 
 .install-linux-package-details-frame {
-  border: 1px solid lightgray;
+  border: 1px solid var(--cros-separator-color);
   display: block;
   height: 150px;
   overflow: scroll;
@@ -2700,6 +2603,7 @@
   margin-top: 5px;
 }
 
+/* TODO(crbug.com/1287914): Update these colors to semantic equivalents. */
 .file-type-filter-button {
   align-items: center;
   /* don't use browser's default <button> background-color. */
diff --git a/ui/gl/init/gl_factory.cc b/ui/gl/init/gl_factory.cc
index 138b7f15..6a369a8 100644
--- a/ui/gl/init/gl_factory.cc
+++ b/ui/gl/init/gl_factory.cc
@@ -172,7 +172,7 @@
 
 GLImplementationParts GetSoftwareGLImplementationForPlatform() {
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || \
-    BUILDFLAG(IS_CHROMEOS)
+    BUILDFLAG(IS_CHROMEOS_LACROS)
   return GetSoftwareGLImplementation();
 #else
   return GetLegacySoftwareGLImplementation();
diff --git a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
index 0c54d912..5e5b849 100644
--- a/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
+++ b/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc
@@ -270,13 +270,15 @@
   return std::vector<gl::GLImplementationParts>{
       gl::GLImplementationParts(gl::kGLImplementationEGLGLES2),
       gl::GLImplementationParts(gl::kGLImplementationEGLANGLE),
-      gl::GLImplementationParts(gl::ANGLEImplementation::kSwiftShader)};
+      gl::GLImplementationParts(gl::ANGLEImplementation::kSwiftShader),
+      gl::GLImplementationParts(gl::kGLImplementationSwiftShaderGL)};
 }
 
 GLOzone* GbmSurfaceFactory::GetGLOzone(
     const gl::GLImplementationParts& implementation) {
   switch (implementation.gl) {
     case gl::kGLImplementationEGLGLES2:
+    case gl::kGLImplementationSwiftShaderGL:
     case gl::kGLImplementationEGLANGLE:
       return egl_implementation_.get();
     default:
diff --git a/ui/ozone/platform/wayland/host/wayland_shm.cc b/ui/ozone/platform/wayland/host/wayland_shm.cc
index de97ad1..31834e37 100644
--- a/ui/ozone/platform/wayland/host/wayland_shm.cc
+++ b/ui/ozone/platform/wayland/host/wayland_shm.cc
@@ -11,7 +11,17 @@
 
 namespace {
 constexpr uint32_t kMinVersion = 1;
-constexpr uint32_t kShmFormat = WL_SHM_FORMAT_ARGB8888;
+
+// Given that buffers for canvas surfaces are submitted with alpha disabled,
+// using a format with alpha channel results in popup surfaces that have black
+// background when they are shown with fade out animation. Thus, disable this
+// channel so that exo sets the background of these canvas surface transparent.
+constexpr uint32_t kShmFormat =
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+    WL_SHM_FORMAT_XRGB8888;
+#else
+    WL_SHM_FORMAT_ARGB8888;
+#endif
 }  // namespace
 
 // static
diff --git a/ui/ozone/platform/wayland/host/wayland_window.cc b/ui/ozone/platform/wayland/host/wayland_window.cc
index c99f1f5..12ec141 100644
--- a/ui/ozone/platform/wayland/host/wayland_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window.cc
@@ -823,19 +823,9 @@
       std::max(overlays.size() - num_background_planes,
                wayland_subsurfaces_.size() + 1));
 
-  // TODO(fangzhoug): Keeping this surface alive removes the black background
-  // when doing animation of showing/hiding auxiliary windows. i.e. Without
-  // overlay delegation feature, black background is shown on tooltip. So keep a
-  // fake config for primary_subsurface when it is not in the overlay list, such
-  // that the frame_manager does not destroy the subsurface.
-  subsurfaces_to_overlays.emplace_back(
-      primary_subsurface(),
-      num_primary_planes ? std::move(*split)
-                         : ui::ozone::mojom::WaylandOverlayConfig::New());
-  if (!num_primary_planes) {
-    auto& primary_config = subsurfaces_to_overlays.back().second;
-    primary_config->opacity =
-        primary_subsurface()->wayland_surface()->opacity();
+  if (num_primary_planes) {
+    subsurfaces_to_overlays.emplace_back(primary_subsurface(),
+                                         std::move(*split));
   }
 
   {
diff --git a/ui/ozone/platform/x11/x11_surface_factory.cc b/ui/ozone/platform/x11/x11_surface_factory.cc
index 6f70ed0f..9e6a71e 100644
--- a/ui/ozone/platform/x11/x11_surface_factory.cc
+++ b/ui/ozone/platform/x11/x11_surface_factory.cc
@@ -107,7 +107,8 @@
       gl::GLImplementationParts(gl::kGLImplementationDesktopGL),
       gl::GLImplementationParts(gl::kGLImplementationEGLGLES2),
       gl::GLImplementationParts(gl::kGLImplementationEGLANGLE),
-      gl::GLImplementationParts(gl::ANGLEImplementation::kSwiftShader)};
+      gl::GLImplementationParts(gl::ANGLEImplementation::kSwiftShader),
+      gl::GLImplementationParts(gl::kGLImplementationSwiftShaderGL)};
 }
 
 GLOzone* X11SurfaceFactory::GetGLOzone(
@@ -117,6 +118,7 @@
       return glx_implementation_.get();
     case gl::kGLImplementationEGLGLES2:
     case gl::kGLImplementationEGLANGLE:
+    case gl::kGLImplementationSwiftShaderGL:
       return egl_implementation_.get();
     default:
       return nullptr;
diff --git a/ui/views/controls/animated_image_view.h b/ui/views/controls/animated_image_view.h
index 46edcaa3..2a163a1f 100644
--- a/ui/views/controls/animated_image_view.h
+++ b/ui/views/controls/animated_image_view.h
@@ -64,6 +64,9 @@
   // Stops any animation and resets it to the start frame.
   void Stop();
 
+  // May return null if SetAnimatedImage() has not been called.
+  lottie::Animation* animated_image() { return animated_image_.get(); }
+
  private:
   // Overridden from View:
   void OnPaint(gfx::Canvas* canvas) override;