diff --git a/DEPS b/DEPS
index f53d49a1..c86c4a63 100644
--- a/DEPS
+++ b/DEPS
@@ -295,15 +295,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': '44062eff3e250e3902e897231339c3f126c9a3f1',
+  'skia_revision': 'c83eef7dc2a3a40f8e7906fb0f764342c4c36c6b',
   # 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': '9dcc25911c1ed7e2c5bbf5a1741e8e9b46b58da9',
+  'v8_revision': 'da3ee47c7e2d2dd1251defc21d35595b4241a899',
   # 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': '684ff60bd7c482b8b64eb254aa5877e7b873f5ca',
+  'angle_revision': '5dc73efc93dc961f8c2b15bf78c1aa6ea3baf617',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -346,7 +346,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
-  'freetype_revision': 'b405fc5c1dbbe8163bf199b921ccfc5f902eaa4f',
+  'freetype_revision': '26e9028f10657acefc801bb7d5276419963e60cb',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling freetype
   # and whatever else without interference from each other.
@@ -410,7 +410,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': '3f96cc8bb817c9a52d6ade192c1ec1025776d84c',
+  'dawn_revision': '60dc70df711186324b8f7a81fe9b5ab87fd724d9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -764,7 +764,7 @@
 
   'src/clank': {
     'url': 'https://chrome-internal.googlesource.com/clank/internal/apps.git' + '@' +
-    '621806c82e5aeeb0a96a44fe836e3c80384f9a7b',
+    '3f25119743363dffaf4a9954bf0b6b33d8bc8cfb',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -953,7 +953,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'pQKsjiiz1nkGWtGNFWM1RU3zQe8J3zyCH6rt8JN1OMMC',
+          'version': 'idkbht2PgWimRkZpE0xUViXvBvgDrBrXybFO9U-c2owC',
       },
     ],
     'condition': 'checkout_android',
@@ -1673,7 +1673,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/r8',
-              'version': '3Vuxtp3m63h2bcmamz2iBC04rNQOUmQ3O6eDyLoVY3EC',
+              'version': 'lillZvBtdIMEXU6ZjAiEGDqyEqLe0DiozKLwy2X0QKkC',
           },
       ],
       'condition': 'checkout_android',
@@ -1815,10 +1815,10 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd1b65aa5a88f6efd900604dfcda840154e9f16e2',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '1ff6ae7870a674b2e2ee7b003004cca4d004d8b1',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'ae20ffa292e10f6761cff57b71e925802a93c3bd',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'a106095333cc0ecb0405dbc2199445878c197c16',
+    Var('webrtc_git') + '/src.git' + '@' + 'b02a8f5a7cb432782fa23afc1cdad3bb1ee5a165',
 
   # Wuffs' canonical repository is at github.com/google/wuffs, but we use
   # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file.
@@ -1888,7 +1888,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@acfddfeb9d6828fdcd5ab3ff32a5427df6ebdb37',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@da9f92d8cf2a95e23e5f202e4c3b2913d69b5d04',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 7414121..6ca0fd7a8 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -336,12 +336,8 @@
     "capture_mode/key_combo_view.h",
     "capture_mode/key_item_view.cc",
     "capture_mode/key_item_view.h",
-    "capture_mode/pointer_highlight_layer.cc",
-    "capture_mode/pointer_highlight_layer.h",
     "capture_mode/recording_overlay_controller.cc",
     "capture_mode/recording_overlay_controller.h",
-    "capture_mode/recording_type_menu_view.cc",
-    "capture_mode/recording_type_menu_view.h",
     "capture_mode/stop_recording_button_tray.cc",
     "capture_mode/stop_recording_button_tray.h",
     "capture_mode/user_nudge_controller.cc",
@@ -2778,7 +2774,6 @@
     "capture_mode/capture_mode_test_util.cc",
     "capture_mode/capture_mode_test_util.h",
     "capture_mode/capture_mode_unittests.cc",
-    "capture_mode/gif_recording_unittests.cc",
     "child_accounts/parent_access_controller_impl_unittest.cc",
     "clipboard/clipboard_history_controller_unittest.cc",
     "clipboard/clipboard_history_resource_manager_unittest.cc",
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 9f5db0a..f965d3d 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -4896,11 +4896,8 @@
       <message name="IDS_ASH_SCREEN_CAPTURE_LABEL_IMAGE_CAPTURE" desc="The capture label message which shows in the middle of the captured region in region image capture mode.">
         Capture
       </message>
-      <message name="IDS_ASH_SCREEN_CAPTURE_LABEL_VIDEO_RECORD" desc="The capture label message which shows in the middel of the captured region in region video recording mode.">
-        Record video
-      </message>
-      <message name="IDS_ASH_SCREEN_CAPTURE_LABEL_GIF_RECORD" desc="The capture label message which shows in the middel of the captured region in region GIF recording mode.">
-        Record GIF
+      <message name="IDS_ASH_SCREEN_CAPTURE_LABEL_VIDEO_RECORD" desc="The capture label message which shows in the middel of the captured region in region video record mode or shows in the middle of the screen in fullscreen video record mode.">
+        Record
       </message>
       <message name="IDS_ASH_SCREEN_CAPTURE_SAVE_TO_DIALOG_TITLE" desc="The title of the folder selection dialog, where users can select a location to save their captured images and screen recordings.">
         Select a folder to save to
@@ -5118,9 +5115,6 @@
       <message name="IDS_ASH_SCREEN_CAPTURE_SETTINGS_A11Y_TITLE" desc="The label of window hosting the screen capture settings menu that will be read by ChromeVox when prompted for title.">
         Screen capture settings
       </message>
-      <message name="IDS_ASH_SCREEN_CAPTURE_RECORDING_TYPE_MENU_A11Y_TITLE" desc="The label of window hosting the recording type drop down menu that will be read by ChromeVox when prompted for title.">
-        Recording format menu
-      </message>
       <message name="IDS_ASH_SCREEN_CAPTURE_SAVE_TO_GOOGLE_DRIVE" desc="The label of the menu item button for selecting the root of Google Drive to store the captured images and videos.">
         Google Drive
       </message>
diff --git a/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_LABEL_GIF_RECORD.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_LABEL_GIF_RECORD.png.sha1
deleted file mode 100644
index 9cb735b..0000000
--- a/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_LABEL_GIF_RECORD.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0a240badf861c44ad33c16f26826936a9a1301ac
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_LABEL_VIDEO_RECORD.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_LABEL_VIDEO_RECORD.png.sha1
index 9cb735b..b244956e 100644
--- a/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_LABEL_VIDEO_RECORD.png.sha1
+++ b/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_LABEL_VIDEO_RECORD.png.sha1
@@ -1 +1 @@
-0a240badf861c44ad33c16f26826936a9a1301ac
\ No newline at end of file
+5cc44477bc0dde6b2231bca438cae4eebcf8bdc3
\ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_RECORDING_TYPE_MENU_A11Y_TITLE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_RECORDING_TYPE_MENU_A11Y_TITLE.png.sha1
deleted file mode 100644
index 9cb735b..0000000
--- a/ash/ash_strings_grd/IDS_ASH_SCREEN_CAPTURE_RECORDING_TYPE_MENU_A11Y_TITLE.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-0a240badf861c44ad33c16f26826936a9a1301ac
\ No newline at end of file
diff --git a/ash/capture_mode/capture_button_view.h b/ash/capture_mode/capture_button_view.h
index 7c1e391..6f5e366 100644
--- a/ash/capture_mode/capture_button_view.h
+++ b/ash/capture_mode/capture_button_view.h
@@ -32,7 +32,6 @@
   ~CaptureButtonView() override = default;
 
   views::LabelButton* capture_button() { return capture_button_; }
-  views::ImageButton* drop_down_button() { return drop_down_button_; }
 
   // Updates the icon and text of `capture_button_`, as well as the visibility
   // of the `separator_` and `drop_down_button_` depending on the current type
diff --git a/ash/capture_mode/capture_label_view.cc b/ash/capture_mode/capture_label_view.cc
index 00282936..604510f 100644
--- a/ash/capture_mode/capture_label_view.cc
+++ b/ash/capture_mode/capture_label_view.cc
@@ -26,7 +26,6 @@
 #include "ui/gfx/geometry/transform.h"
 #include "ui/views/animation/animation_builder.h"
 #include "ui/views/background.h"
-#include "ui/views/controls/button/image_button.h"
 #include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/focus_ring.h"
 #include "ui/views/controls/highlight_path_generator.h"
@@ -145,8 +144,7 @@
 
 CaptureLabelView::CaptureLabelView(
     CaptureModeSession* capture_mode_session,
-    base::RepeatingClosure on_capture_button_pressed,
-    base::RepeatingClosure on_drop_down_button_pressed)
+    base::RepeatingClosure on_capture_button_container_pressed)
     : capture_mode_session_(capture_mode_session) {
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
@@ -157,8 +155,7 @@
   layer()->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);
 
   capture_button_container_ = AddChildView(std::make_unique<CaptureButtonView>(
-      std::move(on_capture_button_pressed),
-      std::move(on_drop_down_button_pressed)));
+      std::move(on_capture_button_container_pressed), base::DoNothing()));
   capture_button_container_->SetPaintToLayer();
   capture_button_container_->layer()->SetFillsBoundsOpaquely(false);
   capture_button_container_->SetNotifyEnterExitOnChild(true);
@@ -182,19 +179,6 @@
   return capture_button_container_->GetVisible();
 }
 
-bool CaptureLabelView::IsPointOnRecordingTypeDropDownButton(
-    const gfx::Point& screen_location) const {
-  auto* drop_down_button = capture_button_container_->drop_down_button();
-  return drop_down_button &&
-         drop_down_button->GetBoundsInScreen().Contains(screen_location);
-}
-
-bool CaptureLabelView::IsRecordingTypeDropDownButtonVisible() const {
-  auto* drop_down_button = capture_button_container_->drop_down_button();
-  return capture_button_container_->GetVisible() && drop_down_button &&
-         drop_down_button->GetVisible();
-}
-
 void CaptureLabelView::UpdateIconAndText() {
   CaptureModeController* controller = CaptureModeController::Get();
   const CaptureModeSource source = controller->source();
diff --git a/ash/capture_mode/capture_label_view.h b/ash/capture_mode/capture_label_view.h
index 1fb2af16..3b1a0bd8 100644
--- a/ash/capture_mode/capture_label_view.h
+++ b/ash/capture_mode/capture_label_view.h
@@ -35,24 +35,11 @@
   METADATA_HEADER(CaptureLabelView);
 
   CaptureLabelView(CaptureModeSession* capture_mode_session,
-                   base::RepeatingClosure on_capture_button_pressed,
-                   base::RepeatingClosure on_drop_down_button_pressed);
+                   base::RepeatingClosure on_capture_button_container_pressed);
   CaptureLabelView(const CaptureLabelView&) = delete;
   CaptureLabelView& operator=(const CaptureLabelView&) = delete;
   ~CaptureLabelView() override;
 
-  CaptureButtonView* capture_button_container() {
-    return capture_button_container_;
-  }
-
-  // Returns true if the given `screen_location` is on the drop down button,
-  // which when clicked opens the recording type menu.
-  bool IsPointOnRecordingTypeDropDownButton(
-      const gfx::Point& screen_location) const;
-
-  // Returns true if the recording drop down button is available and visible.
-  bool IsRecordingTypeDropDownButtonVisible() const;
-
   // Returns true if this view is hosting the capture button instead of just a
   // label, and can be interacted with by the user. In this case, this view has
   // views that are a11y highlightable.
diff --git a/ash/capture_mode/capture_mode_demo_tools_controller.cc b/ash/capture_mode/capture_mode_demo_tools_controller.cc
index 8cb8010..4a6ee1d 100644
--- a/ash/capture_mode/capture_mode_demo_tools_controller.cc
+++ b/ash/capture_mode/capture_mode_demo_tools_controller.cc
@@ -7,22 +7,16 @@
 #include <memory>
 
 #include "ash/capture_mode/capture_mode_constants.h"
-#include "ash/capture_mode/capture_mode_util.h"
 #include "ash/capture_mode/key_combo_view.h"
-#include "ash/capture_mode/pointer_highlight_layer.h"
 #include "ash/capture_mode/video_recording_watcher.h"
 #include "base/check_op.h"
 #include "base/containers/contains.h"
-#include "base/containers/cxx20_erase.h"
-#include "base/containers/unique_ptr_adapters.h"
 #include "ui/compositor/layer.h"
-#include "ui/compositor/layer_animator.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/events/types/event_type.h"
 #include "ui/gfx/geometry/rect.h"
-#include "ui/views/animation/animation_builder.h"
 #include "ui/views/widget/widget.h"
 
 namespace ash {
@@ -31,11 +25,6 @@
 
 constexpr int kDistanceFromBottom = 24;
 
-constexpr float kHighlightLayerFinalOpacity = 0.f;
-constexpr float kHighlightLayerInitialScale = 0.1f;
-constexpr float kHighlightLayerFinalScale = 1.0f;
-constexpr base::TimeDelta kScaleUpDuration = base::Milliseconds(1500);
-
 int GetModifierFlagForKeyCode(ui::KeyboardCode key_code) {
   switch (key_code) {
     case ui::VKEY_COMMAND:
@@ -114,38 +103,6 @@
   OnKeyDownEvent(event);
 }
 
-void CaptureModeDemoToolsController::PerformMousePressAnimation(
-    const gfx::PointF& event_location_in_window) {
-  std::unique_ptr<PointerHighlightLayer> mouse_highlight_layer =
-      std::make_unique<PointerHighlightLayer>(
-          event_location_in_window,
-          video_recording_watcher_->GetOnCaptureSurfaceWidgetParentWindow()
-              ->layer());
-  PointerHighlightLayer* mouse_highlight_layer_ptr =
-      mouse_highlight_layer.get();
-  mouse_highlight_layers_.push_back(std::move(mouse_highlight_layer));
-
-  ui::Layer* highlight_layer = mouse_highlight_layer_ptr->layer();
-  highlight_layer->SetTransform(capture_mode_util::GetScaleTransformAboutCenter(
-      highlight_layer, kHighlightLayerInitialScale));
-  const gfx::Transform scale_up_transform =
-      capture_mode_util::GetScaleTransformAboutCenter(
-          highlight_layer, kHighlightLayerFinalScale);
-
-  views::AnimationBuilder()
-      .OnEnded(base::BindOnce(
-          &CaptureModeDemoToolsController::OnMouseHighlightAnimationEnded,
-          weak_ptr_factory_.GetWeakPtr(), mouse_highlight_layer_ptr))
-      .SetPreemptionStrategy(
-          ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
-      .Once()
-      .SetDuration(kScaleUpDuration)
-      .SetTransform(highlight_layer, scale_up_transform,
-                    gfx::Tween::ACCEL_0_40_DECEL_100)
-      .SetOpacity(highlight_layer, kHighlightLayerFinalOpacity,
-                  gfx::Tween::ACCEL_0_80_DECEL_80);
-}
-
 void CaptureModeDemoToolsController::OnKeyUpEvent(ui::KeyEvent* event) {
   const ui::KeyboardCode key_code = event->key_code();
   const int modifier_flag = GetModifierFlagForKeyCode(key_code);
@@ -231,10 +188,4 @@
   key_combo_view_ = nullptr;
 }
 
-void CaptureModeDemoToolsController::OnMouseHighlightAnimationEnded(
-    PointerHighlightLayer* pointer_highlight_layer_ptr) {
-  base::EraseIf(mouse_highlight_layers_,
-                base::MatchesUniquePtr(pointer_highlight_layer_ptr));
-}
-
 }  // namespace ash
\ No newline at end of file
diff --git a/ash/capture_mode/capture_mode_demo_tools_controller.h b/ash/capture_mode/capture_mode_demo_tools_controller.h
index f2791d9cc..3060b58 100644
--- a/ash/capture_mode/capture_mode_demo_tools_controller.h
+++ b/ash/capture_mode/capture_mode_demo_tools_controller.h
@@ -5,7 +5,6 @@
 #ifndef ASH_CAPTURE_MODE_CAPTURE_MODE_DEMO_TOOLS_CONTROLLER_H_
 #define ASH_CAPTURE_MODE_CAPTURE_MODE_DEMO_TOOLS_CONTROLLER_H_
 
-#include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/views/widget/unique_widget_ptr.h"
@@ -16,13 +15,9 @@
 
 namespace ash {
 
-class PointerHighlightLayer;
 class KeyComboView;
 class VideoRecordingWatcher;
 
-using MouseHighlightLayers =
-    std::vector<std::unique_ptr<PointerHighlightLayer>>;
-
 // Observes and decides whether to show a helper widget representing the
 // currently pressed key combination or not. The key combination will be used to
 // construct or modify the `KeyComboViewer`. The
@@ -41,14 +36,6 @@
   // Decides whether to show a helper widget for the `event` or not.
   void OnKeyEvent(ui::KeyEvent* event);
 
-  // Creates a new highlight layer each time it gets called and performs the
-  // grow-and-fade-out animation on it.
-  void PerformMousePressAnimation(const gfx::PointF& event_location_in_window);
-
-  const MouseHighlightLayers& mouse_highlight_layers_for_testing() const {
-    return mouse_highlight_layers_;
-  }
-
  private:
   friend class CaptureModeDemoToolsTestApi;
 
@@ -64,11 +51,6 @@
   // Resets the `demo_tools_widget_` when the `hide_timer_` expires.
   void AnimateToResetTheWidget();
 
-  // Called when the mouse highlight animation ends to remove the corresponding
-  // pointer highlight from the `mouse_highlight_layers_`.
-  void OnMouseHighlightAnimationEnded(
-      PointerHighlightLayer* pointer_highlight_layer_ptr);
-
   VideoRecordingWatcher* const video_recording_watcher_;
   views::UniqueWidgetPtr demo_tools_widget_;
   KeyComboView* key_combo_view_ = nullptr;
@@ -82,11 +64,6 @@
   // Starts on key up of the last non-modifier key and the `key_combo_view_`
   // will disappear when it expires.
   base::OneShotTimer hide_timer_;
-
-  // Contains all the mouse highlight layers that are being animated.
-  MouseHighlightLayers mouse_highlight_layers_;
-
-  base::WeakPtrFactory<CaptureModeDemoToolsController> weak_ptr_factory_{this};
 };
 
 }  // namespace ash
diff --git a/ash/capture_mode/capture_mode_demo_tools_unittests.cc b/ash/capture_mode/capture_mode_demo_tools_unittests.cc
index b816cb5..a01de1ea 100644
--- a/ash/capture_mode/capture_mode_demo_tools_unittests.cc
+++ b/ash/capture_mode/capture_mode_demo_tools_unittests.cc
@@ -17,23 +17,17 @@
 #include "ash/capture_mode/capture_mode_test_util.h"
 #include "ash/capture_mode/capture_mode_types.h"
 #include "ash/capture_mode/key_combo_view.h"
-#include "ash/capture_mode/pointer_highlight_layer.h"
-#include "ash/capture_mode/video_recording_watcher.h"
 #include "ash/constants/ash_features.h"
+#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/style/icon_button.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
-#include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/controls/button/toggle_button.h"
 #include "ui/views/controls/image_view.h"
-#include "ui/wm/core/coordinate_conversion.h"
 
 namespace ash {
 
@@ -58,8 +52,7 @@
 
 class CaptureModeDemoToolsTest : public AshTestBase {
  public:
-  CaptureModeDemoToolsTest()
-      : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+  CaptureModeDemoToolsTest() = default;
   CaptureModeDemoToolsTest(const CaptureModeDemoToolsTest&) = delete;
   CaptureModeDemoToolsTest& operator=(const CaptureModeDemoToolsTest&) = delete;
   ~CaptureModeDemoToolsTest() override = default;
@@ -414,16 +407,6 @@
     EXPECT_TRUE(controller->is_recording_in_progress());
     return controller;
   }
-
-  gfx::Rect GetDemoToolsConfinedBoundsInScreenCoordinates() {
-    auto* recording_watcher =
-        CaptureModeController::Get()->video_recording_watcher_for_testing();
-    gfx::Rect confined_bounds_in_screen =
-        recording_watcher->GetCaptureSurfaceConfineBounds();
-    wm::ConvertRectToScreen(recording_watcher->window_being_recorded(),
-                            &confined_bounds_in_screen);
-    return confined_bounds_in_screen;
-  }
 };
 
 // Tests that the key combo viewer widget should be centered within its confined
@@ -434,8 +417,17 @@
   auto* demo_tools_controller = GetCaptureModeDemoToolsController();
   EXPECT_TRUE(demo_tools_controller);
 
+  auto* recording_watcher =
+      CaptureModeController::Get()->video_recording_watcher_for_testing();
   gfx::Rect confined_bounds_in_screen =
-      GetDemoToolsConfinedBoundsInScreenCoordinates();
+      recording_watcher->GetCaptureSurfaceConfineBounds();
+
+  // Converts the bounds if it is in the window's coordinate to screen
+  // coordinate.
+  if (GetParam() == CaptureModeSource::kWindow) {
+    auto window_bounds = window()->GetBoundsInScreen();
+    confined_bounds_in_screen.Offset(window_bounds.x(), window_bounds.y());
+  }
 
   // Verifies that the `demo_tools_widget` is positioned in the middle
   // horizontally within the confined bounds.
@@ -466,68 +458,6 @@
   EXPECT_FALSE(controller->IsActive());
 }
 
-// Tests that the mouse highlight layer will be created on mouse down and
-// will disappear after the animation.
-TEST_P(CaptureModeDemoToolsTestWithAllSources, MouseHighlightTest) {
-  ui::ScopedAnimationDurationScaleMode normal_animation(
-      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
-  StartDemoToolsEnabledVideoRecordingWithParam();
-  auto* demo_tools_controller = GetCaptureModeDemoToolsController();
-  EXPECT_TRUE(demo_tools_controller);
-
-  gfx::Rect confined_bounds_in_screen =
-      GetDemoToolsConfinedBoundsInScreenCoordinates();
-  auto* event_generator = GetEventGenerator();
-  event_generator->MoveMouseTo(confined_bounds_in_screen.CenterPoint());
-  event_generator->PressLeftButton();
-  event_generator->ReleaseLeftButton();
-  EXPECT_FALSE(
-      demo_tools_controller->mouse_highlight_layers_for_testing().empty());
-  task_environment()->FastForwardBy(base::Milliseconds(3000));
-  EXPECT_TRUE(
-      demo_tools_controller->mouse_highlight_layers_for_testing().empty());
-}
-
-// Tests that multiple mouse highlight layers will be visible on consecutive
-// mouse press events when the whole duration are within the expiration of the
-// first animation expiration. It also tests that each mouse highlight layer
-// will be centered on its mouse event location.
-TEST_P(CaptureModeDemoToolsTestWithAllSources,
-       MouseHighlightShouldBeCenteredWithMouseClick) {
-  ui::ScopedAnimationDurationScaleMode normal_animation(
-      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
-  StartDemoToolsEnabledVideoRecordingWithParam();
-  auto* recording_watcher =
-      CaptureModeController::Get()->video_recording_watcher_for_testing();
-  auto* window_being_recorded = recording_watcher->window_being_recorded();
-  auto* demo_tools_controller = GetCaptureModeDemoToolsController();
-  EXPECT_TRUE(demo_tools_controller);
-
-  gfx::Rect inner_rect = GetDemoToolsConfinedBoundsInScreenCoordinates();
-  inner_rect.Inset(5);
-
-  auto& layers_vector =
-      demo_tools_controller->mouse_highlight_layers_for_testing();
-  auto* event_generator = GetEventGenerator();
-
-  for (auto point : {inner_rect.CenterPoint(), inner_rect.origin(),
-                     inner_rect.bottom_right()}) {
-    event_generator->MoveMouseTo(point);
-    event_generator->PressLeftButton();
-    event_generator->ReleaseLeftButton();
-    auto* highlight_layer = layers_vector.back().get();
-    auto highlight_center_point =
-        highlight_layer->layer()->bounds().CenterPoint();
-
-    // Convert the highlight layer center pointer to screen coordinates.
-    wm::ConvertPointToScreen(window_being_recorded, &highlight_center_point);
-
-    EXPECT_EQ(highlight_center_point, point);
-  }
-
-  EXPECT_EQ(layers_vector.size(), 3u);
-}
-
 INSTANTIATE_TEST_SUITE_P(All,
                          CaptureModeDemoToolsTestWithAllSources,
                          testing::Values(CaptureModeSource::kFullscreen,
diff --git a/ash/capture_mode/capture_mode_menu_group.cc b/ash/capture_mode/capture_mode_menu_group.cc
index 5471076..6224362d 100644
--- a/ash/capture_mode/capture_mode_menu_group.cc
+++ b/ash/capture_mode/capture_mode_menu_group.cc
@@ -346,12 +346,8 @@
 // -----------------------------------------------------------------------------
 // CaptureModeMenuGroup:
 
-CaptureModeMenuGroup::CaptureModeMenuGroup(
-    Delegate* delegate,
-    const gfx::Insets& inside_border_insets)
-    : CaptureModeMenuGroup(delegate,
-                           /*menu_header=*/nullptr,
-                           inside_border_insets) {}
+CaptureModeMenuGroup::CaptureModeMenuGroup(Delegate* delegate)
+    : CaptureModeMenuGroup(delegate, /*menu_header=*/nullptr) {}
 
 CaptureModeMenuGroup::CaptureModeMenuGroup(Delegate* delegate,
                                            const gfx::VectorIcon& header_icon,
@@ -361,8 +357,7 @@
           delegate,
           std::make_unique<CaptureModeMenuHeader>(header_icon,
                                                   std::move(header_label),
-                                                  managed_by_policy),
-          kMenuGroupPadding) {}
+                                                  managed_by_policy)) {}
 
 CaptureModeMenuGroup::~CaptureModeMenuGroup() = default;
 
@@ -473,8 +468,7 @@
 
 CaptureModeMenuGroup::CaptureModeMenuGroup(
     Delegate* delegate,
-    std::unique_ptr<CaptureModeMenuHeader> menu_header,
-    const gfx::Insets& inside_border_insets)
+    std::unique_ptr<CaptureModeMenuHeader> menu_header)
     : delegate_(delegate),
       menu_header_(menu_header ? AddChildView(std::move(menu_header))
                                : nullptr),
@@ -483,7 +477,7 @@
   options_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
   SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::Orientation::kVertical, inside_border_insets,
+      views::BoxLayout::Orientation::kVertical, kMenuGroupPadding,
       kSpaceBetweenMenuItem));
 }
 
diff --git a/ash/capture_mode/capture_mode_menu_group.h b/ash/capture_mode/capture_mode_menu_group.h
index 365b3466..d91fa99c 100644
--- a/ash/capture_mode/capture_mode_menu_group.h
+++ b/ash/capture_mode/capture_mode_menu_group.h
@@ -48,10 +48,8 @@
 
   // This version of the constructor creates a header-less menu group. Note that
   // menu groups without headers is not designed for settings that are managed
-  // by policy. The `inside_border_insets` are used as paddings around the menu
-  // options and items in this group.
-  CaptureModeMenuGroup(Delegate* delegate,
-                       const gfx::Insets& inside_border_insets);
+  // by policy.
+  explicit CaptureModeMenuGroup(Delegate* delegate);
 
   // If `managed_by_policy` is true, the header of this menu group will show an
   // enterprise-managed feature icon next to the `header_label`.
@@ -130,8 +128,7 @@
   // Acts as a common constructor that's called by the above public
   // constructors.
   CaptureModeMenuGroup(Delegate* delegate,
-                       std::unique_ptr<CaptureModeMenuHeader> menu_header,
-                       const gfx::Insets& inside_border_insets);
+                       std::unique_ptr<CaptureModeMenuHeader> menu_header);
 
   // Returns the option whose ID is |option_id|, and nullptr if no such option
   // exists.
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc
index ae969dc..b5d426a 100644
--- a/ash/capture_mode/capture_mode_session.cc
+++ b/ash/capture_mode/capture_mode_session.cc
@@ -14,14 +14,15 @@
 #include "ash/capture_mode/capture_mode_camera_preview_view.h"
 #include "ash/capture_mode/capture_mode_constants.h"
 #include "ash/capture_mode/capture_mode_controller.h"
+#include "ash/capture_mode/capture_mode_menu_group.h"
 #include "ash/capture_mode/capture_mode_session_focus_cycler.h"
 #include "ash/capture_mode/capture_mode_settings_view.h"
 #include "ash/capture_mode/capture_mode_type_view.h"
 #include "ash/capture_mode/capture_mode_util.h"
 #include "ash/capture_mode/capture_window_observer.h"
 #include "ash/capture_mode/folder_selection_dialog_controller.h"
-#include "ash/capture_mode/recording_type_menu_view.h"
 #include "ash/capture_mode/user_nudge_controller.h"
+#include "ash/constants/ash_features.h"
 #include "ash/display/mouse_cursor_event_filter.h"
 #include "ash/display/screen_orientation_controller.h"
 #include "ash/display/window_tree_host_manager.h"
@@ -29,17 +30,18 @@
 #include "ash/projector/projector_controller_impl.h"
 #include "ash/public/cpp/resources/grit/ash_public_unscaled_resources.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/root_window_controller.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_id.h"
+#include "ash/style/color_util.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_dimmer.h"
 #include "base/bind.h"
 #include "base/callback_helpers.h"
 #include "base/check.h"
-#include "base/functional/bind.h"
 #include "base/memory/ptr_util.h"
 #include "cc/paint/paint_flags.h"
 #include "ui/aura/client/aura_constants.h"
@@ -65,11 +67,13 @@
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/transform_util.h"
+#include "ui/gfx/paint_vector_icon.h"
 #include "ui/gfx/scoped_canvas.h"
 #include "ui/gfx/shadow_value.h"
 #include "ui/gfx/skia_paint_util.h"
 #include "ui/views/animation/animation_builder.h"
 #include "ui/views/background.h"
+#include "ui/views/controls/button/label_button.h"
 #include "ui/views/controls/label.h"
 #include "ui/views/widget/widget.h"
 #include "ui/wm/core/coordinate_conversion.h"
@@ -363,6 +367,13 @@
              widget->GetWindowBoundsInScreen());
 }
 
+// Returns the color provider for the native theme.
+ui::ColorProvider* GetColorProvider() {
+  auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
+  return ui::ColorProviderManager::Get().GetColorProviderFor(
+      native_theme->GetColorProviderKey(nullptr));
+}
+
 }  // namespace
 
 // -----------------------------------------------------------------------------
@@ -842,10 +853,6 @@
   }
 
   if (!capture_mode_settings_widget_) {
-    // Close the recording type menu if any. There can be only one menu visible
-    // at any time.
-    SetRecordingTypeMenuShown(false);
-
     auto* parent = GetParentContainer(current_root_);
     capture_mode_settings_widget_ = std::make_unique<views::Widget>();
     MaybeDismissUserNudgeForever();
@@ -900,21 +907,19 @@
   if (!capture_label_widget_->IsVisible())
     capture_label_widget_->Show();
 
-  DCHECK(capture_label_view_);
-  capture_label_view_->StartCountDown(std::move(countdown_finished_callback));
+  CaptureLabelView* label_view =
+      static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
+  label_view->StartCountDown(std::move(countdown_finished_callback));
   UpdateCaptureLabelWidgetBounds(CaptureLabelAnimation::kCountdownStart);
 
   UpdateCursor(display::Screen::GetScreen()->GetCursorScreenPoint(),
                /*is_touch=*/false);
 
-  // Fade out the capture bar, capture settings, recording type menu, and the
-  // capture toast if they exist.
+  // Fade out the capture bar, capture settings and capture toast if they exist.
   std::vector<ui::Layer*> layers_to_fade_out{
       capture_mode_bar_widget_->GetLayer()};
   if (capture_mode_settings_widget_)
     layers_to_fade_out.push_back(capture_mode_settings_widget_->GetLayer());
-  if (recording_type_menu_widget_)
-    layers_to_fade_out.push_back(recording_type_menu_widget_->GetLayer());
   if (auto* toast_layer = capture_toast_controller_.MaybeGetToastLayer())
     layers_to_fade_out.push_back(toast_layer);
 
@@ -960,8 +965,9 @@
   if (is_shutting_down_)
     return false;
 
-  DCHECK(capture_label_view_);
-  return capture_label_view_->IsInCountDownAnimation();
+  CaptureLabelView* label_view =
+      static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
+  return label_view->IsInCountDownAnimation();
 }
 
 void CaptureModeSession::OnCaptureFolderMayHaveChanged() {
@@ -1118,12 +1124,10 @@
       event->StopPropagation();
       *should_update_opacity_ptr = true;
 
-      // We only dismiss the settings / recording type menus or clear the focus
-      // on ESC key if the count down is not in progress.
+      // We only dismiss the settings menu or clear the focus on ESC key if the
+      // count down is not in progress.
       const bool is_in_count_down = IsInCountDownAnimation();
-      if (recording_type_menu_widget_ && !is_in_count_down)
-        SetRecordingTypeMenuShown(false);
-      else if (capture_mode_settings_widget_ && !is_in_count_down)
+      if (capture_mode_settings_widget_ && !is_in_count_down)
         SetSettingsMenuShown(false);
       else if (focus_cycler_->HasFocus() && !is_in_count_down)
         focus_cycler_->ClearFocus();
@@ -1254,7 +1258,11 @@
   DCHECK_EQ(parent->layer(), layer()->parent());
   layer()->SetBounds(parent->bounds());
 
+  // We need to update the capture bar bounds first and then settings bounds.
+  // The sequence matters here since settings bounds depend on capture bar
+  // bounds.
   RefreshBarWidgetBounds();
+  MaybeUpdateSettingsBounds();
 
   // Only need to update the camera preview's bounds if the capture source is
   // `kFullscreen`, since `ClampCaptureRegionToRootWindowSize` will take care of
@@ -1342,28 +1350,26 @@
 
   // If the current mouse event is on capture label button, and capture label
   // button can handle the event, show the hand mouse cursor.
-  DCHECK(capture_label_view_);
   const bool is_event_on_capture_button =
       capture_label_widget_->GetWindowBoundsInScreen().Contains(
           location_in_screen) &&
-      capture_label_view_->ShouldHandleEvent();
+      static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView())
+          ->ShouldHandleEvent();
   if (is_event_on_capture_button) {
     cursor_setter_->UpdateCursor(ui::mojom::CursorType::kHand);
     return;
   }
 
-  // As long as the settings menu, or the recording type menu are open, a
-  // pointer cursor should be used as long as the cursor is not on top of the
-  // capture button, since clicking anywhere outside the bounds of either of
-  // them (the menus or the clickable capture button) will dismiss the menus.
-  // Also if the event is on the bar, a pointer will also be used, as long as
-  // the bar is visible.
+  // As long as the settings menu is open, a pointer cursor should be used as
+  // long as the cursor is not on top of the capture button, since clicking
+  // anywhere outside the bounds of either of them (the menu or the clickable
+  // capture button) will dismiss the menu. Also if the event is on the bar, a
+  // pointer will also be used, as long as the bar is visible.
   const bool is_event_on_capture_bar =
       capture_mode_bar_widget_->GetLayer()->GetTargetOpacity() &&
       capture_mode_bar_widget_->GetWindowBoundsInScreen().Contains(
           location_in_screen);
-  if (capture_mode_settings_widget_ || is_event_on_capture_bar ||
-      recording_type_menu_widget_) {
+  if (capture_mode_settings_widget_ || is_event_on_capture_bar) {
     cursor_setter_->UpdateCursor(ui::mojom::CursorType::kPointer);
     return;
   }
@@ -1435,8 +1441,6 @@
 
   const bool is_settings_visible = capture_mode_settings_widget_ &&
                                    capture_mode_settings_widget_->IsVisible();
-  const bool is_recording_type_menu_visible =
-      recording_type_menu_widget_ && recording_type_menu_widget_->IsVisible();
   gfx::Rect capture_region = controller_->user_capture_region();
   wm::ConvertRectToScreen(current_root_, &capture_region);
 
@@ -1488,8 +1492,7 @@
     }
 
     if (widget == capture_label_widget_.get() &&
-        (is_cursor_on_top_of_widget || focus_cycler_->CaptureLabelFocused() ||
-         is_recording_type_menu_visible)) {
+        (is_cursor_on_top_of_widget || focus_cycler_->CaptureLabelFocused())) {
       continue;
     }
 
@@ -1516,8 +1519,8 @@
   DCHECK(!controller_->is_recording_in_progress());
 
   // If settings menu is shown at the beginning of drag, we should close it.
-  SetSettingsMenuShown(false);
-  SetRecordingTypeMenuShown(false);
+  if (capture_mode_settings_widget_)
+    SetSettingsMenuShown(false);
 
   // Hide capture UIs while dragging camera preview.
   HideAllUis();
@@ -1589,8 +1592,6 @@
   result.push_back(capture_mode_bar_widget_.get());
   if (capture_label_widget_)
     result.push_back(capture_label_widget_.get());
-  if (recording_type_menu_widget_)
-    result.push_back(recording_type_menu_widget_.get());
   if (capture_mode_settings_widget_)
     result.push_back(capture_mode_settings_widget_.get());
   if (dimensions_label_widget_)
@@ -1659,12 +1660,10 @@
 
 void CaptureModeSession::RefreshBarWidgetBounds() {
   DCHECK(capture_mode_bar_widget_);
-  // We need to update the capture bar bounds first and then settings bounds.
-  // The sequence matters here since settings bounds depend on capture bar
-  // bounds.
   capture_mode_bar_widget_->SetBounds(
       CaptureModeBarView::GetBounds(current_root_, is_in_projector_mode_));
-  MaybeUpdateSettingsBounds();
+  auto* parent = GetParentContainer(current_root_);
+  parent->StackChildAtTop(capture_mode_bar_widget_->GetNativeWindow());
   if (user_nudge_controller_)
     user_nudge_controller_->Reposition();
   capture_toast_controller_.MaybeRepositionCaptureToast();
@@ -1695,11 +1694,6 @@
   controller_->PerformCapture();  // `this` can be deleted after this.
 }
 
-void CaptureModeSession::OnRecordingTypeDropDownButtonPressed() {
-  SetRecordingTypeMenuShown(!recording_type_menu_widget_ ||
-                            !recording_type_menu_widget_->IsVisible());
-}
-
 gfx::Rect CaptureModeSession::GetSelectedWindowBounds() const {
   auto* window = GetSelectedWindow();
   return window ? window->bounds() : gfx::Rect();
@@ -1729,8 +1723,6 @@
     widget_in_order.emplace_back(capture_label_widget_.get());
   if (capture_mode_bar_widget_)
     widget_in_order.emplace_back(capture_mode_bar_widget_.get());
-  if (recording_type_menu_widget_)
-    widget_in_order.emplace_back(recording_type_menu_widget_.get());
   if (capture_mode_settings_widget_)
     widget_in_order.emplace_back(capture_mode_settings_widget_.get());
 
@@ -1773,8 +1765,7 @@
   const float dsf = canvas->UndoDeviceScaleFactor();
   region = gfx::ScaleToEnclosingRect(region, dsf);
 
-  const auto* color_provider =
-      capture_mode_util::GetColorProviderForNativeTheme();
+  const auto* color_provider = GetColorProvider();
 
   if (!adjustable_region) {
     canvas->FillRect(region, SK_ColorTRANSPARENT, SkBlendMode::kClear);
@@ -1954,17 +1945,9 @@
   if (ShouldCaptureLabelHandleEvent(event_target))
     return;
 
-  // Let the recording type menu handle its events if any.
-  if (capture_mode_util::IsEventTargetedOnWidget(
-          *event, recording_type_menu_widget_.get())) {
-    return;
-  }
-
   // Also allow events that target the settings menu (if present) to go through.
-  if (capture_mode_util::IsEventTargetedOnWidget(
-          *event, capture_mode_settings_widget_.get())) {
+  if (IsEventTargetedOnSettingsMenu(*event))
     return;
-  }
 
   // Here we know that the event doesn't target the settings menu, so if it's a
   // press event, we will use it to dismiss the settings menu, unless it's on
@@ -1985,17 +1968,6 @@
     SetSettingsMenuShown(/*shown=*/false);
   }
 
-  // Similar to the above, we want a press event that is outside the recording
-  // type menu to close it, unless it is on on the drop down menu button if any.
-  const bool should_close_recording_type_menu =
-      is_press_event &&
-      !IsPointOnRecordingTypeDropDownButton(screen_location) &&
-      recording_type_menu_widget_;
-  if (should_close_recording_type_menu) {
-    ignore_located_events_ = true;
-    SetRecordingTypeMenuShown(false);
-  }
-
   const bool old_ignore_located_events = ignore_located_events_;
   if (ignore_located_events_) {
     if (is_release_event)
@@ -2003,16 +1975,13 @@
   }
 
   // Events targeting the capture bar should also go through.
-  if (capture_mode_util::IsEventTargetedOnWidget(
-          *event, capture_mode_bar_widget_.get())) {
+  if (IsEventTargetedOnCaptureBar(*event))
     return;
-  }
 
   event->SetHandled();
   event->StopPropagation();
 
-  if (should_close_settings || old_ignore_located_events ||
-      should_close_recording_type_menu) {
+  if (should_close_settings || old_ignore_located_events) {
     // Note that these ignored events have already been consumed above.
     return;
   }
@@ -2412,26 +2381,18 @@
     auto* parent = GetParentContainer(current_root_);
     capture_label_widget_->Init(
         CreateWidgetParams(parent, gfx::Rect(), "CaptureLabel"));
-    capture_label_view_ = capture_label_widget_->SetContentsView(
-        std::make_unique<CaptureLabelView>(
-            this,
-            base::BindRepeating(&CaptureModeSession::DoPerformCapture,
-                                base::Unretained(this)),
-            base::BindRepeating(
-                &CaptureModeSession::OnRecordingTypeDropDownButtonPressed,
-                base::Unretained(this))));
+    capture_label_widget_->SetContentsView(std::make_unique<CaptureLabelView>(
+        this, base::BindRepeating(&CaptureModeSession::DoPerformCapture,
+                                  base::Unretained(this))));
     capture_label_widget_->GetNativeWindow()->SetTitle(
         l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_A11Y_TITLE));
     capture_label_widget_->Show();
   }
 
-  // Note that the order here matters. The bounds of the recording type menu
-  // widget is always relative to the bounds of the `capture_label_widget_`.
-  // Thus, the latter must be updated before the former. Also, the menu may need
-  // to close if the `label_view` becomes not interactable.
-  capture_label_view_->UpdateIconAndText();
+  CaptureLabelView* label_view =
+      static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
+  label_view->UpdateIconAndText();
   UpdateCaptureLabelWidgetBounds(animation_type);
-  MaybeUpdateRecordingTypeMenu();
 
   focus_cycler_->OnCaptureLabelWidgetUpdated();
 }
@@ -2514,9 +2475,10 @@
 
 gfx::Rect CaptureModeSession::CalculateCaptureLabelWidgetBounds() {
   DCHECK(capture_label_widget_);
-  DCHECK(capture_label_view_);
+  CaptureLabelView* label_view =
+      static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
 
-  const gfx::Size preferred_size = capture_label_view_->GetPreferredSize();
+  const gfx::Size preferred_size = label_view->GetPreferredSize();
   const gfx::Rect capture_bar_bounds =
       capture_mode_bar_widget_->GetNativeWindow()->bounds();
 
@@ -2619,7 +2581,7 @@
   // away from one of the edges of the selected window.
   if (source == CaptureModeSource::kRegion && !is_selecting_region_ &&
       !capture_region.IsEmpty()) {
-    if (capture_label_view_->IsInCountDownAnimation()) {
+    if (label_view->IsInCountDownAnimation()) {
       // If countdown starts, calculate the bounds based on the old capture
       // label's position, otherwise, since the countdown label bounds is
       // smaller than the label bounds and may fit into the capture region even
@@ -2648,8 +2610,9 @@
     return false;
   }
 
-  DCHECK(capture_label_view_);
-  return capture_label_view_->ShouldHandleEvent();
+  CaptureLabelView* label_view =
+      static_cast<CaptureLabelView*>(capture_label_widget_->GetContentsView());
+  return label_view->ShouldHandleEvent();
 }
 
 void CaptureModeSession::MaybeChangeRoot(aura::Window* new_root) {
@@ -2691,11 +2654,8 @@
   UpdateCaptureRegion(gfx::Rect(), /*is_resizing=*/false, /*by_user=*/false);
 
   UpdateRootWindowDimmers();
-  MaybeReparentCameraPreviewWidget();
 
-  // Changing the root window may require updating the stacking order on the new
-  // display.
-  RefreshStackingOrder();
+  MaybeReparentCameraPreviewWidget();
 }
 
 void CaptureModeSession::UpdateRootWindowDimmers() {
@@ -2848,69 +2808,18 @@
   }
 }
 
-void CaptureModeSession::SetRecordingTypeMenuShown(bool shown) {
-  if (!shown) {
-    recording_type_menu_widget_.reset();
-    return;
-  }
-
-  if (!recording_type_menu_widget_) {
-    DCHECK(features::IsGifRecordingEnabled());
-    DCHECK(capture_label_widget_);
-    DCHECK(capture_label_widget_->IsVisible());
-
-    // Close the settings widget if any. Only one menu at a time can be visible.
-    SetSettingsMenuShown(false);
-
-    auto* parent = GetParentContainer(current_root_);
-    recording_type_menu_widget_ = std::make_unique<views::Widget>();
-    MaybeDismissUserNudgeForever();
-    capture_toast_controller_.DismissCurrentToastIfAny();
-
-    recording_type_menu_widget_->Init(CreateWidgetParams(
-        parent,
-        RecordingTypeMenuView::GetIdealScreenBounds(
-            capture_label_widget_->GetWindowBoundsInScreen()),
-        "RecordingTypeMenuWidget"));
-    recording_type_menu_widget_->SetContentsView(
-        std::make_unique<RecordingTypeMenuView>());
-
-    auto* menu_window = recording_type_menu_widget_->GetNativeWindow();
-    parent->StackChildAtTop(menu_window);
-
-    menu_window->SetTitle(l10n_util::GetStringUTF16(
-        IDS_ASH_SCREEN_CAPTURE_RECORDING_TYPE_MENU_A11Y_TITLE));
-  }
-
-  recording_type_menu_widget_->Show();
+bool CaptureModeSession::IsEventTargetedOnCaptureBar(
+    const ui::LocatedEvent& event) const {
+  DCHECK(capture_mode_bar_widget_);
+  auto* target = static_cast<aura::Window*>(event.target());
+  return capture_mode_bar_widget_->GetNativeWindow()->Contains(target);
 }
 
-bool CaptureModeSession::IsPointOnRecordingTypeDropDownButton(
-    const gfx::Point& screen_location) const {
-  if (!capture_label_widget_ || !capture_label_widget_->IsVisible())
-    return false;
-
-  DCHECK(capture_label_view_);
-  return capture_label_view_->IsPointOnRecordingTypeDropDownButton(
-      screen_location);
-}
-
-void CaptureModeSession::MaybeUpdateRecordingTypeMenu() {
-  if (!recording_type_menu_widget_)
-    return;
-
-  // If the the drop down button becomes hidden, the recording type menu widget
-  // should also hide.
-  if (!capture_label_widget_ ||
-      !capture_label_view_->IsRecordingTypeDropDownButtonVisible()) {
-    SetRecordingTypeMenuShown(false);
-    return;
-  }
-
-  recording_type_menu_widget_->SetBounds(
-      RecordingTypeMenuView::GetIdealScreenBounds(
-          capture_label_widget_->GetWindowBoundsInScreen(),
-          recording_type_menu_widget_->GetContentsView()));
+bool CaptureModeSession::IsEventTargetedOnSettingsMenu(
+    const ui::LocatedEvent& event) const {
+  auto* target = static_cast<aura::Window*>(event.target());
+  return capture_mode_settings_widget_ &&
+         capture_mode_settings_widget_->GetNativeWindow()->Contains(target);
 }
 
 }  // namespace ash
diff --git a/ash/capture_mode/capture_mode_session.h b/ash/capture_mode/capture_mode_session.h
index 51f599e9e..b3febe7 100644
--- a/ash/capture_mode/capture_mode_session.h
+++ b/ash/capture_mode/capture_mode_session.h
@@ -10,7 +10,6 @@
 
 #include "ash/accessibility/magnifier/magnifier_glass.h"
 #include "ash/ash_export.h"
-#include "ash/capture_mode/capture_label_view.h"
 #include "ash/capture_mode/capture_mode_toast_controller.h"
 #include "ash/capture_mode/capture_mode_types.h"
 #include "ash/capture_mode/folder_selection_dialog_controller.h"
@@ -295,10 +294,6 @@
   // record button in the capture label view.
   void DoPerformCapture();
 
-  // Called when the drop-down button in the `capture_label_widget_` is pressed
-  // which toggles the recording type menu on and off.
-  void OnRecordingTypeDropDownButtonPressed();
-
   // Gets the bounds of current window selected for |kWindow| capture source.
   gfx::Rect GetSelectedWindowBounds() const;
 
@@ -425,19 +420,12 @@
   // camera preview's bounds and visibility.
   void MaybeUpdateCameraPreviewBounds();
 
-  // Creates or distroys the recording type menu widget based on the given
-  // `shown` value.
-  void SetRecordingTypeMenuShown(bool shown);
+  // Returns true if the given `event` is targeted on the capture bar.
+  bool IsEventTargetedOnCaptureBar(const ui::LocatedEvent& event) const;
 
-  // Returns true if the given `screen_location` is on the drop down button in
-  // the `capture_label_widget_` which when clicked opens the recording type
-  // menu.
-  bool IsPointOnRecordingTypeDropDownButton(
-      const gfx::Point& screen_location) const;
-
-  // Updates the availability or bounds of the recording type menu widget
-  // according to the current state.
-  void MaybeUpdateRecordingTypeMenu();
+  // Returns true if the given `event` is targeted on the setting menu if it
+  // exists.
+  bool IsEventTargetedOnSettingsMenu(const ui::LocatedEvent& event) const;
 
   CaptureModeController* const controller_;
 
@@ -466,11 +454,6 @@
   // starting capturing, the widget will transform into a 3-second countdown
   // timer.
   views::UniqueWidgetPtr capture_label_widget_;
-  CaptureLabelView* capture_label_view_ = nullptr;
-
-  // Widget that hosts the recording type menu, from which the user can pick the
-  // desired recording format type.
-  views::UniqueWidgetPtr recording_type_menu_widget_;
 
   // Magnifier glass used during a region capture session.
   MagnifierGlass magnifier_glass_;
diff --git a/ash/capture_mode/capture_mode_session_test_api.cc b/ash/capture_mode/capture_mode_session_test_api.cc
index 3bb8a38..669520c 100644
--- a/ash/capture_mode/capture_mode_session_test_api.cc
+++ b/ash/capture_mode/capture_mode_session_test_api.cc
@@ -4,22 +4,13 @@
 
 #include "ash/capture_mode/capture_mode_session_test_api.h"
 
-#include "ash/capture_mode/capture_mode_controller.h"
 #include "ash/capture_mode/capture_mode_session.h"
 
 namespace ash {
 
-CaptureModeSessionTestApi::CaptureModeSessionTestApi()
-    : session_(CaptureModeController::Get()->capture_mode_session()) {
-  DCHECK(CaptureModeController::Get()->IsActive());
-  DCHECK(session_);
-}
-
 CaptureModeSessionTestApi::CaptureModeSessionTestApi(
     CaptureModeSession* session)
-    : session_(session) {
-  DCHECK(session_);
-}
+    : session_(session) {}
 
 CaptureModeBarView* CaptureModeSessionTestApi::GetCaptureModeBarView() {
   return session_->capture_mode_bar_view_;
@@ -30,10 +21,6 @@
   return session_->capture_mode_settings_view_;
 }
 
-CaptureLabelView* CaptureModeSessionTestApi::GetCaptureLabelView() {
-  return session_->capture_label_view_;
-}
-
 views::Widget* CaptureModeSessionTestApi::GetCaptureModeSettingsWidget() {
   return session_->capture_mode_settings_widget_.get();
 }
@@ -42,10 +29,6 @@
   return session_->capture_label_widget_.get();
 }
 
-views::Widget* CaptureModeSessionTestApi::GetRecordingTypeMenuWidget() {
-  return session_->recording_type_menu_widget_.get();
-}
-
 views::Widget* CaptureModeSessionTestApi::GetDimensionsLabelWidget() {
   return session_->dimensions_label_widget_.get();
 }
diff --git a/ash/capture_mode/capture_mode_session_test_api.h b/ash/capture_mode/capture_mode_session_test_api.h
index 883775d..02c4955 100644
--- a/ash/capture_mode/capture_mode_session_test_api.h
+++ b/ash/capture_mode/capture_mode_session_test_api.h
@@ -9,17 +9,15 @@
 
 namespace ash {
 
-class CaptureLabelView;
-class CaptureModeBarView;
 class CaptureModeSession;
+class CaptureModeBarView;
 class CaptureModeSettingsView;
-class MagnifierGlass;
 class UserNudgeController;
+class MagnifierGlass;
 
 // Wrapper for CaptureModeSession that exposes internal state to test functions.
 class CaptureModeSessionTestApi {
  public:
-  CaptureModeSessionTestApi();
   explicit CaptureModeSessionTestApi(CaptureModeSession* session);
 
   CaptureModeSessionTestApi(CaptureModeSessionTestApi&) = delete;
@@ -30,14 +28,10 @@
 
   CaptureModeSettingsView* GetCaptureModeSettingsView();
 
-  CaptureLabelView* GetCaptureLabelView();
-
   views::Widget* GetCaptureModeSettingsWidget();
 
   views::Widget* GetCaptureLabelWidget();
 
-  views::Widget* GetRecordingTypeMenuWidget();
-
   views::Widget* GetDimensionsLabelWidget();
 
   UserNudgeController* GetUserNudgeController();
diff --git a/ash/capture_mode/capture_mode_util.cc b/ash/capture_mode/capture_mode_util.cc
index de748cb..ab5cb231 100644
--- a/ash/capture_mode/capture_mode_util.cc
+++ b/ash/capture_mode/capture_mode_util.cc
@@ -78,6 +78,11 @@
              ->CalculateCameraPreviewTargetVisibility();
 }
 
+// Returns the local center point of the given `layer`.
+gfx::Point GetLocalCenterPoint(ui::Layer* layer) {
+  return gfx::Rect(layer->GetTargetBounds().size()).CenterPoint();
+}
+
 void FadeInWidget(views::Widget* widget,
                   const AnimationParams& animation_params) {
   DCHECK(widget);
@@ -350,10 +355,6 @@
   return play_view;
 }
 
-gfx::Point GetLocalCenterPoint(ui::Layer* layer) {
-  return gfx::Rect(layer->GetTargetBounds().size()).CenterPoint();
-}
-
 gfx::Transform GetScaleTransformAboutCenter(ui::Layer* layer, float scale) {
   return gfx::GetScaleTransform(GetLocalCenterPoint(layer), scale);
 }
@@ -512,16 +513,4 @@
   }
 }
 
-ui::ColorProvider* GetColorProviderForNativeTheme() {
-  auto* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
-  return ui::ColorProviderManager::Get().GetColorProviderFor(
-      native_theme->GetColorProviderKey(nullptr));
-}
-
-bool IsEventTargetedOnWidget(const ui::LocatedEvent& event,
-                             views::Widget* widget) {
-  auto* target = static_cast<aura::Window*>(event.target());
-  return widget && widget->GetNativeWindow()->Contains(target);
-}
-
 }  // namespace ash::capture_mode_util
diff --git a/ash/capture_mode/capture_mode_util.h b/ash/capture_mode/capture_mode_util.h
index 1a5e3abe..ff56fda 100644
--- a/ash/capture_mode/capture_mode_util.h
+++ b/ash/capture_mode/capture_mode_util.h
@@ -26,9 +26,7 @@
 }  // namespace gfx
 
 namespace ui {
-class ColorProvider;
 class Layer;
-class LocatedEvent;
 }  // namespace ui
 
 namespace views {
@@ -110,9 +108,6 @@
 // notification.
 std::unique_ptr<views::View> CreatePlayIconView();
 
-// Returns the local center point of the given `layer`.
-gfx::Point GetLocalCenterPoint(ui::Layer* layer);
-
 // Returns a transform that scales the given `layer` by the given `scale` factor
 // in both X and Y around its local center point.
 gfx::Transform GetScaleTransformAboutCenter(ui::Layer* layer, float scale);
@@ -194,14 +189,6 @@
 void MaybeUpdateCameraPrivacyIndicator(bool camera_on);
 void MaybeUpdateMicrophonePrivacyIndicator(bool mic_on);
 
-ui::ColorProvider* GetColorProviderForNativeTheme();
-
-// Returns true if the given located `event` is targeted on a window that is a
-// descendant of the given `widget`. Note that `widget` can be provided as null
-// if it no longer exists, in this case this function returns false.
-bool IsEventTargetedOnWidget(const ui::LocatedEvent& event,
-                             views::Widget* widget);
-
 }  // namespace capture_mode_util
 
 }  // namespace ash
diff --git a/ash/capture_mode/gif_recording_unittests.cc b/ash/capture_mode/gif_recording_unittests.cc
deleted file mode 100644
index 794153c..0000000
--- a/ash/capture_mode/gif_recording_unittests.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/capture_mode/capture_button_view.h"
-#include "ash/capture_mode/capture_label_view.h"
-#include "ash/capture_mode/capture_mode_bar_view.h"
-#include "ash/capture_mode/capture_mode_controller.h"
-#include "ash/capture_mode/capture_mode_session_test_api.h"
-#include "ash/capture_mode/capture_mode_test_util.h"
-#include "ash/capture_mode/capture_mode_types.h"
-#include "ash/constants/ash_features.h"
-#include "ash/public/cpp/capture_mode/capture_mode_test_api.h"
-#include "ash/style/icon_button.h"
-#include "ash/test/ash_test_base.h"
-#include "base/test/scoped_feature_list.h"
-#include "ui/events/keycodes/keyboard_codes_posix.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/vector2d.h"
-#include "ui/views/controls/button/image_button.h"
-#include "ui/views/widget/widget.h"
-
-namespace ash {
-
-class GifRecordingTest : public AshTestBase {
- public:
-  GifRecordingTest() : scoped_feature_list_(features::kGifRecording) {}
-  GifRecordingTest(const GifRecordingTest&) = delete;
-  GifRecordingTest& operator=(const GifRecordingTest&) = delete;
-  ~GifRecordingTest() override = default;
-
-  // AshTestBase:
-  void SetUp() override {
-    AshTestBase::SetUp();
-    CaptureModeController::Get()->SetUserCaptureRegion(gfx::Rect(200, 200),
-                                                       /*by_user=*/true);
-  }
-
-  CaptureModeController* StartRegionVideoCapture() {
-    return StartCaptureSession(CaptureModeSource::kRegion,
-                               CaptureModeType::kVideo);
-  }
-
-  CaptureLabelView* GetCaptureLabelView() {
-    return CaptureModeSessionTestApi().GetCaptureLabelView();
-  }
-
-  views::Widget* GetRecordingTypeMenuWidget() {
-    return CaptureModeSessionTestApi().GetRecordingTypeMenuWidget();
-  }
-
-  views::Widget* GetSettingsMenuWidget() {
-    return CaptureModeSessionTestApi().GetCaptureModeSettingsWidget();
-  }
-
-  void ClickOnDropDownButton() {
-    auto* label_view = GetCaptureLabelView();
-    ASSERT_TRUE(label_view->IsRecordingTypeDropDownButtonVisible());
-    CaptureButtonView* capture_button_container =
-        label_view->capture_button_container();
-    LeftClickOn(capture_button_container->drop_down_button());
-  }
-
-  void ClickOnSettingsButton() {
-    CaptureModeBarView* bar_view =
-        CaptureModeSessionTestApi().GetCaptureModeBarView();
-    LeftClickOn(bar_view->settings_button());
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-TEST_F(GifRecordingTest, DropDownButtonVisibility) {
-  // With region video recording, the drop down button should be visible.
-  auto* controller = StartRegionVideoCapture();
-  auto* label_view = GetCaptureLabelView();
-  EXPECT_TRUE(label_view->IsRecordingTypeDropDownButtonVisible());
-
-  // It should hide, once we switch to image recording, but the label view
-  // should remain interactable.
-  controller->SetType(CaptureModeType::kImage);
-  EXPECT_FALSE(label_view->IsRecordingTypeDropDownButtonVisible());
-  EXPECT_TRUE(label_view->IsViewInteractable());
-
-  // Switching to a fullscreen source, the label view becomes no longer
-  // interactable, and the drop down button remains hidden.
-  controller->SetSource(CaptureModeSource::kFullscreen);
-  EXPECT_FALSE(label_view->IsRecordingTypeDropDownButtonVisible());
-  EXPECT_FALSE(label_view->IsViewInteractable());
-
-  // Even when we switch back to video recording.
-  controller->SetType(CaptureModeType::kVideo);
-  EXPECT_FALSE(label_view->IsRecordingTypeDropDownButtonVisible());
-  EXPECT_FALSE(label_view->IsViewInteractable());
-
-  // Only region recording in video mode, that the label view is interactable,
-  // and the button is visible.
-  controller->SetSource(CaptureModeSource::kRegion);
-  EXPECT_TRUE(label_view->IsRecordingTypeDropDownButtonVisible());
-  EXPECT_TRUE(label_view->IsViewInteractable());
-}
-
-TEST_F(GifRecordingTest, RecordingTypeMenuCreation) {
-  // The drop down button acts as a toggle.
-  StartRegionVideoCapture();
-  ClickOnDropDownButton();
-  EXPECT_TRUE(GetRecordingTypeMenuWidget());
-  ClickOnDropDownButton();
-  EXPECT_FALSE(GetRecordingTypeMenuWidget());
-
-  // The settings menu and the recording type menu are mutually exclusive,
-  // opening one closes the other.
-  ClickOnSettingsButton();
-  EXPECT_TRUE(GetSettingsMenuWidget());
-  ClickOnDropDownButton();
-  EXPECT_TRUE(GetRecordingTypeMenuWidget());
-  EXPECT_FALSE(GetSettingsMenuWidget());
-  ClickOnSettingsButton();
-  EXPECT_TRUE(GetSettingsMenuWidget());
-  EXPECT_FALSE(GetRecordingTypeMenuWidget());
-}
-
-TEST_F(GifRecordingTest, EscKeyClosesMenu) {
-  // Hitting the ESC key closes the recording type menu, but the session remains
-  // active.
-  auto* controller = StartRegionVideoCapture();
-  ClickOnDropDownButton();
-  EXPECT_TRUE(GetRecordingTypeMenuWidget());
-  PressAndReleaseKey(ui::VKEY_ESCAPE);
-  EXPECT_FALSE(GetRecordingTypeMenuWidget());
-  EXPECT_TRUE(controller->IsActive());
-}
-
-TEST_F(GifRecordingTest, EnterKeyHidesMenuAndStartsCountDown) {
-  StartRegionVideoCapture();
-  ClickOnDropDownButton();
-  auto* recording_type_menu = GetRecordingTypeMenuWidget();
-  EXPECT_TRUE(recording_type_menu);
-
-  // Pressing the ENTER key starts the recording count down, at which point, the
-  // menu remains open but fades out to an opacity of 0.
-  PressAndReleaseKey(ui::VKEY_RETURN);
-  EXPECT_TRUE(CaptureModeTestApi().IsInCountDownAnimation());
-  ASSERT_EQ(recording_type_menu, GetRecordingTypeMenuWidget());
-  EXPECT_FLOAT_EQ(recording_type_menu->GetLayer()->GetTargetOpacity(), 0);
-}
-
-TEST_F(GifRecordingTest, ClickingOutsideClosesMenu) {
-  auto* controller = StartRegionVideoCapture();
-  ClickOnDropDownButton();
-  EXPECT_TRUE(GetRecordingTypeMenuWidget());
-
-  // Clicking outside the menu widget should close it, but the region should not
-  // change.
-  const auto region = controller->user_capture_region();
-  auto* generator = GetEventGenerator();
-  generator->MoveMouseTo(region.bottom_right() + gfx::Vector2d(10, 10));
-  generator->ClickLeftButton();
-  EXPECT_FALSE(GetRecordingTypeMenuWidget());
-  EXPECT_EQ(region, controller->user_capture_region());
-}
-
-}  // namespace ash
diff --git a/ash/capture_mode/pointer_highlight_layer.cc b/ash/capture_mode/pointer_highlight_layer.cc
deleted file mode 100644
index 66673b2..0000000
--- a/ash/capture_mode/pointer_highlight_layer.cc
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/capture_mode/pointer_highlight_layer.h"
-
-#include "ash/capture_mode/capture_mode_util.h"
-#include "ash/style/dark_light_mode_controller_impl.h"
-#include "base/check.h"
-#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
-#include "ui/color/color_provider.h"
-#include "ui/compositor/layer.h"
-#include "ui/compositor/layer_type.h"
-#include "ui/compositor/paint_recorder.h"
-#include "ui/events/event.h"
-#include "ui/gfx/geometry/dip_util.h"
-#include "ui/gfx/geometry/point_f.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/scoped_canvas.h"
-
-namespace ash {
-
-namespace {
-
-constexpr int kHighlightLayerRadius = 36;
-constexpr float kHighlightLayerInitialOpacity = 1.f;
-constexpr float kLightModeBorderOpacityScaleFactor = 0.8f;
-const int kHighlightStrokeWidth = 2;
-constexpr int kFillsRadius = kHighlightLayerRadius - kHighlightStrokeWidth;
-
-// Calculates the layer bounds based on the event location in the coordinates of
-// the window being recorded.
-gfx::Rect CalculateHighlightLayerBounds(
-    const gfx::PointF& event_location_in_window) {
-  return gfx::Rect(event_location_in_window.x() - kHighlightLayerRadius,
-                   event_location_in_window.y() - kHighlightLayerRadius,
-                   kHighlightLayerRadius * 2, kHighlightLayerRadius * 2);
-}
-
-// Returns the color used for the highlight layer affordance and border.
-SkColor GetColor() {
-  return capture_mode_util::GetColorProviderForNativeTheme()->GetColor(
-      cros_tokens::kCrosSysOnSurface);
-}
-
-}  // namespace
-
-PointerHighlightLayer::PointerHighlightLayer(
-    const gfx::PointF& event_location_in_window,
-    ui::Layer* parent_layer) {
-  SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED));
-  layer()->SetFillsBoundsOpaquely(false);
-  layer()->SetBounds(CalculateHighlightLayerBounds(event_location_in_window));
-  layer()->SetRoundedCornerRadius(gfx::RoundedCornersF(kHighlightLayerRadius));
-  layer()->SetOpacity(kHighlightLayerInitialOpacity);
-  layer()->set_delegate(this);
-  layer()->SetName("PointerHighlightLayer");
-
-  DCHECK(parent_layer);
-  parent_layer->Add(layer());
-  parent_layer->StackAtTop(layer());
-}
-
-PointerHighlightLayer::~PointerHighlightLayer() = default;
-
-void PointerHighlightLayer::OnPaintLayer(const ui::PaintContext& context) {
-  ui::PaintRecorder recorder(context, layer()->size());
-  gfx::ScopedCanvas scoped_canvas(recorder.canvas());
-  const float dsf = recorder.canvas()->UndoDeviceScaleFactor();
-  const float scaled_highlight_radius = dsf * kHighlightLayerRadius;
-  const float scaled_fills_radius = dsf * kFillsRadius;
-  const gfx::PointF scaled_highlight_center = gfx::ConvertPointToPixels(
-      capture_mode_util::GetLocalCenterPoint(layer()), dsf);
-
-  cc::PaintFlags flags;
-  const SkColor color = GetColor();
-
-  // 50% opacity.
-  flags.setColor(SkColorSetA(color, 128));
-  flags.setAntiAlias(true);
-  flags.setStyle(cc::PaintFlags::kFill_Style);
-  recorder.canvas()->DrawCircle(scaled_highlight_center,
-                                scaled_highlight_radius, flags);
-
-  flags.setColor(
-      SkColorSetA(color, DarkLightModeControllerImpl::Get()->IsDarkModeEnabled()
-                             ? 255
-                             : 255 * kLightModeBorderOpacityScaleFactor));
-  flags.setStyle(cc::PaintFlags::kStroke_Style);
-  flags.setStrokeWidth(kHighlightStrokeWidth);
-  recorder.canvas()->DrawCircle(scaled_highlight_center, scaled_fills_radius,
-                                flags);
-}
-
-}  // namespace ash
\ No newline at end of file
diff --git a/ash/capture_mode/pointer_highlight_layer.h b/ash/capture_mode/pointer_highlight_layer.h
deleted file mode 100644
index 082c4b4..0000000
--- a/ash/capture_mode/pointer_highlight_layer.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_CAPTURE_MODE_POINTER_HIGHLIGHT_LAYER_H_
-#define ASH_CAPTURE_MODE_POINTER_HIGHLIGHT_LAYER_H_
-
-#include "ui/aura/window.h"
-#include "ui/compositor/layer_delegate.h"
-#include "ui/compositor/layer_owner.h"
-#include "ui/compositor/paint_context.h"
-#include "ui/events/event.h"
-
-namespace gfx {
-class PointF;
-}  // namespace gfx
-
-namespace ash {
-
-// `PointerHighlightLayer` is a `LayerOwner` that owns a texture layer that is
-// added as a descendant of the window being recorded and on top of it (z-order)
-// such that it can be captured with it. This layer is used to highlight the
-// mouse or touch press events by painting a ring centered at the event
-// location. `PointerHighlightLayer` is owned by
-// `CaptureModeDemoToolsController` which will be created when animation starts
-// and destroyed when animation ends.
-class PointerHighlightLayer : public ui::LayerOwner, public ui::LayerDelegate {
- public:
-  PointerHighlightLayer(const gfx::PointF& event_location_in_window,
-                        ui::Layer* parent_layer);
-  PointerHighlightLayer(const PointerHighlightLayer&) = delete;
-  PointerHighlightLayer& operator=(const PointerHighlightLayer&) = delete;
-  ~PointerHighlightLayer() override;
-
-  // ui::LayerDelegate:
-  void OnPaintLayer(const ui::PaintContext& context) override;
-  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
-                                  float new_device_scale_factor) override {}
-};
-
-}  // namespace ash
-
-#endif  // ASH_CAPTURE_MODE_POINTER_HIGHLIGHT_LAYER_H_
\ No newline at end of file
diff --git a/ash/capture_mode/recording_type_menu_view.cc b/ash/capture_mode/recording_type_menu_view.cc
deleted file mode 100644
index 1b8985a..0000000
--- a/ash/capture_mode/recording_type_menu_view.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/capture_mode/recording_type_menu_view.h"
-
-#include "ash/public/cpp/style/color_provider.h"
-#include "ash/resources/vector_icons/vector_icons.h"
-#include "ash/strings/grit/ash_strings.h"
-#include "ash/style/ash_color_id.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/compositor/layer.h"
-#include "ui/gfx/geometry/point.h"
-#include "ui/gfx/geometry/rect.h"
-#include "ui/gfx/geometry/rounded_corners_f.h"
-#include "ui/views/background.h"
-
-namespace ash {
-
-namespace {
-
-// The IDs of the options representing the available recording formats.
-enum RecordingTypeOption {
-  kWebM = 0,
-  kGif,
-};
-
-// The padding around the menu options.
-constexpr auto kMenuPadding = gfx::Insets::VH(12, 0);
-
-// The vertical space between the two nearest edges of the capture label widget
-// and the recording type menu widget.
-constexpr int kYOffsetFromLabelWidget = 8;
-
-constexpr int kMinimumWidth = 184;
-constexpr gfx::Size kIdealSize{kMinimumWidth, 96};
-
-constexpr gfx::RoundedCornersF kBorderRadius{12.f};
-
-// Gets the ideal size of the widget hosting the `RecordingTypeMenuView` either
-// from the preferred size of `contents_view` (if given), or the default size.
-gfx::Size GetIdealSize(views::View* contents_view) {
-  gfx::Size size =
-      contents_view ? contents_view->GetPreferredSize() : kIdealSize;
-  if (size.width() < kMinimumWidth)
-    size.set_width(kMinimumWidth);
-  return size;
-}
-
-}  // namespace
-
-RecordingTypeMenuView::RecordingTypeMenuView()
-    : CaptureModeMenuGroup(this, kMenuPadding) {
-  SetPaintToLayer();
-  SetBackground(views::CreateThemedSolidBackground(kColorAshShieldAndBase80));
-  layer()->SetFillsBoundsOpaquely(false);
-  layer()->SetRoundedCornerRadius(kBorderRadius);
-  layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
-  layer()->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);
-
-  AddOption(
-      &kCaptureModeVideoIcon,
-      l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_LABEL_VIDEO_RECORD),
-      RecordingTypeOption::kWebM);
-  AddOption(&kCaptureGifIcon,
-            l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_LABEL_GIF_RECORD),
-            RecordingTypeOption::kGif);
-}
-
-// static
-gfx::Rect RecordingTypeMenuView::GetIdealScreenBounds(
-    const gfx::Rect& capture_label_widget_screen_bounds,
-    views::View* contents_view) {
-  const auto size = GetIdealSize(contents_view);
-  const auto bottom_center = capture_label_widget_screen_bounds.bottom_center();
-  const int y = bottom_center.y() + kYOffsetFromLabelWidget;
-  const int x = bottom_center.x() - (size.width() / 2);
-  return gfx::Rect(gfx::Point(x, y), size);
-}
-
-void RecordingTypeMenuView::OnOptionSelected(int option_id) const {}
-
-bool RecordingTypeMenuView::IsOptionChecked(int option_id) const {
-  return option_id == RecordingTypeOption::kWebM;
-}
-
-bool RecordingTypeMenuView::IsOptionEnabled(int option_id) const {
-  return true;
-}
-
-}  // namespace ash
diff --git a/ash/capture_mode/recording_type_menu_view.h b/ash/capture_mode/recording_type_menu_view.h
deleted file mode 100644
index 0831ccd..0000000
--- a/ash/capture_mode/recording_type_menu_view.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef ASH_CAPTURE_MODE_RECORDING_TYPE_MENU_VIEW_H_
-#define ASH_CAPTURE_MODE_RECORDING_TYPE_MENU_VIEW_H_
-
-#include "ash/capture_mode/capture_mode_menu_group.h"
-
-namespace gfx {
-class Rect;
-}  // namespace gfx
-
-namespace ash {
-
-// Defines a view that will be the contents view of the recording type menu
-// widget, from which users can pick the desired recording format.
-class RecordingTypeMenuView : public CaptureModeMenuGroup,
-                              public CaptureModeMenuGroup::Delegate {
- public:
-  RecordingTypeMenuView();
-  RecordingTypeMenuView(const RecordingTypeMenuView&) = delete;
-  RecordingTypeMenuView& operator=(const RecordingTypeMenuView&) = delete;
-  ~RecordingTypeMenuView() override = default;
-
-  // Returns the ideal bounds of the widget hosting this view, relative to the
-  // `capture_label_widget_screen_bounds` which hosts the drop down button that
-  // opens the recording type menu widget. If `contents_view` is provided, its
-  // preferred size will be used, otherwise, the default size will be used.
-  static gfx::Rect GetIdealScreenBounds(
-      const gfx::Rect& capture_label_widget_screen_bounds,
-      views::View* contents_view = nullptr);
-
-  // CaptureModeMenuGroup::Delegate:
-  void OnOptionSelected(int option_id) const override;
-  bool IsOptionChecked(int option_id) const override;
-  bool IsOptionEnabled(int option_id) const override;
-};
-
-}  // namespace ash
-
-#endif  // ASH_CAPTURE_MODE_RECORDING_TYPE_MENU_VIEW_H_
diff --git a/ash/capture_mode/video_recording_watcher.cc b/ash/capture_mode/video_recording_watcher.cc
index 4833f42..7c3633d 100644
--- a/ash/capture_mode/video_recording_watcher.cc
+++ b/ash/capture_mode/video_recording_watcher.cc
@@ -18,6 +18,7 @@
 #include "ash/projector/projector_controller_impl.h"
 #include "ash/shell.h"
 #include "ash/style/ash_color_id.h"
+#include "ash/style/ash_color_provider.h"
 #include "ash/wm/desks/desks_util.h"
 #include "ash/wm/mru_window_tracker.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
@@ -28,12 +29,12 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/aura/client/cursor_shape_client.h"
+#include "ui/aura/window.h"
 #include "ui/aura/window_tree_host.h"
 #include "ui/base/cursor/cursor.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/paint_recorder.h"
 #include "ui/display/screen.h"
-#include "ui/events/types/event_type.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/point.h"
@@ -526,8 +527,6 @@
 }
 
 void VideoRecordingWatcher::OnMouseEvent(ui::MouseEvent* event) {
-  const gfx::PointF location_in_window =
-      GetEventLocationInWindow(window_being_recorded_, *event);
   switch (event->type()) {
     case ui::ET_MOUSEWHEEL:
     case ui::ET_MOUSE_CAPTURE_CHANGED:
@@ -537,18 +536,17 @@
       auto* camera_preview_view = GetCameraPreviewView();
       if (camera_preview_view)
         camera_preview_view->MaybeBlurFocus(*event);
-
-      if (demo_tools_controller_)
-        demo_tools_controller_->PerformMousePressAnimation(location_in_window);
     }
       [[fallthrough]];
     case ui::ET_MOUSE_RELEASED:
       // Pressed/released events are important, so we handle them immediately.
-      UpdateCursorOverlayNow(location_in_window);
+      UpdateCursorOverlayNow(
+          GetEventLocationInWindow(window_being_recorded_, *event));
       return;
 
     default:
-      UpdateOrThrottleCursorOverlay(location_in_window);
+      UpdateOrThrottleCursorOverlay(
+          GetEventLocationInWindow(window_being_recorded_, *event));
   }
 }
 
diff --git a/ash/capture_mode/video_recording_watcher.h b/ash/capture_mode/video_recording_watcher.h
index 4589ab0..2160b8f 100644
--- a/ash/capture_mode/video_recording_watcher.h
+++ b/ash/capture_mode/video_recording_watcher.h
@@ -149,7 +149,7 @@
 
   void SendThrottledWindowSizeChangedNowForTesting();
 
-  CaptureModeDemoToolsController* demo_tools_controller_for_testing() const {
+  CaptureModeDemoToolsController* demo_tools_controller_for_testing() {
     return demo_tools_controller_.get();
   }
 
diff --git a/ash/style/ash_color_id.h b/ash/style/ash_color_id.h
index 94c043ce..cbdcbda 100644
--- a/ash/style/ash_color_id.h
+++ b/ash/style/ash_color_id.h
@@ -121,7 +121,9 @@
   E_CPONLY(kColorAshIconPrimaryDisabledColor) \
   E_CPONLY(KColorAshTextDisabledColor) \
   /* Color for icon of the blocked bluetooth device */ \
-  E_CPONLY(kColorAshIconColorBlocked)
+  E_CPONLY(kColorAshIconColorBlocked)\
+  /* Color for icon in title of app streaming bubble */ \
+  E_CPONLY(kColorAshEcheIconColorStreaming)
 
 #include "ui/color/color_id_macros.inc"
 
diff --git a/ash/style/ash_color_mixer.cc b/ash/style/ash_color_mixer.cc
index 3db80d4..673777de 100644
--- a/ash/style/ash_color_mixer.cc
+++ b/ash/style/ash_color_mixer.cc
@@ -480,6 +480,8 @@
       ui::SetAlpha(cros_tokens::kCrosSysOnSurface, kDisabledColorOpacity);
 
   mixer[kColorAshIconColorBlocked] = {gfx::kGoogleGrey100};
+
+  mixer[kColorAshEcheIconColorStreaming] = {ui::ColorTransform(SK_ColorGREEN)};
 }
 
 }  // namespace ash
diff --git a/ash/system/eche/eche_tray.cc b/ash/system/eche/eche_tray.cc
index ffb374a1..0ecc68b 100644
--- a/ash/system/eche/eche_tray.cc
+++ b/ash/system/eche/eche_tray.cc
@@ -24,6 +24,7 @@
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
+#include "ash/style/ash_color_id.h"
 #include "ash/style/ash_color_provider.h"
 #include "ash/style/icon_button.h"
 #include "ash/system/eche/eche_icon_loading_indicator_view.h"
@@ -49,19 +50,23 @@
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/base/models/image_model.h"
 #include "ui/compositor/layer.h"
 #include "ui/events/event.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/event_target.h"
 #include "ui/events/keycodes/keyboard_codes_posix.h"
 #include "ui/events/types/event_type.h"
+#include "ui/gfx/canvas.h"
 #include "ui/gfx/geometry/insets.h"
+#include "ui/gfx/geometry/point.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/geometry/vector2d.h"
 #include "ui/gfx/image/image.h"
 #include "ui/gfx/image/image_skia_operations.h"
 #include "ui/gfx/paint_vector_icon.h"
+#include "ui/gfx/text_constants.h"
 #include "ui/gfx/vector_icon_types.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/border.h"
@@ -71,6 +76,7 @@
 #include "ui/views/controls/image_view.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/flex_layout.h"
+#include "ui/views/layout/layout_types.h"
 #include "ui/views/view.h"
 #include "ui/views/views_delegate.h"
 #include "url/gurl.h"
@@ -97,6 +103,13 @@
 
 constexpr auto kBubblePadding = gfx::Insets::VH(8, 8);
 
+constexpr gfx::Insets kAppStreamingTitleTextInset =
+    gfx::Insets::TLBR(0, 45, 0, 25);
+constexpr int kAppStreamingTitlTextWidth = 140;
+constexpr int kAppStreamingTitleTextFontSize = 14;
+constexpr int kAppStreamingTitleDotSize = 4;
+constexpr int kAppStreamingTitleSpacing = 4;
+
 constexpr float kDefaultAspectRatio = 16.0 / 9.0f;
 constexpr gfx::Size kDefaultBubbleSize(360, 360 * kDefaultAspectRatio);
 
@@ -165,6 +178,97 @@
   return button;
 }
 
+// Draws a dot with no shadow.
+class StatusDotView : public views::View {
+ public:
+  StatusDotView() = default;
+  StatusDotView(const StatusDotView&) = delete;
+  StatusDotView& operator=(const StatusDotView&) = delete;
+  ~StatusDotView() override = default;
+
+  // views::View:
+  void OnPaint(gfx::Canvas* canvas) override {
+    DCHECK_EQ(width(), height());
+    const float radius = width() / 2.0f;
+    const float scale = canvas->UndoDeviceScaleFactor();
+    gfx::PointF center = gfx::RectF(GetLocalBounds()).CenterPoint();
+    center.Scale(scale);
+
+    cc::PaintFlags flags;
+    flags.setColor(
+        GetColorProvider()->GetColor(kColorAshEcheIconColorStreaming));
+    flags.setAntiAlias(true);
+    canvas->DrawCircle(center, scale * radius, flags);
+  }
+
+  void OnThemeChanged() override {
+    views::View::OnThemeChanged();
+    SchedulePaint();
+  }
+};
+
+class AppStreamingTitleView : public views::View {
+ public:
+  explicit AppStreamingTitleView(const std::u16string& title) {
+    auto title_text = std::make_unique<views::Label>(title);
+    title_text->SetMultiLine(false);
+    title_text->SetAllowCharacterBreak(true);
+    title_text->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+    title_text->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
+    title_text->SetMaximumWidthSingleLine(kAppStreamingTitlTextWidth);
+
+    gfx::Font default_font;
+    gfx::Font text_font = default_font.Derive(
+        kAppStreamingTitleTextFontSize - default_font.GetFontSize(),
+        gfx::Font::NORMAL, gfx::Font::Weight::NORMAL);
+    gfx::FontList font_list(text_font);
+    title_text->SetFontList(font_list);
+
+    title_ = AddChildView(std::move(title_text));
+
+    icon_ = AddChildView(std::make_unique<StatusDotView>());
+    icon_->SetVisible(true);
+  }
+  ~AppStreamingTitleView() override = default;
+  AppStreamingTitleView(AppStreamingTitleView&) = delete;
+  AppStreamingTitleView operator=(AppStreamingTitleView&) = delete;
+
+  // views::View:
+  void Layout() override {
+    SetLayoutManager(std::make_unique<views::FlexLayout>())
+        ->SetCollapseMargins(false)
+        .SetMinimumCrossAxisSize(kHeaderHeight)
+        .SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
+
+    SetProperty(
+        views::kFlexBehaviorKey,
+        views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+                                 views::MaximumFlexSizeRule::kUnbounded,
+                                 /*adjust_height_for_width =*/true)
+            .WithWeight(1));
+
+    // Manually calculate the position where the status dot should be placed.
+    // Otherwise the status dot does not display. TODO(pushi): can we let layout
+    // manager handle this?
+    gfx::Rect rect(GetContentsBounds());
+    gfx::Rect title_bounds = gfx::Rect(rect);
+    title_bounds.Inset(kAppStreamingTitleTextInset);
+    title_bounds.ClampToCenteredSize(title_->GetPreferredSize());
+    title_->SetBoundsRect(title_bounds);
+
+    icon_->SetBounds(title_bounds.x() - kAppStreamingTitleDotSize -
+                         kAppStreamingTitleSpacing,
+                     title_bounds.y() + title_bounds.height() / 2 -
+                         kAppStreamingTitleDotSize / 2,
+                     kAppStreamingTitleDotSize, kAppStreamingTitleDotSize);
+  }
+  const char* GetClassName() const override { return "AppStreamingTitleView"; }
+
+ private:
+  StatusDotView* icon_ = nullptr;
+  views::Label* title_ = nullptr;
+};
+
 }  // namespace
 
 EcheTray::EventInterceptor::EventInterceptor(EcheTray* eche_tray)
@@ -603,20 +707,9 @@
                                        weak_factory_.GetWeakPtr()),
                    kEcheArrowBackIcon, IDS_APP_ACCNAME_BACK));
 
-  views::Label* title = header->AddChildView(std::make_unique<views::Label>(
-      l10n_util::GetStringFUTF16(ID_ASH_ECHE_APP_STREAMING_BUBBLE_TITLE,
-                                 phone_name),
-      views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_PRIMARY,
-      gfx::DirectionalityMode::DIRECTIONALITY_AS_URL));
-  title->SetMultiLine(false);
-  title->SetAllowCharacterBreak(true);
-  title->SetProperty(
-      views::kFlexBehaviorKey,
-      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
-                               views::MaximumFlexSizeRule::kUnbounded,
-                               /*adjust_height_for_width =*/true)
-          .WithWeight(1));
-  title->SetHorizontalAlignment(gfx::ALIGN_CENTER);
+  header->AddChildView(
+      std::make_unique<AppStreamingTitleView>(l10n_util::GetStringFUTF16(
+          ID_ASH_ECHE_APP_STREAMING_BUBBLE_TITLE, phone_name)));
 
   // Add minimize button
   minimize_button_ = header->AddChildView(CreateButton(
diff --git a/ash/touch/touch_selection_magnifier_runner_ash.cc b/ash/touch/touch_selection_magnifier_runner_ash.cc
index cf02de1..c48244b3 100644
--- a/ash/touch/touch_selection_magnifier_runner_ash.cc
+++ b/ash/touch/touch_selection_magnifier_runner_ash.cc
@@ -16,16 +16,16 @@
 
 namespace {
 
-constexpr float kMagnifierScale = 2.0f;
-
-constexpr gfx::RoundedCornersF kMagnifierRoundedCorners(20);
-
-constexpr gfx::Size kMagnifierLayerSize(100, 48);
-
-gfx::Rect GetBounds(const gfx::Point& point) {
-  return gfx::Rect(gfx::Point(point.x() - kMagnifierLayerSize.width() / 2,
-                              point.y() - kMagnifierLayerSize.height() / 2),
-                   kMagnifierLayerSize);
+// Gets the bounds of the magnifier when showing the specified point of
+// interest. `point_of_interest` and returned bounds are in root window
+// coordinates.
+gfx::Rect GetBounds(const gfx::Point& point_of_interest) {
+  const gfx::Size size = TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize;
+  const gfx::Point origin(
+      point_of_interest.x() - size.width() / 2,
+      point_of_interest.y() - size.height() / 2 +
+          TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset);
+  return gfx::Rect(origin, size);
 }
 
 // Returns the child container in `root` that should parent the magnifier layer.
@@ -90,6 +90,8 @@
   magnifier_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
   magnifier_layer_->SetBounds(GetBounds(gfx::ToRoundedPoint(position_in_root)));
   magnifier_layer_->SetBackgroundZoom(kMagnifierScale, 0);
+  magnifier_layer_->SetBackgroundOffset(
+      gfx::Point(0, kMagnifierVerticalOffset));
   magnifier_layer_->SetFillsBoundsOpaquely(false);
   magnifier_layer_->SetRoundedCornerRadius(kMagnifierRoundedCorners);
   parent_layer->Add(magnifier_layer_.get());
diff --git a/ash/touch/touch_selection_magnifier_runner_ash.h b/ash/touch/touch_selection_magnifier_runner_ash.h
index c5b61d35..7f5a5ab 100644
--- a/ash/touch/touch_selection_magnifier_runner_ash.h
+++ b/ash/touch/touch_selection_magnifier_runner_ash.h
@@ -7,16 +7,14 @@
 
 #include "ash/ash_export.h"
 #include "base/memory/raw_ptr.h"
+#include "ui/gfx/geometry/rounded_corners_f.h"
+#include "ui/gfx/geometry/size.h"
 #include "ui/touch_selection/touch_selection_magnifier_runner.h"
 
 namespace aura {
 class Window;
 }  // namespace aura
 
-namespace gfx {
-class PointF;
-}  // namespace gfx
-
 namespace ui {
 class Layer;
 }  // namespace ui
@@ -36,6 +34,17 @@
 
   ~TouchSelectionMagnifierRunnerAsh() override;
 
+  static constexpr float kMagnifierScale = 2.0f;
+
+  static constexpr gfx::Size kMagnifierLayerSize{100, 48};
+
+  static constexpr gfx::RoundedCornersF kMagnifierRoundedCorners{20};
+
+  // Offset to apply so that the magnifier is shown vertically above the point
+  // of interest. The offset specifies vertical displacement from the center of
+  // the text selection caret to the center of the magnifier bounds.
+  static constexpr int kMagnifierVerticalOffset = -32;
+
   // ui::TouchSelectionMagnifierRunner:
   void ShowMagnifier(aura::Window* context,
                      const gfx::PointF& position) override;
diff --git a/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc b/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc
index efa017b..6828ad4 100644
--- a/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc
+++ b/ash/touch/touch_selection_magnifier_runner_ash_unittest.cc
@@ -17,10 +17,6 @@
 
 namespace {
 
-// Should match `kMagnifierLayerSize` in
-// touch_selection_magnifier_runner_ash.cc.
-constexpr gfx::Size kMagnifierLayerSize(100, 48);
-
 TouchSelectionMagnifierRunnerAsh* GetMagnifierRunner() {
   return static_cast<TouchSelectionMagnifierRunnerAsh*>(
       ui::TouchSelectionMagnifierRunner::GetInstance());
@@ -135,8 +131,14 @@
   ASSERT_TRUE(magnifier_layer);
 
   gfx::Rect bounds = magnifier_layer->bounds();
-  EXPECT_EQ(bounds.size(), kMagnifierLayerSize);
-  EXPECT_EQ(bounds.CenterPoint(), gfx::Point(position.x(), position.y()));
+  EXPECT_EQ(bounds.size(),
+            TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize);
+  EXPECT_EQ(
+      bounds.CenterPoint(),
+      gfx::Point(
+          position.x(),
+          position.y() +
+              TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset));
 
   magnifier_runner->CloseMagnifier();
   RunPendingMessages();
@@ -154,8 +156,14 @@
   ASSERT_TRUE(magnifier_layer);
 
   gfx::Rect bounds = magnifier_layer->bounds();
-  EXPECT_EQ(bounds.size(), kMagnifierLayerSize);
-  EXPECT_EQ(bounds.CenterPoint(), gfx::Point(position.x(), position.y()));
+  EXPECT_EQ(bounds.size(),
+            TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize);
+  EXPECT_EQ(
+      bounds.CenterPoint(),
+      gfx::Point(
+          position.x(),
+          position.y() +
+              TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset));
 
   // Move the magnifier.
   position = gfx::PointF(400, 150);
@@ -163,8 +171,14 @@
   EXPECT_EQ(magnifier_layer, magnifier_runner->GetMagnifierLayerForTesting());
 
   bounds = magnifier_layer->bounds();
-  EXPECT_EQ(bounds.size(), kMagnifierLayerSize);
-  EXPECT_EQ(bounds.CenterPoint(), gfx::Point(position.x(), position.y()));
+  EXPECT_EQ(bounds.size(),
+            TouchSelectionMagnifierRunnerAsh::kMagnifierLayerSize);
+  EXPECT_EQ(
+      bounds.CenterPoint(),
+      gfx::Point(
+          position.x(),
+          position.y() +
+              TouchSelectionMagnifierRunnerAsh::kMagnifierVerticalOffset));
 
   magnifier_runner->CloseMagnifier();
   RunPendingMessages();
diff --git a/ash/webui/camera_app_ui/resources/views/main.html b/ash/webui/camera_app_ui/resources/views/main.html
index 127fe28..d7b9b99 100644
--- a/ash/webui/camera_app_ui/resources/views/main.html
+++ b/ash/webui/camera_app_ui/resources/views/main.html
@@ -253,7 +253,8 @@
       <div class="centered-overlay">
         <div id="timer-tick-msg"></div>
       </div>
-      <div id="nudge" tabindex="-1" role="alert" aria-live="polite" hidden>
+      <div id="nudge" tabindex="0" role="alert" aria-label="storage low warning"
+           aria-live="polite" hidden>
         <div class="warning-icon"></div>
         <div class="warning-msg"
              i18n-text="low_storage_nudge_warning_msg"></div>
@@ -506,7 +507,8 @@
              id="low-storage-dialog-description"></div>
         <div class="low-storage-dialog-buttons">
           <button class="dialog-negative-button text-button pill
-                  system-secondary dark" tabindex="0">
+                  system-secondary dark" aria-label="open storage management"
+                  tabindex="0">
             <div class="external-link-icon"></div>
             <div i18n-text="low_storage_dialog_storage_button"></div>
           </button>
diff --git a/base/allocator/partition_allocator/partition_alloc.gni b/base/allocator/partition_allocator/partition_alloc.gni
index 04abf46..cf598e18 100644
--- a/base/allocator/partition_allocator/partition_alloc.gni
+++ b/base/allocator/partition_allocator/partition_alloc.gni
@@ -128,7 +128,8 @@
   # to go through build_overrides
   enable_dangling_raw_ptr_perf_experiment = false
 
-  backup_ref_ptr_poison_oob_ptr = false
+  # TODO(bartekn): Enabled temporarily, disable before reaches Beta.
+  backup_ref_ptr_poison_oob_ptr = enable_backup_ref_ptr_support
 }
 
 declare_args() {
diff --git a/cc/paint/filter_operation.cc b/cc/paint/filter_operation.cc
index 39e99ba2..5eb6354 100644
--- a/cc/paint/filter_operation.cc
+++ b/cc/paint/filter_operation.cc
@@ -30,8 +30,7 @@
   if (type_ == BLUR)
     return amount_ == other.amount_ && blur_tile_mode_ == other.blur_tile_mode_;
   if (type_ == DROP_SHADOW) {
-    return amount_ == other.amount_ &&
-           drop_shadow_offset_ == other.drop_shadow_offset_ &&
+    return amount_ == other.amount_ && offset_ == other.offset_ &&
            drop_shadow_color_ == other.drop_shadow_color_;
   }
   if (type_ == REFERENCE) {
@@ -41,6 +40,9 @@
     return shape_ == other.shape_ && amount_ == other.amount_ &&
            outer_threshold_ == other.outer_threshold_;
   }
+  if (type_ == OFFSET) {
+    return offset_ == other.offset_;
+  }
   return amount_ == other.amount_;
 }
 
@@ -50,12 +52,13 @@
     : type_(type),
       amount_(amount),
       outer_threshold_(0),
-      drop_shadow_offset_(0, 0),
+      offset_(0, 0),
       drop_shadow_color_(SkColors::kTransparent),
       zoom_inset_(0) {
   DCHECK_NE(type_, DROP_SHADOW);
   DCHECK_NE(type_, COLOR_MATRIX);
   DCHECK_NE(type_, REFERENCE);
+  DCHECK_NE(type_, OFFSET);
   matrix_.fill(0.0f);
 }
 
@@ -65,7 +68,7 @@
     : type_(type),
       amount_(amount),
       outer_threshold_(0),
-      drop_shadow_offset_(0, 0),
+      offset_(0, 0),
       drop_shadow_color_(SkColors::kTransparent),
       zoom_inset_(0),
       blur_tile_mode_(tile_mode) {
@@ -80,7 +83,7 @@
     : type_(type),
       amount_(stdDeviation),
       outer_threshold_(0),
-      drop_shadow_offset_(offset),
+      offset_(offset),
       drop_shadow_color_(color),
       zoom_inset_(0) {
   DCHECK_EQ(type_, DROP_SHADOW);
@@ -91,7 +94,7 @@
     : type_(type),
       amount_(0),
       outer_threshold_(0),
-      drop_shadow_offset_(0, 0),
+      offset_(0, 0),
       drop_shadow_color_(SkColors::kTransparent),
       matrix_(matrix),
       zoom_inset_(0) {
@@ -102,19 +105,30 @@
     : type_(type),
       amount_(amount),
       outer_threshold_(0),
-      drop_shadow_offset_(0, 0),
+      offset_(0, 0),
       drop_shadow_color_(SkColors::kTransparent),
       zoom_inset_(inset) {
   DCHECK_EQ(type_, ZOOM);
   matrix_.fill(0.0f);
 }
 
+FilterOperation::FilterOperation(FilterType type, const gfx::Point& offset)
+    : type_(type),
+      amount_(0),
+      outer_threshold_(0),
+      offset_(offset),
+      drop_shadow_color_(SkColors::kTransparent),
+      zoom_inset_(0) {
+  DCHECK_EQ(type_, OFFSET);
+  matrix_.fill(0.0f);
+}
+
 FilterOperation::FilterOperation(FilterType type,
                                  sk_sp<PaintFilter> image_filter)
     : type_(type),
       amount_(0),
       outer_threshold_(0),
-      drop_shadow_offset_(0, 0),
+      offset_(0, 0),
       drop_shadow_color_(SkColors::kTransparent),
       image_filter_(std::move(image_filter)),
       zoom_inset_(0) {
@@ -129,7 +143,7 @@
     : type_(type),
       amount_(inner_threshold),
       outer_threshold_(outer_threshold),
-      drop_shadow_offset_(0, 0),
+      offset_(0, 0),
       drop_shadow_color_(SkColors::kTransparent),
       zoom_inset_(0),
       shape_(shape) {
@@ -177,6 +191,8 @@
     case FilterOperation::ALPHA_THRESHOLD:
       return FilterOperation::CreateAlphaThresholdFilter(
           FilterOperation::ShapeRects(), 1.f, 0.f);
+    case FilterOperation::OFFSET:
+      return FilterOperation::CreateOffsetFilter(gfx::Point(0, 0));
   }
   NOTREACHED();
   return FilterOperation::CreateEmptyFilter();
@@ -203,6 +219,7 @@
     case FilterOperation::SATURATING_BRIGHTNESS:
       return amount;
     case FilterOperation::COLOR_MATRIX:
+    case FilterOperation::OFFSET:
     case FilterOperation::REFERENCE:
       NOTREACHED();
       return amount;
@@ -244,13 +261,12 @@
   if (to_op.type() == FilterOperation::BLUR) {
     blended_filter.set_blur_tile_mode(to_op.blur_tile_mode());
   } else if (to_op.type() == FilterOperation::DROP_SHADOW) {
-    gfx::Point blended_offset(gfx::Tween::LinearIntValueBetween(
-                                  progress, from_op.drop_shadow_offset().x(),
-                                  to_op.drop_shadow_offset().x()),
-                              gfx::Tween::LinearIntValueBetween(
-                                  progress, from_op.drop_shadow_offset().y(),
-                                  to_op.drop_shadow_offset().y()));
-    blended_filter.set_drop_shadow_offset(blended_offset);
+    gfx::Point blended_offset(
+        gfx::Tween::LinearIntValueBetween(progress, from_op.offset().x(),
+                                          to_op.offset().x()),
+        gfx::Tween::LinearIntValueBetween(progress, from_op.offset().y(),
+                                          to_op.offset().y()));
+    blended_filter.set_offset(blended_offset);
     blended_filter.set_drop_shadow_color(gfx::Tween::ColorValueBetween(
         progress, from_op.drop_shadow_color(), to_op.drop_shadow_color()));
   } else if (to_op.type() == FilterOperation::ZOOM) {
@@ -286,7 +302,7 @@
       break;
     case FilterOperation::DROP_SHADOW:
       value->SetDouble("std_deviation", amount_);
-      MathUtil::AddToTracedValue("offset", drop_shadow_offset_, value);
+      MathUtil::AddToTracedValue("offset", offset_, value);
       // TODO(crbug/1308932): Remove toSkColor and make all SkColor4f.
       value->SetInteger("color", drop_shadow_color_.toSkColor());
       break;
@@ -321,6 +337,9 @@
       }
       value->EndArray();
     } break;
+    case FilterOperation::OFFSET:
+      MathUtil::AddToTracedValue("offset", offset_, value);
+      break;
   }
 }
 
@@ -359,7 +378,7 @@
       gfx::RectF result(rect);
       result.Inset(gfx::InsetsF::VH(-spread_y, -spread_x));
 
-      gfx::Point drop_shadow_offset = op.drop_shadow_offset();
+      gfx::Point drop_shadow_offset = op.offset();
       SkVector mapped_drop_shadow_offset;
       matrix.mapVector(drop_shadow_offset.x(), drop_shadow_offset.y(),
                        &mapped_drop_shadow_offset);
@@ -376,6 +395,15 @@
       return gfx::SkIRectToRect(op.image_filter()->filter_bounds(
           gfx::RectToSkIRect(rect), matrix, direction));
     }
+    case FilterOperation::OFFSET: {
+      SkVector mapped_offset;
+      matrix.mapVector(op.offset().x(), op.offset().y(), &mapped_offset);
+      if (direction == SkImageFilter::kReverse_MapDirection)
+        mapped_offset = -mapped_offset;
+      return gfx::ToEnclosingRect(
+          gfx::RectF(rect) +
+          gfx::Vector2dF(mapped_offset.x(), mapped_offset.y()));
+    }
     default:
       return rect;
   }
diff --git a/cc/paint/filter_operation.h b/cc/paint/filter_operation.h
index 46eb3e3..93b6c8f 100644
--- a/cc/paint/filter_operation.h
+++ b/cc/paint/filter_operation.h
@@ -49,7 +49,8 @@
     REFERENCE,
     SATURATING_BRIGHTNESS,  // Not used in CSS/SVG.
     ALPHA_THRESHOLD,        // Not used in CSS/SVG.
-    FILTER_TYPE_LAST = ALPHA_THRESHOLD
+    OFFSET,                 // Not used in CSS/SVG.
+    FILTER_TYPE_LAST = OFFSET
   };
 
   FilterOperation();
@@ -71,9 +72,9 @@
     return outer_threshold_;
   }
 
-  gfx::Point drop_shadow_offset() const {
-    DCHECK_EQ(type_, DROP_SHADOW);
-    return drop_shadow_offset_;
+  gfx::Point offset() const {
+    DCHECK(type_ == DROP_SHADOW || type_ == OFFSET);
+    return offset_;
   }
 
   SkColor4f drop_shadow_color() const {
@@ -174,6 +175,10 @@
                            outer_threshold);
   }
 
+  static FilterOperation CreateOffsetFilter(const gfx::Point& offset) {
+    return FilterOperation(OFFSET, offset);
+  }
+
   bool operator==(const FilterOperation& other) const;
 
   bool operator!=(const FilterOperation& other) const {
@@ -198,9 +203,9 @@
     outer_threshold_ = outer_threshold;
   }
 
-  void set_drop_shadow_offset(const gfx::Point& offset) {
-    DCHECK_EQ(type_, DROP_SHADOW);
-    drop_shadow_offset_ = offset;
+  void set_offset(const gfx::Point& offset) {
+    DCHECK(type_ == DROP_SHADOW || type_ == OFFSET);
+    offset_ = offset;
   }
 
   void set_drop_shadow_color(SkColor4f color) {
@@ -268,6 +273,8 @@
 
   FilterOperation(FilterType type, float amount, int inset);
 
+  FilterOperation(FilterType type, const gfx::Point& offset);
+
   FilterOperation(FilterType type, sk_sp<PaintFilter> image_filter);
 
   FilterOperation(FilterType type,
@@ -278,7 +285,7 @@
   FilterType type_;
   float amount_;
   float outer_threshold_;
-  gfx::Point drop_shadow_offset_;
+  gfx::Point offset_;
   SkColor4f drop_shadow_color_;
   sk_sp<PaintFilter> image_filter_;
   Matrix matrix_;
diff --git a/cc/paint/filter_operations.cc b/cc/paint/filter_operations.cc
index 1769b34..6cf836b 100644
--- a/cc/paint/filter_operations.cc
+++ b/cc/paint/filter_operations.cc
@@ -84,6 +84,7 @@
       case FilterOperation::BLUR:
       case FilterOperation::DROP_SHADOW:
       case FilterOperation::ZOOM:
+      case FilterOperation::OFFSET:
         return true;
       case FilterOperation::REFERENCE:
         // TODO(hendrikw): SkImageFilter needs a function that tells us if the
@@ -117,10 +118,9 @@
         continue;
       case FilterOperation::DROP_SHADOW:
         // |op.amount| here is the blur radius.
-        max_movement =
-            fmax(max_movement, fmax(std::abs(op.drop_shadow_offset().x()),
-                                    std::abs(op.drop_shadow_offset().y())) +
-                                   op.amount() * 3.f);
+        max_movement = fmax(max_movement, fmax(std::abs(op.offset().x()),
+                                               std::abs(op.offset().y())) +
+                                              op.amount() * 3.f);
         continue;
       case FilterOperation::ZOOM:
         max_movement = fmax(max_movement, op.zoom_inset());
@@ -130,6 +130,13 @@
         // the filter can move pixels. See crbug.com/523538 (sort of).
         max_movement = fmax(max_movement, 100);
         continue;
+      case FilterOperation::OFFSET:
+        // TODO(crbug/1379125): Work out how to correctly set maximum pixel
+        // movement when an offset filter may be combined with other pixel
+        // moving filters.
+        max_movement =
+            fmax(std::abs(op.offset().x()), std::abs(op.offset().y()));
+        continue;
       case FilterOperation::OPACITY:
       case FilterOperation::COLOR_MATRIX:
       case FilterOperation::GRAYSCALE:
@@ -175,6 +182,7 @@
       case FilterOperation::BRIGHTNESS:
       case FilterOperation::CONTRAST:
       case FilterOperation::SATURATING_BRIGHTNESS:
+      case FilterOperation::OFFSET:
         break;
     }
   }
diff --git a/cc/paint/filter_operations_unittest.cc b/cc/paint/filter_operations_unittest.cc
index 7a221613..f3bb045 100644
--- a/cc/paint/filter_operations_unittest.cc
+++ b/cc/paint/filter_operations_unittest.cc
@@ -213,6 +213,29 @@
             ops.MapRectReverse(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
 }
 
+TEST(FilterOperationsTest, MapRectOffset) {
+  FilterOperations ops;
+  ops.Append(FilterOperation::CreateOffsetFilter(gfx::Point(30, 40)));
+  EXPECT_EQ(gfx::Rect(30, 40, 10, 10),
+            ops.MapRect(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
+  EXPECT_EQ(gfx::Rect(60, 80, 20, 20),
+            ops.MapRect(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
+  EXPECT_EQ(gfx::Rect(30, -50, 10, 10),
+            ops.MapRect(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
+}
+
+TEST(FilterOperationsTest, MapRectReverseOffset) {
+  FilterOperations ops;
+  ops.Append(FilterOperation::CreateOffsetFilter(gfx::Point(30, 40)));
+  EXPECT_EQ(gfx::Rect(-30, -40, 10, 10),
+            ops.MapRectReverse(gfx::Rect(0, 0, 10, 10), SkMatrix::I()));
+  EXPECT_EQ(gfx::Rect(-60, -80, 20, 20),
+            ops.MapRectReverse(gfx::Rect(0, 0, 20, 20), SkMatrix::Scale(2, 2)));
+  EXPECT_EQ(
+      gfx::Rect(-30, 30, 10, 10),
+      ops.MapRectReverse(gfx::Rect(0, -10, 10, 10), SkMatrix::Scale(1, -1)));
+}
+
 TEST(FilterOperationsTest, MapRectTypeConversionDoesNotOverflow) {
   // Must be bigger than half of the positive range so that the width/height
   // overflow happens, but small enough that there aren't other issues before
@@ -253,23 +276,23 @@
     FilterOperation op =                                                    \
         FilterOperation::Create##filter_name##Filter(a, b, c);              \
     EXPECT_EQ(FilterOperation::filter_type, op.type());                     \
-    EXPECT_EQ(a, op.drop_shadow_offset());                                  \
+    EXPECT_EQ(a, op.offset());                                              \
     EXPECT_EQ(b, op.amount());                                              \
     EXPECT_EQ(c, op.drop_shadow_color());                                   \
                                                                             \
     FilterOperation op2 = FilterOperation::CreateEmptyFilter();             \
     op2.set_type(FilterOperation::filter_type);                             \
                                                                             \
-    EXPECT_NE(a, op2.drop_shadow_offset());                                 \
+    EXPECT_NE(a, op2.offset());                                             \
     EXPECT_NE(b, op2.amount());                                             \
     EXPECT_NE(c, op2.drop_shadow_color());                                  \
                                                                             \
-    op2.set_drop_shadow_offset(a);                                          \
+    op2.set_offset(a);                                                      \
     op2.set_amount(b);                                                      \
     op2.set_drop_shadow_color(c);                                           \
                                                                             \
     EXPECT_EQ(FilterOperation::filter_type, op2.type());                    \
-    EXPECT_EQ(a, op2.drop_shadow_offset());                                 \
+    EXPECT_EQ(a, op2.offset());                                             \
     EXPECT_EQ(b, op2.amount());                                             \
     EXPECT_EQ(c, op2.drop_shadow_color());                                  \
   }
@@ -979,6 +1002,10 @@
   EXPECT_FLOAT_EQ(3.f, filters.MaximumPixelMovement());
 
   filters.Clear();
+  filters.Append(FilterOperation::CreateOffsetFilter(gfx::Point(3, -4)));
+  EXPECT_FLOAT_EQ(4.0f, filters.MaximumPixelMovement());
+
+  filters.Clear();
   filters.Append(FilterOperation::CreateReferenceFilter(
       sk_make_sp<OffsetPaintFilter>(10, 10, nullptr)));
   // max movement = 100.
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc
index 6986eb7a..f9f43e4 100644
--- a/cc/paint/paint_op_buffer.cc
+++ b/cc/paint/paint_op_buffer.cc
@@ -436,7 +436,7 @@
 const PaintOp* PaintOpBuffer::GetOpAtForTesting(size_t index,
                                                 PaintOpType type) const {
   size_t i = 0;
-  for (const auto& op : Iterator(this)) {
+  for (const auto& op : *this) {
     if (i == index) {
       return op.GetType() == type ? &op : nullptr;
     }
@@ -445,4 +445,12 @@
   return nullptr;
 }
 
+PaintOpBuffer::Iterator PaintOpBuffer::begin() const {
+  return Iterator(this);
+}
+
+PaintOpBuffer::Iterator PaintOpBuffer::end() const {
+  return Iterator(this).end();
+}
+
 }  // namespace cc
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h
index d9e9056..a4f9de177 100644
--- a/cc/paint/paint_op_buffer.h
+++ b/cc/paint/paint_op_buffer.h
@@ -310,6 +310,12 @@
   class CompositeIterator;
   class PlaybackFoldingIterator;
 
+  // STL-like container support:
+  using value_type = PaintOp;
+  using const_iterator = Iterator;
+  Iterator begin() const;
+  Iterator end() const;
+
  private:
   friend class DisplayItemList;
   friend class PaintOp;
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc
index bc2a4f1f..63ba26b 100644
--- a/cc/paint/paint_op_buffer_unittest.cc
+++ b/cc/paint/paint_op_buffer_unittest.cc
@@ -1276,7 +1276,7 @@
       bytes_written_[i] = 0;
 
     size_t op_idx = 0;
-    for (const PaintOp& op : PaintOpBuffer::Iterator(&buffer)) {
+    for (const PaintOp& op : buffer) {
       size_t bytes_written = op.Serialize(current_, remaining_,
                                           options_provider_.serialize_options(),
                                           nullptr, SkM44(), SkM44());
@@ -1979,7 +1979,7 @@
   TestOptionsProvider options_provider;
 
   size_t op_idx = 0;
-  for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter, ++op_idx) {
+  for (const PaintOp& op : buffer_) {
     SCOPED_TRACE(base::StringPrintf(
         "%s #%zu", PaintOpTypeToString(GetParamType()).c_str(), op_idx));
     size_t expected_bytes = bytes_written[op_idx];
@@ -1992,15 +1992,16 @@
     for (size_t i = 0; i < bytes_written[op_idx] + 2; ++i) {
       options_provider.ClearPaintCache();
       options_provider.ForcePurgeSkottieSerializationHistory();
-      size_t written_bytes = iter->Serialize(
-          output_.get(), i, options_provider.serialize_options(), nullptr,
-          SkM44(), SkM44());
+      size_t written_bytes =
+          op.Serialize(output_.get(), i, options_provider.serialize_options(),
+                       nullptr, SkM44(), SkM44());
       if (i >= expected_bytes) {
         EXPECT_EQ(expected_bytes, written_bytes) << "i: " << i;
       } else {
         EXPECT_EQ(0u, written_bytes) << "i: " << i;
       }
     }
+    ++op_idx;
   }
 }
 
@@ -2026,9 +2027,8 @@
   std::unique_ptr<char, base::AlignedFreeDeleter> deserialize_buffer_(
       static_cast<char*>(base::AlignedAlloc(kOutputOpSize, kAlign)));
 
-  size_t op_idx = 0;
   size_t total_read = 0;
-  for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter, ++op_idx) {
+  for (size_t op_idx = 0; op_idx < buffer_.size(); ++op_idx) {
     PaintOp* serialized = reinterpret_cast<PaintOp*>(current);
     uint32_t skip = serialized->skip;
 
@@ -2117,7 +2117,7 @@
   std::unique_ptr<char, base::AlignedFreeDeleter> deserialized(
       static_cast<char*>(
           base::AlignedAlloc(deserialized_size, PaintOpBuffer::kPaintOpAlign)));
-  for (const PaintOp& op : PaintOpBuffer::Iterator(&buffer_)) {
+  for (const PaintOp& op : buffer_) {
     size_t bytes_written = op.Serialize(output_.get(), output_size_,
                                         options_provider.serialize_options(),
                                         nullptr, SkM44(), SkM44());
@@ -2617,8 +2617,7 @@
   TestOptionsProvider options_provider;
 
   int op_idx = 0;
-  for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
-    const PaintOp& op = *iter;
+  for (const PaintOp& op : buffer) {
     size_t bytes_written = op.Serialize(serialized.get(), buffer_size,
                                         options_provider.serialize_options(),
                                         nullptr, SkM44(), SkM44());
@@ -2698,8 +2697,7 @@
   TestOptionsProvider options_provider;
 
   int op_idx = 0;
-  for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
-    const PaintOp& op = *iter;
+  for (const PaintOp& op : buffer) {
     size_t bytes_written = op.Serialize(serialized.get(), buffer_size,
                                         options_provider.serialize_options(),
                                         nullptr, SkM44(), SkM44());
@@ -2759,8 +2757,7 @@
 
   // Every op should serialize but fail to deserialize due to the bad rect.
   int op_idx = 0;
-  for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
-    const PaintOp& op = *iter;
+  for (const PaintOp& op : buffer) {
     size_t bytes_written = op.Serialize(serialized.get(), buffer_size,
                                         options_provider.serialize_options(),
                                         nullptr, SkM44(), SkM44());
@@ -2787,7 +2784,7 @@
   PushDrawImageOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawImageOp&>(base_op);
 
     SkRect image_rect =
@@ -2802,7 +2799,7 @@
   PushDrawImageRectOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawImageRectOp&>(base_op);
 
     ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
@@ -2815,7 +2812,7 @@
   PushDrawIRectOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawIRectOp&>(base_op);
 
     ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
@@ -2828,7 +2825,7 @@
   PushDrawOvalOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawOvalOp&>(base_op);
 
     ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
@@ -2841,7 +2838,7 @@
   PushDrawPathOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawPathOp&>(base_op);
 
     ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
@@ -2854,7 +2851,7 @@
   PushDrawRectOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawRectOp&>(base_op);
 
     ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
@@ -2867,7 +2864,7 @@
   PushDrawRRectOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawRRectOp&>(base_op);
 
     ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
@@ -2880,7 +2877,7 @@
   PushDrawLineOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawLineOp&>(base_op);
 
     SkRect line_rect;
@@ -2898,7 +2895,7 @@
   PushDrawDRRectOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawDRRectOp&>(base_op);
 
     ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
@@ -2911,7 +2908,7 @@
   PushDrawTextBlobOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawTextBlobOp&>(base_op);
 
     ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
@@ -3364,12 +3361,12 @@
   serializer.Serialize(&buffer);
   ASSERT_NE(serializer.written(), 0u);
 
-  auto deserialized_buffer =
+  sk_sp<PaintOpBuffer> deserialized_buffer =
       PaintOpBuffer::MakeFromMemory(memory.get(), serializer.written(),
                                     options_provider.deserialize_options());
   ASSERT_TRUE(deserialized_buffer);
 
-  for (const PaintOp& op : PaintOpBuffer::Iterator(deserialized_buffer.get())) {
+  for (const PaintOp& op : *deserialized_buffer) {
     testing::NiceMock<MockCanvas> canvas;
     PlaybackParams params(nullptr);
     testing::Sequence s;
@@ -3563,7 +3560,7 @@
   PushDrawSkottieOps(&buffer);
 
   SkRect rect;
-  for (const PaintOp& base_op : PaintOpBuffer::Iterator(&buffer)) {
+  for (const PaintOp& base_op : buffer) {
     const auto& op = static_cast<const DrawSkottieOp&>(base_op);
 
     ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
@@ -3969,7 +3966,7 @@
     else
       EXPECT_EQ(records[i]->size(), 2u);
 
-    for (const PaintOp& base_op : PaintOpBuffer::Iterator(records[i].get())) {
+    for (const PaintOp& base_op : *records[i]) {
       if (base_op.GetType() != PaintOpType::DrawRect)
         continue;
       const auto& op = static_cast<const DrawRectOp&>(base_op);
@@ -4331,6 +4328,14 @@
   EXPECT_EQ(sizeof(PaintOpBuffer), buffer.bytes_used());
 }
 
+TEST(IteratorTest, StlContainerLikeIterationTest) {
+  PaintOpBuffer buffer;
+  buffer.push<SaveOp>();
+  buffer.push<SetMatrixOp>(SkM44::Scale(1, 2));
+  EXPECT_THAT(buffer,
+              ElementsAre(Eq(SaveOp()), Eq(SetMatrixOp(SkM44::Scale(1, 2)))));
+}
+
 TEST(IteratorTest, IterationTest) {
   PaintOpBuffer buffer;
   buffer.push<SaveOp>();
diff --git a/cc/paint/render_surface_filters.cc b/cc/paint/render_surface_filters.cc
index e44d6820..922dddd 100644
--- a/cc/paint/render_surface_filters.cc
+++ b/cc/paint/render_surface_filters.cc
@@ -199,8 +199,7 @@
         break;
       case FilterOperation::DROP_SHADOW:
         image_filter = sk_make_sp<DropShadowPaintFilter>(
-            SkIntToScalar(op.drop_shadow_offset().x()),
-            SkIntToScalar(op.drop_shadow_offset().y()),
+            SkIntToScalar(op.offset().x()), SkIntToScalar(op.offset().y()),
             SkIntToScalar(op.amount()), SkIntToScalar(op.amount()),
             op.drop_shadow_color(),
             DropShadowPaintFilter::ShadowMode::kDrawShadowAndForeground,
@@ -295,6 +294,12 @@
         }
         break;
       }
+      case FilterOperation::OFFSET: {
+        image_filter = sk_make_sp<OffsetPaintFilter>(
+            SkIntToScalar(op.offset().x()), SkIntToScalar(op.offset().y()),
+            std::move(image_filter));
+        break;
+      }
     }
   }
   return image_filter;
diff --git a/chrome/android/java/res/xml/privacy_preferences_v2.xml b/chrome/android/java/res/xml/privacy_preferences_v2.xml
index 9394eed..2a73d850 100644
--- a/chrome/android/java/res/xml/privacy_preferences_v2.xml
+++ b/chrome/android/java/res/xml/privacy_preferences_v2.xml
@@ -21,6 +21,10 @@
         android:title="@string/prefs_privacy_guide_title"
         android:summary="@string/prefs_privacy_guide_summary"
         android:fragment="org.chromium.chrome.browser.privacy_guide.PrivacyGuideFragment" />
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:key="third_party_cookies"
+        android:title="@string/third_party_cookies_page_title"
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings" />
     <Preference
         android:key="do_not_track"
         android:title="@string/do_not_track_title"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
index b5bcd061..bf6987f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
@@ -4,6 +4,8 @@
 
 package org.chromium.chrome.browser.privacy.settings;
 
+import static org.chromium.components.content_settings.PrefNames.COOKIE_CONTROLS_MODE;
+
 import android.os.Build;
 import android.os.Bundle;
 import android.text.SpannableString;
@@ -39,6 +41,8 @@
 import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
 import org.chromium.components.browser_ui.settings.SettingsLauncher;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
+import org.chromium.components.browser_ui.site_settings.ContentSettingsResources;
+import org.chromium.components.browser_ui.site_settings.SingleCategorySettings;
 import org.chromium.components.prefs.PrefService;
 import org.chromium.components.signin.identitymanager.ConsentLevel;
 import org.chromium.components.user_prefs.UserPrefs;
@@ -62,6 +66,7 @@
     private static final String PREF_PRIVACY_SANDBOX = "privacy_sandbox";
     private static final String PREF_PRIVACY_GUIDE = "privacy_guide";
     private static final String PREF_INCOGNITO_LOCK = "incognito_lock";
+    private static final String PREF_THIRD_PARTY_COOKIES = "third_party_cookies";
 
     private ManagedPreferenceDelegate mManagedPreferenceDelegate;
     private IncognitoLockSettings mIncognitoLockSettings;
@@ -138,6 +143,14 @@
         Preference syncAndServicesLink = findPreference(PREF_SYNC_AND_SERVICES_LINK);
         syncAndServicesLink.setSummary(buildSyncAndServicesLink());
 
+        Preference thirdPartyCookies = findPreference(PREF_THIRD_PARTY_COOKIES);
+        if (thirdPartyCookies != null) {
+            thirdPartyCookies.getExtras().putString(
+                    SingleCategorySettings.EXTRA_CATEGORY, thirdPartyCookies.getKey());
+            thirdPartyCookies.getExtras().putString(
+                    SingleCategorySettings.EXTRA_TITLE, thirdPartyCookies.getTitle().toString());
+        }
+
         updatePreferences();
     }
 
@@ -248,6 +261,12 @@
         }
 
         mIncognitoLockSettings.updateIncognitoReauthPreferenceIfNeeded(getActivity());
+
+        Preference thirdPartyCookies = findPreference(PREF_THIRD_PARTY_COOKIES);
+        if (thirdPartyCookies != null) {
+            thirdPartyCookies.setSummary(ContentSettingsResources.getThirdPartyCookieListSummary(
+                    prefService.getInteger(COOKIE_CONTROLS_MODE)));
+        }
     }
 
     private ChromeManagedPreferenceDelegate createManagedPreferenceDelegate() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java
index 7f42ca9..1994ef3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java
@@ -125,11 +125,6 @@
                 return ContentFeatureList.isEnabled(ContentFeatures.FED_CM);
             case SiteSettingsCategory.Type.NFC:
                 return ContentFeatureList.isEnabled(ContentFeatureList.WEB_NFC);
-            case SiteSettingsCategory.Type.COOKIES:
-                return !isPrivacySandboxSettings4Enabled();
-            case SiteSettingsCategory.Type.SITE_DATA:
-            case SiteSettingsCategory.Type.THIRD_PARTY_COOKIES:
-                return isPrivacySandboxSettings4Enabled();
             default:
                 return true;
         }
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 011f386..2aa1f53a 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -2001,7 +2001,13 @@
     Container name
   </message>
   <message name="IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CONTAINER_IP_LABEL" desc="Column heading for container IP address.">
-    IP
+    IP address
+  </message>
+  <message name="IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_SHARE_MICROPHONE" desc="Toggle label to share host microhone with this container.">
+    Share microphone
+  </message>
+  <message name="IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_APP_BADGE_COLOR" desc="Label for color picker to choose a badge color for this container's apps.">
+    App badge color
   </message>
   <message name="IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE_DIALOG_TITLE" desc="Title of the dialog for entering details to create a new Linux container.">
     Create a new container
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_APP_BADGE_COLOR.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_APP_BADGE_COLOR.png.sha1
new file mode 100644
index 0000000..be7f76f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_APP_BADGE_COLOR.png.sha1
@@ -0,0 +1 @@
+2e97e9e713a32afea8cacd94b6f7324673e7d9e2
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CONTAINER_IP_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CONTAINER_IP_LABEL.png.sha1
index 86223f34..be7f76f 100644
--- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CONTAINER_IP_LABEL.png.sha1
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CONTAINER_IP_LABEL.png.sha1
@@ -1 +1 @@
-570e2c5720acea6523c9352deb80a5a92ab420e0
\ No newline at end of file
+2e97e9e713a32afea8cacd94b6f7324673e7d9e2
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_SHARE_MICROPHONE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_SHARE_MICROPHONE.png.sha1
new file mode 100644
index 0000000..be7f76f
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_SHARE_MICROPHONE.png.sha1
@@ -0,0 +1 @@
+2e97e9e713a32afea8cacd94b6f7324673e7d9e2
\ No newline at end of file
diff --git a/chrome/app/password_manager_ui_strings.grdp b/chrome/app/password_manager_ui_strings.grdp
index 699e334..d5edc3f 100644
--- a/chrome/app/password_manager_ui_strings.grdp
+++ b/chrome/app/password_manager_ui_strings.grdp
@@ -57,6 +57,9 @@
   <message name="IDS_PASSWORD_MANAGER_UI_EXPORT_DIALOG_BODY" desc="Text shown to the user on the dialog for exporting passwords, before any passwords have been exported.">
     Your passwords will be visible to anyone who can see the exported file.
   </message>
+  <message name="IDS_PASSWORD_MANAGER_UI_EXPORTING_TITLE" desc="The title of a dialog for exporting passwords. This title is shown while Chrome is performing the export and the user should wait.">
+    Exporting passwords...
+  </message>
   <message name="IDS_PASSWORD_MANAGER_UI_CHECKUP_TITLE" desc="Title for the Password Checkup page.">
     Password Checkup
   </message>
diff --git a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_EXPORTING_TITLE.png.sha1 b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_EXPORTING_TITLE.png.sha1
new file mode 100644
index 0000000..0c5106d
--- /dev/null
+++ b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_EXPORTING_TITLE.png.sha1
@@ -0,0 +1 @@
+21f63716259835f8ce80b3cc2ddff534400b3803
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index a022dc9..a4cba93 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1823,6 +1823,18 @@
   <message name="IDS_SETTINGS_TOPICS_PAGE_TOGGLE_SUB_LABEL" translateable="false" desc="Subabel for the toggle in the Topics preferences page.">
     Vivamus nibh turpis, varius quis nisi vel, porta laoreet tellus. Mauris porta imperdiet venenatis
   </message>
+  <message name="IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_HEADING" translateable="false" desc="Section title for the current Topics list underneath toggle in the Topics preferences page.">
+    Mauris at lectus
+  </message>
+  <message name="IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION" translateable="false" desc="Section description for the current Topics list in the Topics preferences page.">
+    Nulla tincidunt iaculis nulla, sit amet viverra massa luctus nec. Integer eget pellentesque magna, et venenatis lorem. Integer a porta elit. Eget bibendum neque.
+  </message>
+  <message name="IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION_DISABLED" translateable="false" desc="Section description for the current Topics list when Topics is disabled in the Topics preferences page.">
+    Sed porta viverra lacus ut euismod. Integer a cursus metus, ac ultricies libero.
+  </message>
+  <message name="IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION_EMPTY" translateable="false" desc="Section description for the current Topics list when the Topics list is empty in the Topics preferences page.">
+    Curabitur sagittis sapien ut turpis interdum, vitae porttitor sem pretium. Vestibulum sem mauris, ultrices ac massa sit amet, sodales aliquet est.
+  </message>
 
   <!-- Privacy Sandbox Settings 4 - Fledge Page -->
   <message name="IDS_SETTINGS_FLEDGE_PAGE_TITLE" translateable="false" desc="Title for the Fledge preferences page.">
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 22d057c..b130454 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -943,8 +943,6 @@
     "ntp_tiles/chrome_popular_sites_factory.h",
     "offline_items_collection/offline_content_aggregator_factory.cc",
     "offline_items_collection/offline_content_aggregator_factory.h",
-    "optimization_guide/chrome_browser_main_extra_parts_optimization_guide.cc",
-    "optimization_guide/chrome_browser_main_extra_parts_optimization_guide.h",
     "optimization_guide/chrome_hints_manager.cc",
     "optimization_guide/chrome_hints_manager.h",
     "optimization_guide/model_validator_keyed_service.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 95110ce..d22e33d 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3319,6 +3319,18 @@
     {"- Use first channel", kWebRtcApmDownmixMethodFirstChannel,
      std::size(kWebRtcApmDownmixMethodFirstChannel), nullptr}};
 
+#if !BUILDFLAG(IS_ANDROID)
+const FeatureEntry::FeatureParam
+    kSafetyCheckUnusedSitePermissionsNoDelayParam[] = {
+        {"unused-site-permissions-no-delay-for-testing", "true"}};
+
+const FeatureEntry::FeatureVariation
+    kSafetyCheckUnusedSitePermissionsVariations[] = {
+        {"for testing", kSafetyCheckUnusedSitePermissionsNoDelayParam,
+         std::size(kSafetyCheckUnusedSitePermissionsNoDelayParam), nullptr},
+};
+#endif
+
 // RECORDING USER METRICS FOR FLAGS:
 // -----------------------------------------------------------------------------
 // The first line of the entry is the internal name.
@@ -9046,7 +9058,10 @@
      flag_descriptions::kSafetyCheckUnusedSitePermissionsName,
      flag_descriptions::kSafetyCheckUnusedSitePermissionsDescription,
      kOsDesktop,
-     FEATURE_VALUE_TYPE(features::kSafetyCheckUnusedSitePermissions)},
+     FEATURE_WITH_PARAMS_VALUE_TYPE(
+         content_settings::features::kSafetyCheckUnusedSitePermissions,
+         kSafetyCheckUnusedSitePermissionsVariations,
+         "SafetyCheckUnusedSitePermissions")},
 #endif  // !BUILDFLAG(IS_ANDROID)
 
     {"autofill-enable-upstream-save-card-offer-ui-experiment",
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn
index 197d7a0..04b7947 100644
--- a/chrome/browser/ash/BUILD.gn
+++ b/chrome/browser/ash/BUILD.gn
@@ -4212,7 +4212,6 @@
     # builds as well.
     "../ui/views/frame/immersive_mode_controller_chromeos_unittest.cc",
     "../ui/views/select_file_dialog_extension_unittest.cc",
-    "../ui/webui/ash/cloud_upload/cloud_upload_notification_manager_unittest.cc",
     "../ui/webui/ash/login/l10n_util_test_util.cc",
     "../ui/webui/ash/login/l10n_util_test_util.h",
     "../ui/webui/ash/login/l10n_util_unittest.cc",
@@ -4847,6 +4846,8 @@
     "policy/remote_commands/device_command_set_volume_job_unittest.cc",
     "policy/remote_commands/device_command_start_crd_session_unittest.cc",
     "policy/remote_commands/device_command_wipe_users_job_unittest.cc",
+    "policy/remote_commands/fake_cros_network_config.cc",
+    "policy/remote_commands/fake_cros_network_config.h",
     "policy/remote_commands/fake_cros_network_config_base.cc",
     "policy/remote_commands/fake_cros_network_config_base.h",
     "policy/remote_commands/user_command_arc_job_unittest.cc",
diff --git a/chrome/browser/ash/app_list/search/app_search_provider_test_base.cc b/chrome/browser/ash/app_list/search/app_search_provider_test_base.cc
index c82eaf93..c70ba08 100644
--- a/chrome/browser/ash/app_list/search/app_search_provider_test_base.cc
+++ b/chrome/browser/ash/app_list/search/app_search_provider_test_base.cc
@@ -20,7 +20,6 @@
 #include "base/time/time.h"
 #include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h"
 #include "chrome/browser/ash/app_list/arc/arc_app_test.h"
-#include "chrome/browser/ash/app_list/arc/arc_default_app_list.h"
 #include "chrome/browser/ash/app_list/search/app_search_data_source.h"
 #include "chrome/browser/ash/app_list/search/app_search_provider.h"
 #include "chrome/browser/ash/app_list/search/app_zero_state_provider.h"
@@ -31,7 +30,6 @@
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/sync/model/string_ordinal.h"
-#include "extensions/browser/extension_prefs.h"
 #include "extensions/common/extension_builder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -106,6 +104,13 @@
   return result_str;
 }
 
+std::vector<ChromeSearchResult*> AppSearchProviderTestBase::GetLastResults() {
+  std::vector<ChromeSearchResult*> sorted_results;
+  for (const auto& result : search_controller_->last_results())
+    sorted_results.emplace_back(result.get());
+  return sorted_results;
+}
+
 std::string AppSearchProviderTestBase::AddArcApp(const std::string& name,
                                                  const std::string& package,
                                                  const std::string& activity,
diff --git a/chrome/browser/ash/app_list/search/app_search_provider_test_base.h b/chrome/browser/ash/app_list/search/app_search_provider_test_base.h
index 8c7ebe6..3490306 100644
--- a/chrome/browser/ash/app_list/search/app_search_provider_test_base.h
+++ b/chrome/browser/ash/app_list/search/app_search_provider_test_base.h
@@ -11,6 +11,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/test/simple_test_clock.h"
 #include "chrome/browser/ash/app_list/arc/arc_app_test.h"
+#include "chrome/browser/ash/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
 #include "extensions/common/mojom/manifest.mojom.h"
 
@@ -55,6 +56,9 @@
   // Returns list of result IDs sorted by their relevance.
   std::string GetSortedResultsString();
 
+  // Returns list of results.
+  std::vector<ChromeSearchResult*> GetLastResults();
+
   // Installs a test ARC app.
   std::string AddArcApp(const std::string& name,
                         const std::string& package,
diff --git a/chrome/browser/ash/app_list/search/app_search_provider_unittest.cc b/chrome/browser/ash/app_list/search/app_search_provider_unittest.cc
index 657ccda5..96dea594 100644
--- a/chrome/browser/ash/app_list/search/app_search_provider_unittest.cc
+++ b/chrome/browser/ash/app_list/search/app_search_provider_unittest.cc
@@ -12,6 +12,7 @@
 #include <utility>
 
 #include "ash/components/arc/test/fake_app_instance.h"
+#include "ash/public/cpp/app_list/internal_app_id_constants.h"
 #include "base/containers/contains.h"
 #include "base/i18n/rtl.h"
 #include "base/run_loop.h"
@@ -336,6 +337,24 @@
   EXPECT_EQ("WebApp1", RunQuery("WebA"));
 }
 
+TEST_F(AppSearchProviderTest, BasicAppServiceAppResult) {
+  InitializeSearchProvider();
+  RunQuery("Keyboard");
+  std::vector<ChromeSearchResult*> keyboard_results = GetLastResults();
+  EXPECT_EQ(keyboard_results.size(), 1u);
+  EXPECT_EQ(base::UTF16ToUTF8(keyboard_results[0]->title()),
+            kKeyboardShortcutHelperInternalName);
+  EXPECT_EQ(keyboard_results[0]->display_type(),
+            ash::SearchResultDisplayType::kList);
+  EXPECT_EQ(keyboard_results[0]->result_type(),
+            ash::AppListSearchResultType::kInternalApp);
+  EXPECT_EQ(keyboard_results[0]->metrics_type(), ash::INTERNAL_APP);
+  EXPECT_EQ(keyboard_results[0]->is_recommendation(), false);
+  EXPECT_EQ(keyboard_results[0]->category(), Category::kApps);
+  EXPECT_EQ(keyboard_results[0]->id(),
+            ash::kInternalAppIdKeyboardShortcutViewer);
+}
+
 class AppSearchProviderCrostiniTest : public AppSearchProviderTest {
  public:
   void SetUp() override {
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest.cc b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
index eed92a1d..25610a80 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest.cc
@@ -187,6 +187,11 @@
     return *this;
   }
 
+  TestCase& EnableOsFeedback() {
+    options.enable_os_feedback = true;
+    return *this;
+  }
+
   std::string GetFullName() const {
     std::string full_name = name;
 
@@ -235,6 +240,9 @@
     if (options.enable_search_v2)
       full_name += "_SearchV2";
 
+    if (options.enable_os_feedback)
+      full_name += "_OsFeedback";
+
     return full_name;
   }
 
@@ -1549,6 +1557,7 @@
         TestCase("hideCurrentDirectoryByTogglingHiddenAndroidFolders"),
         TestCase("newFolderInDownloads"),
         TestCase("showSendFeedbackAction"),
+        TestCase("showSendFeedbackAction").EnableOsFeedback(),
         TestCase("enableDisableStorageSettingsLink"),
         TestCase("showAvailableStorageMyFiles"),
         TestCase("showAvailableStorageDrive"),
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
index 72fb93e7..071e7ab0 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest_base.cc
@@ -2001,6 +2001,12 @@
     disabled_features.push_back(ash::features::kFilesSearchV2);
   }
 
+  if (options.enable_os_feedback) {
+    enabled_features.push_back(ash::features::kOsFeedback);
+  } else {
+    disabled_features.push_back(ash::features::kOsFeedback);
+  }
+
   // This is destroyed in |TearDown()|. We cannot initialize this in the
   // constructor due to this feature values' above dependence on virtual
   // method calls, but by convention subclasses of this fixture may initialize
@@ -2401,12 +2407,13 @@
     return;
   }
 
-  if (name == "expectWindowURL") {
-    const std::string* expected_url = value.FindString("expectedUrl");
-    EXPECT_TRUE(expected_url);
+  if (name == "expectWindowOrigin") {
+    const std::string* expected_origin = value.FindString("expectedOrigin");
+    EXPECT_TRUE(expected_origin);
     for (auto* web_contents : GetAllWebContents()) {
-      const std::string& url = web_contents->GetVisibleURL().spec();
-      if (url == *expected_url) {
+      const std::string& origin =
+          url::Origin::Create(web_contents->GetVisibleURL()).Serialize();
+      if (origin == *expected_origin) {
         *output = "true";
         return;
       }
@@ -3058,6 +3065,11 @@
     return;
   }
 
+  if (name == "isOsFeedbackEnabled") {
+    *output = options.enable_os_feedback ? "true" : "false";
+    return;
+  }
+
   if (name == "switchLanguage") {
     const std::string* language = value.FindString("language");
     ASSERT_TRUE(language);
diff --git a/chrome/browser/ash/file_manager/file_manager_browsertest_base.h b/chrome/browser/ash/file_manager/file_manager_browsertest_base.h
index 322018e..69da4c6 100644
--- a/chrome/browser/ash/file_manager/file_manager_browsertest_base.h
+++ b/chrome/browser/ash/file_manager/file_manager_browsertest_base.h
@@ -138,6 +138,9 @@
 
     // Whether tests should enable V2 of search.
     bool enable_search_v2 = false;
+
+    // Whether testt should enable OS Feedback.
+    bool enable_os_feedback = false;
   };
 
   FileManagerBrowserTestBase(const FileManagerBrowserTestBase&) = delete;
diff --git a/chrome/browser/ash/login/debug_overlay_browsertest.cc b/chrome/browser/ash/login/debug_overlay_browsertest.cc
index 67621a98..7807b5c 100644
--- a/chrome/browser/ash/login/debug_overlay_browsertest.cc
+++ b/chrome/browser/ash/login/debug_overlay_browsertest.cc
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
 #include "ash/public/cpp/login_screen_test_api.h"
 #include "base/command_line.h"
@@ -20,8 +21,8 @@
 constexpr char kDebugOverlay[] = "debuggerOverlay";
 constexpr char kScreensPanel[] = "DebuggerPanelScreens";
 
-constexpr int kOobeScreensCount = 46;
-constexpr int kLoginScreensCount = 44;
+constexpr int kOobeScreensCount = 47;
+constexpr int kLoginScreensCount = 45;
 constexpr int kOsInstallScreensCount = 2;
 
 std::string ElementsInPanel(const std::string& panel) {
@@ -32,7 +33,9 @@
 
 class DebugOverlayTest : public OobeBaseTest {
  public:
-  DebugOverlayTest() = default;
+  DebugOverlayTest() {
+    feature_list_.InitAndEnableFeature(features::kOobeChoobe);
+  }
 
   ~DebugOverlayTest() override = default;
 
@@ -40,6 +43,8 @@
     command_line->AppendSwitch(switches::kShowOobeDevOverlay);
     OobeBaseTest::SetUpCommandLine(command_line);
   }
+
+  base::test::ScopedFeatureList feature_list_;
 };
 
 class DebugOverlayOnLoginTest : public DebugOverlayTest {
diff --git a/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.cc b/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.cc
index 2539e35..61298e76 100644
--- a/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.cc
@@ -10,14 +10,18 @@
 
 #include "base/base64.h"
 #include "base/check.h"
+#include "base/containers/fixed_flat_map.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_forward.h"
 #include "base/guid.h"
+#include "base/json/json_writer.h"
 #include "base/logging.h"
-#include "base/notreached.h"
+#include "base/strings/string_piece_forward.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/types/expected.h"
 #include "base/values.h"
+#include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h"
 #include "chrome/common/channel_info.h"
 #include "chromeos/ash/components/attestation/attestation_flow.h"
 #include "chromeos/ash/components/dbus/attestation/keystore.pb.h"
@@ -42,12 +46,65 @@
 constexpr char kDeviceSigninBaseUrl[] =
     "https://devicesignin-pa.googleapis.com";
 constexpr char kGetChallengeDataApi[] = "/v1/getchallengedata";
+constexpr char kStartSessionApi[] = "/v1/startsession";
 
 // JSON keys.
 constexpr char kChallengeDataKey[] = "challengeData";
 constexpr char kChallengeKey[] = "challenge";
+constexpr char kSessionStatusKey[] = "session_status";
+constexpr char kRejectionReasonKey[] = "rejection_reason";
+constexpr char kTargetFallbackUrlKey[] = "target_fallback_url";
+constexpr char kSourceDeviceFallbackUrlKey[] = "source_device_fallback_url";
+constexpr char kEmailKey[] = "email";
+constexpr char kTargetSessionIdentifierKey[] = "target_session_identifier";
+constexpr char kCredentialIdKey[] = "credential_id";
+constexpr char kAuthenticatorDataKey[] = "authenticator_data";
+constexpr char kClientDataKey[] = "client_data";
+constexpr char kSignatureKey[] = "signature";
+constexpr char kFulfilledChallengeTypeKey[] = "fulfilled_challenge_type";
+constexpr char kAssertionInfoKey[] = "assertion_info";
+constexpr char kFallbackOptionKey[] = "fallback_option";
+constexpr char kDeviceTypeKey[] = "device_type";
+constexpr char kDeviceAttestationCertificateKey[] =
+    "device_attestation_certificate";
+constexpr char kClientIdKey[] = "client_id";
+constexpr char kChromeOsDeviceInfoKey[] = "chrome_os_device_info";
+constexpr char kFulfilledChallengeKey[] = "fulfilled_challenge";
+constexpr char kPlatformDataKey[] = "platform_data";
+constexpr char kSourceDeviceInfoKey[] = "source_device_info";
+constexpr char kTargetDeviceInfoKey[] = "target_device_info";
+constexpr char kCredentialDataKey[] = "credential_data";
+constexpr char kOauthTokenKey[] = "oauth_token";
 
 const int64_t kGetChallengeDataTimeoutInSeconds = 60;
+const int64_t kStartSessionTimeoutInSeconds = 60;
+constexpr char kHttpMethod[] = "POST";
+constexpr char kHttpContentType[] = "application/json";
+
+constexpr auto kRejectionReasonErrorMap = base::MakeFixedFlatMap<
+    base::StringPiece,
+    SecondDeviceAuthBroker::RefreshTokenRejectionResponse::Reason>({
+    {"invalid_oauth_token",
+     SecondDeviceAuthBroker::RefreshTokenRejectionResponse::Reason::
+         kInvalidOAuthToken},
+    {"account_not_supported",
+     SecondDeviceAuthBroker::RefreshTokenRejectionResponse::Reason::
+         kAccountNotSupported},
+    {"less_secure_device",
+     SecondDeviceAuthBroker::RefreshTokenRejectionResponse::Reason::
+         kLessSecureDevice},
+    {"already_authenticated",
+     SecondDeviceAuthBroker::RefreshTokenRejectionResponse::Reason::
+         kAlreadyAuthenticated},
+    {"session_expired", SecondDeviceAuthBroker::RefreshTokenRejectionResponse::
+                            Reason::kSessionExpired},
+    {"challenge_expired",
+     SecondDeviceAuthBroker::RefreshTokenRejectionResponse::Reason::
+         kChallengeExpired},
+    {"credential_id_mismatch",
+     SecondDeviceAuthBroker::RefreshTokenRejectionResponse::Reason::
+         kCredentialIdMismatch},
+});
 
 // Network annotations.
 constexpr net::NetworkTrafficAnnotationTag kChallengeDataAnnotation =
@@ -77,6 +134,35 @@
             "flow which is user-initiated, and is not a background request."
         }
       )");
+constexpr net::NetworkTrafficAnnotationTag kStartSessionAnnotation =
+    net::DefineNetworkTrafficAnnotation("quick_start_session_auth_requester",
+                                        R"(
+        semantics {
+          sender: "Chrome OS Start Screen"
+          description:
+            "Requests an OAuth authorization code from Google's authentication"
+            "server, in exchange for a FIDO assertion and a remote attestation"
+            "certificate"
+          trigger: "When the user starts the Quick Start flow from OOBE"
+          data:
+            "Authentication to this API is done through Chrome's API key. "
+            "Following information is sent to Google's authentication server -"
+            "1. FIDO assertion information - see FidoAssertionInfo struct."
+            "2. A remote attestation certificate signed by Google's Privacy CA."
+            "3. Chrome's OAuth client ID."
+          destination: GOOGLE_OWNED_SERVICE
+        }
+        policy {
+          cookies_allowed: NO
+          setting:
+            "There is no setting to disable the Quick Start flow. This request "
+            "is triggered as part of user interaction in OOBE Quick Start - "
+            "and is not a background request."
+          policy_exception_justification:
+            "Not implemented, not considered useful. This request is part of a "
+            "flow which is user-initiated, and is not a background request."
+        }
+      )");
 
 bool AreChallengeBytesValid(const std::string& challenge_bytes) {
   return base::Base64Decode(challenge_bytes).has_value();
@@ -195,16 +281,299 @@
   }
 }
 
+std::string CreateStartSessionRequestData(
+    const FidoAssertionInfo& fido_assertion_info,
+    const std::string& certificate) {
+  std::string request_string;
+
+  // This is the request format:
+  // {
+  //     "fulfilled_challenge": {
+  //         "fulfilled_challenge_type": "FIDO",
+  //         "assertion_info": {
+  //             "email": <Email as string>,
+  //             "credential_id": <Base64 encoded credential id as string>,
+  //             "authenticator_data": <Byte array of authenticator data>,
+  //             "client_data": <Byte array of client data>,
+  //             "signature": <Byte array of signature generated by the
+  //                           authenticator>
+  //         }
+  //     },
+  //     "platform_data": {
+  //         "fallback_option": "TARGET_ONLY"
+  //     },
+  //     "source_device_info": {
+  //         "device_type": "ANDROID"
+  //     },
+  //     "target_device_info": {
+  //         "chrome_os_device_info": {
+  //             "device_attestation_certificate": <Byte array of cert chain>,
+  //             "client_id": <Chrome's OAuth client id as string>,
+  //         },
+  //         "device_type": "CHROME_OS",
+  //     }
+  // }
+
+  base::Value::Dict assertion_info;
+  assertion_info.Set(kEmailKey, fido_assertion_info.email);
+  assertion_info.Set(kCredentialIdKey, fido_assertion_info.credential_id);
+  // The following fields are binary data that will be represented as a protobuf
+  // `bytes` field on Google's side. Protobuf guarantees a stable translation
+  // between byte arrays and Base64 encoded JSON fields.
+  assertion_info.Set(
+      kAuthenticatorDataKey,
+      base::Base64Encode(fido_assertion_info.authenticator_data));
+  assertion_info.Set(kClientDataKey,
+                     base::Base64Encode(fido_assertion_info.client_data));
+  assertion_info.Set(kSignatureKey,
+                     base::Base64Encode(fido_assertion_info.signature));
+
+  base::Value::Dict fulfilled_challenge;
+  fulfilled_challenge.Set(kFulfilledChallengeTypeKey, "FIDO");
+  fulfilled_challenge.Set(kAssertionInfoKey, std::move(assertion_info));
+
+  base::Value::Dict platform_data;
+  platform_data.Set(kFallbackOptionKey, "TARGET_ONLY");
+
+  base::Value::Dict source_device_info;
+  source_device_info.Set(kDeviceTypeKey, "ANDROID");
+
+  // TODO(b/259021973): Figure out how to send the device model here - after
+  // taking user's consent. Also change the network annotation after adding
+  // this.
+  base::Value::Dict chrome_os_device_info;
+  chrome_os_device_info.Set(kDeviceAttestationCertificateKey, certificate);
+  chrome_os_device_info.Set(
+      kClientIdKey,
+      google_apis::GetOAuth2ClientID(google_apis::OAuth2Client::CLIENT_MAIN));
+
+  base::Value::Dict target_device_info;
+  target_device_info.Set(kChromeOsDeviceInfoKey,
+                         std::move(chrome_os_device_info));
+  target_device_info.Set(kDeviceTypeKey, "CHROME_OS");
+
+  base::Value::Dict request;
+  request.Set(kFulfilledChallengeKey, std::move(fulfilled_challenge));
+  request.Set(kPlatformDataKey, std::move(platform_data));
+  request.Set(kSourceDeviceInfoKey, std::move(source_device_info));
+  request.Set(kTargetDeviceInfoKey, std::move(target_device_info));
+
+  base::JSONWriter::Write(request, &request_string);
+
+  return request_string;
+}
+
+void RunRefreshTokenCallbackWithRejectionResponse(
+    SecondDeviceAuthBroker::RefreshTokenCallback refresh_token_callback,
+    base::Value::Dict* response) {
+  SecondDeviceAuthBroker::RefreshTokenRejectionResponse rejection_response;
+
+  // Note that email may be empty.
+  rejection_response.email = *response->FindString(kEmailKey);
+  rejection_response.reason = SecondDeviceAuthBroker::
+      RefreshTokenRejectionResponse::Reason::kUnknownReason;
+  std::string* rejection_reason = response->FindString(kRejectionReasonKey);
+  if (!rejection_reason) {
+    LOG(ERROR) << "Could not fetch OAuth authorization code. Request rejected "
+                  "without providing a reason";
+    std::move(refresh_token_callback).Run(rejection_response);
+    return;
+  }
+
+  std::string rejection_reason_lowercase =
+      base::ToLowerASCII(*rejection_reason);
+  if (!kRejectionReasonErrorMap.contains(rejection_reason_lowercase)) {
+    LOG(ERROR) << "Could not fetch OAuth authorization code. Request rejected "
+                  "with unknown reason";
+    std::move(refresh_token_callback).Run(rejection_response);
+    return;
+  }
+  rejection_response.reason =
+      kRejectionReasonErrorMap.at(rejection_reason_lowercase);
+  std::move(refresh_token_callback).Run(rejection_response);
+}
+
+void RunRefreshTokenCallbackWithAdditionalChallengesOnTargetResponse(
+    SecondDeviceAuthBroker::RefreshTokenCallback refresh_token_callback,
+    base::Value::Dict* response) {
+  SecondDeviceAuthBroker::RefreshTokenAdditionalChallengesOnTargetResponse
+      additional_challenges_response;
+
+  // Note that email may be empty.
+  additional_challenges_response.email = *response->FindString(kEmailKey);
+  std::string* target_fallback_url =
+      response->FindString(kTargetFallbackUrlKey);
+  if (!target_fallback_url) {
+    LOG(ERROR) << "Could not fetch OAuth authorization code. Request required "
+                  "additional target challenges on unknown URL";
+    std::move(refresh_token_callback)
+        .Run(SecondDeviceAuthBroker::RefreshTokenParsingErrorResponse());
+    return;
+  }
+  additional_challenges_response.fallback_url = *target_fallback_url;
+
+  std::move(refresh_token_callback).Run(additional_challenges_response);
+}
+
+void RunRefreshTokenCallbackWithAdditionalChallengesOnSourceResponse(
+    SecondDeviceAuthBroker::RefreshTokenCallback refresh_token_callback,
+    base::Value::Dict* response) {
+  SecondDeviceAuthBroker::RefreshTokenAdditionalChallengesOnSourceResponse
+      additional_challenges_response;
+
+  // Note that email may be empty.
+  additional_challenges_response.email = *response->FindString(kEmailKey);
+  // May be empty.
+  additional_challenges_response.target_session_identifier =
+      *response->FindString(kTargetSessionIdentifierKey);
+  std::string* source_device_fallback_url =
+      response->FindString(kSourceDeviceFallbackUrlKey);
+  if (!source_device_fallback_url) {
+    LOG(ERROR) << "Could not fetch OAuth authorization code. Request required "
+                  "additional source challenges on unknown URL";
+    std::move(refresh_token_callback)
+        .Run(SecondDeviceAuthBroker::RefreshTokenParsingErrorResponse());
+    return;
+  }
+  additional_challenges_response.fallback_url = *source_device_fallback_url;
+
+  std::move(refresh_token_callback).Run(additional_challenges_response);
+}
+
+// Runs `refresh_token_callback` with the `result` of refresh token fetch.
+void RunRefreshTokenCallback(
+    const std::string& email,
+    SecondDeviceAuthBroker::RefreshTokenCallback refresh_token_callback,
+    const base::expected<std::string, GoogleServiceAuthError>& result) {
+  if (!result.has_value()) {
+    SecondDeviceAuthBroker::RefreshTokenRejectionResponse response;
+    response.email = email;
+    response.reason = SecondDeviceAuthBroker::RefreshTokenRejectionResponse::
+        Reason::kInvalidAuthorizationCode;
+    std::move(refresh_token_callback).Run(response);
+    return;
+  }
+
+  SecondDeviceAuthBroker::RefreshTokenSuccessResponse response;
+  response.email = email;
+  response.refresh_token = result.value();
+  std::move(refresh_token_callback).Run(response);
+}
+
+void FetchRefreshTokenAndRunCallback(
+    SecondDeviceAuthBroker::RefreshTokenCallback refresh_token_callback,
+    base::OnceCallback<
+        void(const std::string&,
+             SecondDeviceAuthBroker::RefreshTokenOrErrorCallback)>
+        refresh_token_fetcher,
+    base::Value::Dict* response) {
+  base::Value::Dict* credential_data = response->FindDict(kCredentialDataKey);
+  if (!credential_data) {
+    LOG(ERROR) << "Could not fetch OAuth refresh token. Could not find "
+                  "credential_data";
+    std::move(refresh_token_callback)
+        .Run(SecondDeviceAuthBroker::RefreshTokenParsingErrorResponse());
+    return;
+  }
+
+  std::string* auth_code = credential_data->FindString(kOauthTokenKey);
+  if (!auth_code) {
+    LOG(ERROR)
+        << "Could not fetch OAuth refresh token. Could not find oauth_token";
+    std::move(refresh_token_callback)
+        .Run(SecondDeviceAuthBroker::RefreshTokenParsingErrorResponse());
+    return;
+  }
+
+  SecondDeviceAuthBroker::RefreshTokenOrErrorCallback callback =
+      base::BindOnce(&RunRefreshTokenCallback,
+                     /*email=*/*response->FindString(kEmailKey),
+                     std::move(refresh_token_callback));
+  // Fetch refresh token with `auth_code` and respond to `callback` - which in
+  // turn will respond to `refresh_token_callback`.
+  std::move(refresh_token_fetcher).Run(*auth_code, std::move(callback));
+}
+
+void RunRefreshTokenCallbackFromParsedResponse(
+    SecondDeviceAuthBroker::RefreshTokenCallback refresh_token_callback,
+    std::unique_ptr<EndpointResponse> unparsed_response,
+    base::OnceCallback<
+        void(const std::string&,
+             SecondDeviceAuthBroker::RefreshTokenOrErrorCallback)>
+        refresh_token_fetcher,
+    data_decoder::DataDecoder::ValueOrError response) {
+  if (!response.has_value() || !response->is_dict()) {
+    // When we can't even parse the response, it most probably is an error from
+    // Google's FrontEnd (GFE) - which may not be sending JSON responses. Check
+    // if it's an auth error from GFE.
+    if (unparsed_response->error_type &&
+        unparsed_response->error_type.value() == FetchErrorType::kAuthError) {
+      SecondDeviceAuthBroker::RefreshTokenRejectionResponse rejection_response;
+      rejection_response.reason = SecondDeviceAuthBroker::
+          RefreshTokenRejectionResponse::Reason::kUnknownReason;
+      LOG(ERROR) << "Could not fetch OAuth authorization code. Received an "
+                    "auth error from server";
+      std::move(refresh_token_callback).Run(rejection_response);
+      return;
+    }
+
+    // We could not parse the response and it is not an auth error.
+    LOG(ERROR) << "Could not fetch OAuth authorization code. Error parsing "
+                  "response from server";
+    std::move(refresh_token_callback)
+        .Run(SecondDeviceAuthBroker::RefreshTokenParsingErrorResponse());
+    return;
+  }
+
+  std::string* session_status =
+      response->GetDict().FindString(kSessionStatusKey);
+  if (!session_status) {
+    LOG(ERROR) << "Could not fetch OAuth authorization code. Error parsing "
+                  "session status";
+    std::move(refresh_token_callback)
+        .Run(SecondDeviceAuthBroker::RefreshTokenParsingErrorResponse());
+    return;
+  }
+
+  if (base::ToLowerASCII(*session_status) == "rejected") {
+    RunRefreshTokenCallbackWithRejectionResponse(
+        std::move(refresh_token_callback), &response->GetDict());
+    return;
+  } else if (base::ToLowerASCII(*session_status) == "continue_on_target") {
+    RunRefreshTokenCallbackWithAdditionalChallengesOnTargetResponse(
+        std::move(refresh_token_callback), &response->GetDict());
+    return;
+  } else if (base::ToLowerASCII(*session_status) == "pending") {
+    RunRefreshTokenCallbackWithAdditionalChallengesOnSourceResponse(
+        std::move(refresh_token_callback), &response->GetDict());
+    return;
+  } else if (base::ToLowerASCII(*session_status) == "authenticated") {
+    FetchRefreshTokenAndRunCallback(std::move(refresh_token_callback),
+                                    std::move(refresh_token_fetcher),
+                                    &response->GetDict());
+    return;
+  }
+
+  // Unknown session status.
+  std::move(refresh_token_callback)
+      .Run(SecondDeviceAuthBroker::RefreshTokenUnknownErrorResponse());
+}
+
 }  // namespace
 
 SecondDeviceAuthBroker::SecondDeviceAuthBroker(
+    const std::string& device_id,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     std::unique_ptr<attestation::AttestationFlow> attestation_flow)
-    : url_loader_factory_(std::move(url_loader_factory)),
+    : device_id_(device_id),
+      url_loader_factory_(std::move(url_loader_factory)),
       attestation_(std::move(attestation_flow)),
       weak_ptr_factory_(this) {
   DCHECK(url_loader_factory_);
   DCHECK(attestation_);
+
+  gaia_auth_fetcher_ = std::make_unique<GaiaAuthFetcher>(
+      this, gaia::GaiaSource::kChromeOS, url_loader_factory_);
 }
 
 SecondDeviceAuthBroker::~SecondDeviceAuthBroker() = default;
@@ -217,8 +586,8 @@
   endpoint_fetcher_ = std::make_unique<EndpointFetcher>(
       /*url_loader_factory=*/url_loader_factory_,
       /*url=*/GURL(kDeviceSigninBaseUrl).Resolve(kGetChallengeDataApi),
-      /*http_method=*/"POST",
-      /*content_type=*/"application/json",
+      /*http_method=*/kHttpMethod,
+      /*content_type=*/kHttpContentType,
       /*timeout_ms=*/kGetChallengeDataTimeoutInSeconds * 1000,
       /*post_data=*/std::string(),
       /*headers=*/std::vector<std::string>(),
@@ -237,8 +606,8 @@
     ChallengeBytesCallback challenge_callback,
     std::unique_ptr<EndpointResponse> response) {
   DCHECK(endpoint_fetcher_)
-      << "Received an unexpected callback for challenge bytes";
-  // Reset the fetcher. It's existence is used to check for pending requests.
+      << "Received a callback for challenge bytes without a pending request";
+  // Reset the fetcher. Its existence is used to check for pending requests.
   endpoint_fetcher_.reset();
 
   if (response->http_status_code != google_apis::ApiErrorCode::HTTP_SUCCESS) {
@@ -280,8 +649,79 @@
     const FidoAssertionInfo& fido_assertion_info,
     const std::string& certificate,
     RefreshTokenCallback refresh_token_callback) {
-  // Fetch auth code. If successful, fetch refresh token.
-  NOTIMPLEMENTED();
+  DCHECK(!endpoint_fetcher_)
+      << "This class can handle only one request at a time";
+
+  endpoint_fetcher_ = std::make_unique<EndpointFetcher>(
+      /*url_loader_factory=*/url_loader_factory_,
+      /*url=*/GURL(kDeviceSigninBaseUrl).Resolve(kStartSessionApi),
+      /*http_method=*/kHttpMethod,
+      /*content_type=*/kHttpContentType,
+      /*timeout_ms=*/kStartSessionTimeoutInSeconds * 1000,
+      /*post_data=*/
+      CreateStartSessionRequestData(fido_assertion_info, certificate),
+      /*headers=*/std::vector<std::string>(),
+      /*annotation_tag=*/kStartSessionAnnotation,
+      /*is_stable_channel=*/chrome::GetChannel() ==
+          version_info::Channel::STABLE);
+
+  endpoint_fetcher_->PerformRequest(
+      base::BindOnce(&SecondDeviceAuthBroker::OnAuthorizationCodeFetched,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     std::move(refresh_token_callback)),
+      google_apis::GetAPIKey().c_str());
+}
+
+void SecondDeviceAuthBroker::OnAuthorizationCodeFetched(
+    RefreshTokenCallback refresh_token_callback,
+    std::unique_ptr<EndpointResponse> response) {
+  DCHECK(endpoint_fetcher_)
+      << "Received a callback for authorization code without a pending request";
+  // Reset the fetcher. Its existence is used to check for pending requests.
+  endpoint_fetcher_.reset();
+
+  if (response->http_status_code != google_apis::ApiErrorCode::HTTP_SUCCESS) {
+    LOG(ERROR) << "Could not fetch OAuth authorization code. HTTP status code: "
+               << response->http_status_code;
+  }
+
+  base::OnceCallback<void(const std::string&, RefreshTokenOrErrorCallback)>
+      refresh_token_fetcher = base::BindOnce(
+          &SecondDeviceAuthBroker::FetchRefreshTokenFromAuthorizationCode,
+          weak_ptr_factory_.GetWeakPtr());
+  // Creating a copy here because we are going to move `response` soon.
+  std::string unparsed_response = response->response;
+  data_decoder::DataDecoder::ParseJsonIsolated(
+      unparsed_response,
+      base::BindOnce(&RunRefreshTokenCallbackFromParsedResponse,
+                     std::move(refresh_token_callback), std::move(response),
+                     std::move(refresh_token_fetcher)));
+}
+
+void SecondDeviceAuthBroker::FetchRefreshTokenFromAuthorizationCode(
+    const std::string& authorization_code,
+    RefreshTokenOrErrorCallback callback) {
+  DCHECK(!refresh_token_internal_callback_)
+      << "This class can handle only one request at a time";
+  refresh_token_internal_callback_ = std::move(callback);
+  gaia_auth_fetcher_->StartAuthCodeForOAuth2TokenExchangeWithDeviceId(
+      authorization_code, device_id_);
+}
+
+void SecondDeviceAuthBroker::OnClientOAuthSuccess(
+    const ClientOAuthResult& result) {
+  DCHECK(refresh_token_internal_callback_)
+      << "Received an unexpected callback for refresh token";
+  std::move(refresh_token_internal_callback_)
+      .Run(base::ok(result.refresh_token));
+}
+
+void SecondDeviceAuthBroker::OnClientOAuthFailure(
+    const GoogleServiceAuthError& error) {
+  DCHECK(refresh_token_internal_callback_)
+      << "Received an unexpected callback for refresh token";
+  LOG(ERROR) << "Could not fetch refresh token. Error: " << error.ToString();
+  std::move(refresh_token_internal_callback_).Run(base::unexpected(error));
 }
 
 }  //  namespace ash::quick_start
diff --git a/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.h b/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.h
index 302662c..a9af5123 100644
--- a/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.h
+++ b/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.h
@@ -14,6 +14,7 @@
 #include "base/types/expected.h"
 #include "chromeos/ash/components/attestation/attestation_flow.h"
 #include "components/endpoint_fetcher/endpoint_fetcher.h"
+#include "google_apis/gaia/gaia_auth_consumer.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
 
 class GoogleServiceAuthError;
@@ -26,7 +27,7 @@
 
 struct FidoAssertionInfo;
 
-class SecondDeviceAuthBroker {
+class SecondDeviceAuthBroker : public GaiaAuthConsumer {
  public:
   enum class AttestationErrorType {
     // The error was temporary / transient and the request can be tried again.
@@ -81,6 +82,9 @@
 
       // Credential ID mismatch thrown during FIDO assertion verification.
       kCredentialIdMismatch,
+
+      // OAuth authorization code to refresh token exchange request failed.
+      kInvalidAuthorizationCode,
     };
 
     Reason reason;
@@ -110,6 +114,8 @@
       const base::expected<std::string, GoogleServiceAuthError>&)>;
   using AttestationCertificateCallback = base::OnceCallback<void(
       const base::expected<std::string, AttestationErrorType>&)>;
+  using RefreshTokenOrErrorCallback = base::OnceCallback<void(
+      const base::expected<std::string, GoogleServiceAuthError>&)>;
 
   // Possible set of response types for `RefreshTokenCallback`.
   using RefreshTokenResponse =
@@ -124,11 +130,12 @@
 
   // Constructs an instance of `SecondDeviceAuthBroker`.
   SecondDeviceAuthBroker(
+      const std::string& device_id,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       std::unique_ptr<attestation::AttestationFlow> attestation_flow);
   SecondDeviceAuthBroker(const SecondDeviceAuthBroker&) = delete;
   SecondDeviceAuthBroker& operator=(const SecondDeviceAuthBroker&) = delete;
-  ~SecondDeviceAuthBroker();
+  ~SecondDeviceAuthBroker() override;
 
   // Gets Base64 encoded nonce challenge bytes from Gaia SecondDeviceAuth
   // service.
@@ -159,6 +166,30 @@
   void OnChallengeBytesFetched(ChallengeBytesCallback challenge_callback,
                                std::unique_ptr<EndpointResponse> response);
 
+  // Callback for handling the response from Gaia to our request for an OAuth
+  // authorization code.
+  // If successful, the received OAuth authorization code is in turn exchanged
+  // for an LST, and `refresh_token_callback` is completed.
+  // Otherwise `refresh_token_callback` is completed with an appropriate
+  // `RefreshTokenResponse` type.
+  void OnAuthorizationCodeFetched(RefreshTokenCallback refresh_token_callback,
+                                  std::unique_ptr<EndpointResponse> response);
+
+  // Exchanges an OAuth `authorization_code` for an OAuth login scoped refresh
+  // token.
+  // The callback is completed with a refresh token or an error reason.
+  void FetchRefreshTokenFromAuthorizationCode(
+      const std::string& authorization_code,
+      RefreshTokenOrErrorCallback callback);
+
+  // `GaiaAuthConsumer` overrides.
+  void OnClientOAuthSuccess(const ClientOAuthResult& result) override;
+  void OnClientOAuthFailure(const GoogleServiceAuthError& error) override;
+
+  RefreshTokenOrErrorCallback refresh_token_internal_callback_;
+
+  const std::string device_id_;
+
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   // Used for fetching results from Gaia endpoints.
@@ -168,6 +199,9 @@
   // Attestation certificate.
   std::unique_ptr<attestation::AttestationFlow> attestation_;
 
+  // Used for fetching OAuth refresh tokens.
+  std::unique_ptr<GaiaAuthFetcher> gaia_auth_fetcher_;
+
   base::WeakPtrFactory<SecondDeviceAuthBroker> weak_ptr_factory_;
 };
 
diff --git a/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker_unittest.cc b/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker_unittest.cc
index 3f0c7ba..020b9f3 100644
--- a/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker_unittest.cc
+++ b/chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker_unittest.cc
@@ -13,10 +13,12 @@
 #include "base/test/bind.h"
 #include "base/test/task_environment.h"
 #include "base/types/expected.h"
+#include "chrome/browser/ash/login/oobe_quick_start/connectivity/fido_assertion_info.h"
 #include "chromeos/ash/components/attestation/attestation_flow.h"
 #include "chromeos/ash/components/attestation/mock_attestation_flow.h"
 #include "chromeos/ash/components/dbus/constants/attestation_constants.h"
 #include "components/account_id/account_id.h"
+#include "google_apis/gaia/gaia_urls.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "net/http/http_status_code.h"
 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
@@ -25,11 +27,15 @@
 #include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace ash::quick_start {
 
 using ::testing::_;
+using ::testing::AllOf;
 using ::testing::Eq;
+using ::testing::ExplainMatchResult;
+using ::testing::Field;
 using ::testing::Gt;
 using ::testing::Invoke;
 using ::testing::IsFalse;
@@ -39,32 +45,94 @@
 using ::testing::VariantWith;
 using ::testing::WithArg;
 
+using RefreshTokenUnknownErrorResponse =
+    SecondDeviceAuthBroker::RefreshTokenUnknownErrorResponse;
+using RefreshTokenSuccessResponse =
+    SecondDeviceAuthBroker::RefreshTokenSuccessResponse;
+using RefreshTokenParsingErrorResponse =
+    SecondDeviceAuthBroker::RefreshTokenParsingErrorResponse;
+using RefreshTokenRejectionResponse =
+    SecondDeviceAuthBroker::RefreshTokenRejectionResponse;
+using RefreshTokenAdditionalChallengesOnSourceResponse =
+    SecondDeviceAuthBroker::RefreshTokenAdditionalChallengesOnSourceResponse;
+using RefreshTokenAdditionalChallengesOnTargetResponse =
+    SecondDeviceAuthBroker::RefreshTokenAdditionalChallengesOnTargetResponse;
+
 namespace {
 
 constexpr char kGetChallengeDataUrl[] =
     "https://devicesignin-pa.googleapis.com/v1/getchallengedata";
-constexpr char kFakeChallengeDataResponse[] =
-    "{"
-    "\"challengeData\": {"
-    "  \"challenge\": "
-    "\"AKVcFQJJ0zreBQSrWiDJlFmeTr6K1Ik+"
-    "i58k4p5A64dYYcofARHmhUNQrh0vpYZ4zbOvyBSamG/"
-    "hyOxa7WdmZHLfEyobJ2FyifgY114deg==\""
-    "}"
-    "}";
-constexpr char kInvalidBase64ChallengeDataResponse[] =
-    "{"
-    "\"challengeData\": {"
-    "  \"challenge\": "
-    "\"Not-a-base64-character()\""
-    "}"
-    "}";
+constexpr char kStartSessionUrl[] =
+    "https://devicesignin-pa.googleapis.com/v1/startsession";
+constexpr char kFakeChallengeDataResponse[] = R"({
+      "challengeData": {
+        "challenge": "eA=="
+      }
+    })";
+constexpr char kInvalidBase64ChallengeDataResponse[] = R"({
+      "challengeData": {
+        "challenge": "Not-a-base64-character)("
+      }
+    })";
+constexpr char kOAuthRefreshTokenSuccessBody[] = R"({
+      "refresh_token": "fake-refresh-token",
+      "access_token": "fake-access-token",
+      "expires_in": 99999
+    })";
 constexpr char kFidoCredentialId[] = "fido_credential_id";
+constexpr char kCertificate[] = "fake_certificate";
+constexpr char kFakeDeviceId[] = "fake_device_id";
 
 MATCHER_P(ProtoBufContentBindingEq, expected, "") {
   return arg.content_binding() == expected;
 }
 
+MATCHER_P(RefreshTokenAdditionalChallengesOnTargetResponseEq, expected, "") {
+  return ExplainMatchResult(
+      AllOf(
+          Field("email",
+                &RefreshTokenAdditionalChallengesOnTargetResponse::email,
+                Eq(expected.email)),
+          Field("fallback_url",
+                &RefreshTokenAdditionalChallengesOnTargetResponse::fallback_url,
+                Eq(expected.fallback_url))),
+      arg, result_listener);
+}
+
+MATCHER_P(RefreshTokenAdditionalChallengesOnSourceResponseEq, expected, "") {
+  return ExplainMatchResult(
+      AllOf(
+          Field("email",
+                &RefreshTokenAdditionalChallengesOnSourceResponse::email,
+                Eq(expected.email)),
+          Field("fallback_url",
+                &RefreshTokenAdditionalChallengesOnSourceResponse::fallback_url,
+                Eq(expected.fallback_url)),
+          Field("target_session_identifier",
+                &RefreshTokenAdditionalChallengesOnSourceResponse::
+                    target_session_identifier,
+                Eq(expected.target_session_identifier))),
+      arg, result_listener);
+}
+
+MATCHER_P(RefreshTokenSuccessResponseEq, expected, "") {
+  return ExplainMatchResult(
+      AllOf(Field("email", &RefreshTokenSuccessResponse::email,
+                  Eq(expected.email)),
+            Field("refresh_token", &RefreshTokenSuccessResponse::refresh_token,
+                  Eq(expected.refresh_token))),
+      arg, result_listener);
+}
+
+MATCHER_P(RefreshTokenRejectionResponseEq, expected, "") {
+  return ExplainMatchResult(
+      AllOf(Field("email", &RefreshTokenRejectionResponse::email,
+                  Eq(expected.email)),
+            Field("reason", &RefreshTokenRejectionResponse::reason,
+                  Eq(expected.reason))),
+      arg, result_listener);
+}
+
 // This class simply delegates all API calls to its mock object. It is used for
 // passing information from inside `SecondDeviceAuthBroker` to the mock object,
 // without passing ownership of the mock object to `SecondDeviceAuthBroker`.
@@ -101,7 +169,8 @@
 class SecondDeviceAuthBrokerTest : public ::testing::Test {
  public:
   SecondDeviceAuthBrokerTest()
-      : second_device_auth_broker_(test_factory_.GetSafeWeakWrapper(),
+      : second_device_auth_broker_(kFakeDeviceId,
+                                   test_factory_.GetSafeWeakWrapper(),
                                    std::make_unique<MockAttestationFlowFacade>(
                                        &mock_attestation_flow_)) {}
 
@@ -149,8 +218,30 @@
     return response;
   }
 
-  void AddFakeResponse(const std::string& url, const std::string& response) {
-    test_factory_.AddResponse(url, response);
+  SecondDeviceAuthBroker::RefreshTokenResponse FetchRefreshToken(
+      const FidoAssertionInfo& fido_assertion_info,
+      const std::string& certificate) {
+    SecondDeviceAuthBroker::RefreshTokenResponse response;
+    base::RunLoop run_loop;
+    SecondDeviceAuthBroker::RefreshTokenCallback callback =
+        base::BindLambdaForTesting(
+            [&response,
+             &run_loop](const SecondDeviceAuthBroker::RefreshTokenResponse&
+                            returned_response) -> void {
+              response = returned_response;
+              run_loop.Quit();
+            });
+    second_device_auth_broker_.FetchRefreshToken(
+        fido_assertion_info, certificate, std::move(callback));
+    run_loop.Run();
+
+    return response;
+  }
+
+  void AddFakeResponse(const std::string& url,
+                       const std::string& response,
+                       net::HttpStatusCode status = net::HTTP_OK) {
+    test_factory_.AddResponse(url, response, status);
   }
 
   void SimulateAuthError(const std::string& url) {
@@ -158,6 +249,18 @@
                               net::HTTP_UNAUTHORIZED);
   }
 
+  void SimulateOAuthTokenFetchSuccess() {
+    test_factory_.AddResponse(
+        GaiaUrls::GetInstance()->oauth2_token_url().spec(),
+        kOAuthRefreshTokenSuccessBody);
+  }
+
+  void SimulateOAuthTokenFetchFailure() {
+    test_factory_.AddResponse(
+        GaiaUrls::GetInstance()->oauth2_token_url().spec(), /*content=*/"{}",
+        net::HTTP_BAD_REQUEST);
+  }
+
   attestation::MockAttestationFlow& mock_attestation_flow() {
     return mock_attestation_flow_;
   }
@@ -281,7 +384,6 @@
 
 TEST_F(SecondDeviceAuthBrokerTest,
        FetchAttestationCertificateReturnsACertificate) {
-  const std::string kCertificate = "fake_certificate";
   EXPECT_CALL(
       mock_attestation_flow(),
       GetCertificate(
@@ -295,10 +397,8 @@
               VariantWith<::attestation::DeviceSetupCertificateRequestMetadata>(
                   ProtoBufContentBindingEq(kFidoCredentialId))),
           /*callback*/ _))
-      .WillOnce(WithArg<
-                7>(Invoke([&kCertificate](
-                              attestation::AttestationFlow::CertificateCallback
-                                  callback) -> void {
+      .WillOnce(WithArg<7>(Invoke([](attestation::AttestationFlow::
+                                         CertificateCallback callback) -> void {
         std::move(callback).Run(
             /*status=*/ash::attestation::AttestationStatus::ATTESTATION_SUCCESS,
             /*pem_certificate_chain=*/kCertificate);
@@ -310,4 +410,121 @@
   EXPECT_THAT(response.value(), Eq(kCertificate));
 }
 
+TEST_F(SecondDeviceAuthBrokerTest,
+       FetchRefreshTokenReturnsUnknownErrorResponseForUnknownErrors) {
+  AddFakeResponse(kStartSessionUrl, std::string(R"(
+      {
+        "session_status": "UNKNOWN_SESSION_STATUS"
+      }
+    )"));
+  SecondDeviceAuthBroker::RefreshTokenResponse response =
+      FetchRefreshToken(/*fido_assertion_info=*/FidoAssertionInfo{},
+                        /*certificate=*/kCertificate);
+  EXPECT_THAT(response, VariantWith<RefreshTokenUnknownErrorResponse>(_));
+}
+
+TEST_F(SecondDeviceAuthBrokerTest,
+       FetchRefreshTokenReturnsRejectionResponseForRequestRejectionsByServer) {
+  SimulateAuthError(kStartSessionUrl);
+  SecondDeviceAuthBroker::RefreshTokenResponse response =
+      FetchRefreshToken(/*fido_assertion_info=*/FidoAssertionInfo{},
+                        /*certificate=*/kCertificate);
+  EXPECT_THAT(response, VariantWith<RefreshTokenRejectionResponse>(_));
+}
+
+TEST_F(
+    SecondDeviceAuthBrokerTest,
+    FetchRefreshTokenReturnsAdditionalChallengesOnSourceResponseForSourceChallenges) {
+  AddFakeResponse(kStartSessionUrl, std::string(R"(
+      {
+        "session_status": "PENDING",
+        "target_session_identifier": "fake-target-session",
+        "source_device_fallback_url": "https://example.com",
+        "email": "fake-user@example.com"
+      }
+    )"));
+
+  RefreshTokenAdditionalChallengesOnSourceResponse expected_response;
+  expected_response.email = "fake-user@example.com";
+  expected_response.fallback_url = "https://example.com";
+  expected_response.target_session_identifier = "fake-target-session";
+  SecondDeviceAuthBroker::RefreshTokenResponse response =
+      FetchRefreshToken(/*fido_assertion_info=*/FidoAssertionInfo{},
+                        /*certificate=*/kCertificate);
+  EXPECT_THAT(response,
+              VariantWith<RefreshTokenAdditionalChallengesOnSourceResponse>(
+                  RefreshTokenAdditionalChallengesOnSourceResponseEq(
+                      expected_response)));
+}
+
+TEST_F(
+    SecondDeviceAuthBrokerTest,
+    FetchRefreshTokenReturnsAdditionalChallengesOnTargetResponseForTargetChallenges) {
+  AddFakeResponse(kStartSessionUrl, std::string(R"(
+      {
+        "session_status": "CONTINUE_ON_TARGET",
+        "target_fallback_url": "https://example.com",
+        "email": "fake-user@example.com"
+      }
+    )"));
+
+  RefreshTokenAdditionalChallengesOnTargetResponse expected_response;
+  expected_response.email = "fake-user@example.com";
+  expected_response.fallback_url = "https://example.com";
+  SecondDeviceAuthBroker::RefreshTokenResponse response =
+      FetchRefreshToken(/*fido_assertion_info=*/FidoAssertionInfo{},
+                        /*certificate=*/kCertificate);
+  EXPECT_THAT(response,
+              VariantWith<RefreshTokenAdditionalChallengesOnTargetResponse>(
+                  RefreshTokenAdditionalChallengesOnTargetResponseEq(
+                      expected_response)));
+}
+
+TEST_F(SecondDeviceAuthBrokerTest, FetchRefreshTokenReturnsARefreshToken) {
+  AddFakeResponse(kStartSessionUrl, std::string(R"(
+      {
+        "session_status": "AUTHENTICATED",
+        "credential_data": {
+            "oauth_token": "fake-auth-code"
+        },
+        "email": "fake-user@example.com"
+      }
+    )"));
+  SimulateOAuthTokenFetchSuccess();
+
+  RefreshTokenSuccessResponse expected_response;
+  expected_response.email = "fake-user@example.com";
+  expected_response.refresh_token = "fake-refresh-token";
+  SecondDeviceAuthBroker::RefreshTokenResponse response =
+      FetchRefreshToken(/*fido_assertion_info=*/FidoAssertionInfo{},
+                        /*certificate=*/kCertificate);
+  EXPECT_THAT(response, VariantWith<RefreshTokenSuccessResponse>(
+                            RefreshTokenSuccessResponseEq(expected_response)));
+}
+
+TEST_F(SecondDeviceAuthBrokerTest,
+       FetchRefreshTokenReturnsAnErrorForInvalidAuthorizationCode) {
+  AddFakeResponse(kStartSessionUrl, std::string(R"(
+      {
+        "session_status": "AUTHENTICATED",
+        "credential_data": {
+            "oauth_token": "fake-auth-code"
+        },
+        "email": "fake-user@example.com"
+      }
+    )"));
+  SimulateOAuthTokenFetchFailure();
+
+  RefreshTokenRejectionResponse expected_response;
+  expected_response.email = "fake-user@example.com";
+  expected_response.reason =
+      RefreshTokenRejectionResponse::Reason::kInvalidAuthorizationCode;
+  SecondDeviceAuthBroker::RefreshTokenResponse response =
+      FetchRefreshToken(/*fido_assertion_info=*/FidoAssertionInfo{},
+                        /*certificate=*/kCertificate);
+  EXPECT_THAT(response,
+              VariantWith<RefreshTokenRejectionResponse>(
+                  RefreshTokenRejectionResponseEq(expected_response)));
+}
+
 }  //  namespace ash::quick_start
diff --git a/chrome/browser/ash/policy/remote_commands/crd_remote_command_utils.cc b/chrome/browser/ash/policy/remote_commands/crd_remote_command_utils.cc
index dfddd84..5fcf83a 100644
--- a/chrome/browser/ash/policy/remote_commands/crd_remote_command_utils.cc
+++ b/chrome/browser/ash/policy/remote_commands/crd_remote_command_utils.cc
@@ -116,52 +116,57 @@
   const auto& user_manager = CHECK_DEREF(user_manager::UserManager::Get());
 
   if (!user_manager.IsUserLoggedIn())
-    return UserSessionType::kNoUser;
+    return UserSessionType::NO_SESSION;
 
   if (user_manager.IsLoggedInAsAnyKioskApp()) {
     if (IsRunningAutoLaunchedKiosk(user_manager))
-      return UserSessionType::kAutoLaunchedKiosk;
+      return UserSessionType::AUTO_LAUNCHED_KIOSK_SESSION;
     else
-      return UserSessionType::kManuallyLaunchedKiosk;
+      return UserSessionType::MANUALLY_LAUNCHED_KIOSK_SESSION;
   }
 
   if (user_manager.IsLoggedInAsPublicAccount())
-    return UserSessionType::kManagedGuestSession;
+    return UserSessionType::MANAGED_GUEST_SESSION;
 
   if (user_manager.IsLoggedInAsGuest())
-    return UserSessionType::kOther;
+    return UserSessionType::GUEST_SESSION;
 
   if (user_manager.GetActiveUser()->IsAffiliated())
-    return UserSessionType::kAffiliatedUser;
+    return UserSessionType::AFFILIATED_USER_SESSION;
 
-  return UserSessionType::kOther;
+  return UserSessionType::UNAFFILIATED_USER_SESSION;
 }
 
 bool UserSessionSupportsRemoteAccess(UserSessionType user_session) {
   // Remote access is currently only supported while no user is logged in
   // (and the device sits at the login screen).
   switch (user_session) {
-    case UserSessionType::kNoUser:
+    case UserSessionType::NO_SESSION:
       return true;
 
-    case UserSessionType::kAutoLaunchedKiosk:
-    case UserSessionType::kManuallyLaunchedKiosk:
-    case UserSessionType::kAffiliatedUser:
-    case UserSessionType::kManagedGuestSession:
-    case UserSessionType::kOther:
+    case UserSessionType::AUTO_LAUNCHED_KIOSK_SESSION:
+    case UserSessionType::MANUALLY_LAUNCHED_KIOSK_SESSION:
+    case UserSessionType::AFFILIATED_USER_SESSION:
+    case UserSessionType::MANAGED_GUEST_SESSION:
+    case UserSessionType::UNAFFILIATED_USER_SESSION:
+    case UserSessionType::GUEST_SESSION:
+    case UserSessionType::USER_SESSION_TYPE_UNKNOWN:
       return false;
   }
 }
+
 bool UserSessionSupportsRemoteSupport(UserSessionType user_session) {
   switch (user_session) {
-    case UserSessionType::kAutoLaunchedKiosk:
-    case UserSessionType::kManuallyLaunchedKiosk:
-    case UserSessionType::kAffiliatedUser:
-    case UserSessionType::kManagedGuestSession:
+    case UserSessionType::AUTO_LAUNCHED_KIOSK_SESSION:
+    case UserSessionType::MANUALLY_LAUNCHED_KIOSK_SESSION:
+    case UserSessionType::AFFILIATED_USER_SESSION:
+    case UserSessionType::MANAGED_GUEST_SESSION:
       return true;
 
-    case UserSessionType::kNoUser:
-    case UserSessionType::kOther:
+    case UserSessionType::NO_SESSION:
+    case UserSessionType::UNAFFILIATED_USER_SESSION:
+    case UserSessionType::GUEST_SESSION:
+    case UserSessionType::USER_SESSION_TYPE_UNKNOWN:
       return false;
   }
 }
@@ -172,12 +177,14 @@
     return #type_;
 
   switch (value) {
-    CASE(kAutoLaunchedKiosk);
-    CASE(kManuallyLaunchedKiosk);
-    CASE(kNoUser);
-    CASE(kAffiliatedUser);
-    CASE(kOther);
-    CASE(kManagedGuestSession);
+    CASE(AUTO_LAUNCHED_KIOSK_SESSION);
+    CASE(MANUALLY_LAUNCHED_KIOSK_SESSION);
+    CASE(NO_SESSION);
+    CASE(AFFILIATED_USER_SESSION);
+    CASE(UNAFFILIATED_USER_SESSION);
+    CASE(MANAGED_GUEST_SESSION);
+    CASE(GUEST_SESSION);
+    CASE(USER_SESSION_TYPE_UNKNOWN);
   }
 
 #undef CASE
diff --git a/chrome/browser/ash/policy/remote_commands/crd_remote_command_utils.h b/chrome/browser/ash/policy/remote_commands/crd_remote_command_utils.h
index 3cba722..30f458b 100644
--- a/chrome/browser/ash/policy/remote_commands/crd_remote_command_utils.h
+++ b/chrome/browser/ash/policy/remote_commands/crd_remote_command_utils.h
@@ -11,17 +11,13 @@
 
 #include "base/functional/callback_forward.h"
 #include "base/time/time.h"
+#include "components/policy/proto/device_management_backend.pb.h"
 
 namespace policy {
 
-enum class UserSessionType {
-  kAutoLaunchedKiosk,
-  kManuallyLaunchedKiosk,
-  kNoUser,
-  kAffiliatedUser,
-  kManagedGuestSession,
-  kOther,
-};
+// The current active session type on the device, or `NO_SESSION` if no user
+// is currently logged in.
+using ::enterprise_management::UserSessionType;
 
 // Returns the time since the last user activity on this device.
 base::TimeDelta GetDeviceIdleTime();
@@ -32,6 +28,7 @@
 // Returns if a remote admin is allowed to start a 'CRD remote support' session
 // when an user session of the given type is active.
 bool UserSessionSupportsRemoteSupport(UserSessionType user_session);
+
 // Returns if a remote admin is allowed to start a 'CRD remote access' session
 // when an user session of the given type is active.
 bool UserSessionSupportsRemoteAccess(UserSessionType user_session);
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_job.cc b/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_job.cc
index 18a979a3..6af7fad 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_job.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_job.cc
@@ -389,18 +389,20 @@
 DeviceCommandStartCrdSessionJob::UmaSessionType
 DeviceCommandStartCrdSessionJob::GetUmaSessionType() const {
   switch (GetCurrentUserSessionType()) {
-    case UserSessionType::kAutoLaunchedKiosk:
+    case UserSessionType::AUTO_LAUNCHED_KIOSK_SESSION:
       return UmaSessionType::kAutoLaunchedKiosk;
-    case UserSessionType::kAffiliatedUser:
+    case UserSessionType::AFFILIATED_USER_SESSION:
       return UmaSessionType::kAffiliatedUser;
-    case UserSessionType::kManagedGuestSession:
+    case UserSessionType::MANAGED_GUEST_SESSION:
       return UmaSessionType::kManagedGuestSession;
-    case UserSessionType::kManuallyLaunchedKiosk:
+    case UserSessionType::MANUALLY_LAUNCHED_KIOSK_SESSION:
       return UmaSessionType::kManuallyLaunchedKiosk;
-    case UserSessionType::kNoUser:
+    case UserSessionType::NO_SESSION:
       // TODO(b/236689277): Introduce UmaSessionType::kNoLocalUser.
       return UmaSessionType::kMaxValue;
-    case UserSessionType::kOther:
+    case UserSessionType::UNAFFILIATED_USER_SESSION:
+    case UserSessionType::GUEST_SESSION:
+    case UserSessionType::USER_SESSION_TYPE_UNKNOWN:
       NOTREACHED();
       return UmaSessionType::kMaxValue;
   }
@@ -420,14 +422,19 @@
 
 bool DeviceCommandStartCrdSessionJob::ShouldShowConfirmationDialog() const {
   switch (GetCurrentUserSessionType()) {
-    case UserSessionType::kAutoLaunchedKiosk:
-    case UserSessionType::kManuallyLaunchedKiosk:
-    case UserSessionType::kNoUser:
+    case UserSessionType::AUTO_LAUNCHED_KIOSK_SESSION:
+    case UserSessionType::MANUALLY_LAUNCHED_KIOSK_SESSION:
+    case UserSessionType::NO_SESSION:
       return false;
 
-    case UserSessionType::kAffiliatedUser:
-    case UserSessionType::kManagedGuestSession:
-    case UserSessionType::kOther:
+    case UserSessionType::AFFILIATED_USER_SESSION:
+    case UserSessionType::MANAGED_GUEST_SESSION:
+    case UserSessionType::UNAFFILIATED_USER_SESSION:
+    case UserSessionType::GUEST_SESSION:
+      return true;
+
+    case UserSessionType::USER_SESSION_TYPE_UNKNOWN:
+      NOTREACHED();
       return true;
   }
 }
@@ -437,8 +444,8 @@
     return false;
 
   switch (GetCurrentUserSessionType()) {
-    case UserSessionType::kAffiliatedUser:
-    case UserSessionType::kManagedGuestSession:
+    case UserSessionType::AFFILIATED_USER_SESSION:
+    case UserSessionType::MANAGED_GUEST_SESSION:
       // We never terminate upon input for the user-session scenarios, because:
       //   1. There is no risk of the admin spying on the users, as they need to
       //       explicitly accept the connection request.
@@ -448,12 +455,17 @@
       //      user input.
       return false;
 
-    case UserSessionType::kAutoLaunchedKiosk:
-    case UserSessionType::kManuallyLaunchedKiosk:
+    case UserSessionType::AUTO_LAUNCHED_KIOSK_SESSION:
+    case UserSessionType::MANUALLY_LAUNCHED_KIOSK_SESSION:
       return !acked_user_presence_;
 
-    case UserSessionType::kNoUser:
-    case UserSessionType::kOther:
+    case UserSessionType::NO_SESSION:
+    case UserSessionType::UNAFFILIATED_USER_SESSION:
+    case UserSessionType::GUEST_SESSION:
+      return true;
+
+    case UserSessionType::USER_SESSION_TYPE_UNKNOWN:
+      NOTREACHED();
       return true;
   }
 }
diff --git a/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_unittest.cc b/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_unittest.cc
index 4eceb5a..3d27229 100644
--- a/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_unittest.cc
+++ b/chrome/browser/ash/policy/remote_commands/device_command_start_crd_session_unittest.cc
@@ -18,7 +18,7 @@
 #include "chrome/browser/ash/app_mode/arc/arc_kiosk_app_manager.h"
 #include "chrome/browser/ash/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/ash/app_mode/web_app/web_kiosk_app_manager.h"
-#include "chrome/browser/ash/policy/remote_commands/fake_cros_network_config_base.h"
+#include "chrome/browser/ash/policy/remote_commands/fake_cros_network_config.h"
 #include "chrome/browser/ash/settings/device_settings_test_helper.h"
 #include "chrome/browser/device_identity/device_oauth2_token_service.h"
 #include "chrome/browser/device_identity/device_oauth2_token_service_factory.h"
@@ -43,11 +43,7 @@
 using extensions::DictionaryBuilder;
 using ResultCode = DeviceCommandStartCrdSessionJob::ResultCode;
 using UmaSessionType = DeviceCommandStartCrdSessionJob::UmaSessionType;
-using chromeos::network_config::mojom::NetworkStateProperties;
-using chromeos::network_config::mojom::NetworkStatePropertiesPtr;
 using chromeos::network_config::mojom::NetworkType;
-using chromeos::network_config::mojom::NetworkTypeStateProperties;
-using chromeos::network_config::mojom::NetworkTypeStatePropertiesPtr;
 using chromeos::network_config::mojom::OncSource;
 using remoting::features::kEnableCrdAdminRemoteAccess;
 
@@ -155,111 +151,15 @@
   }
 }
 
+test::NetworkBuilder CreateNetwork(NetworkType type = NetworkType::kWiFi) {
+  return test::NetworkBuilder(type);
+}
+
 struct Result {
   RemoteCommandJob::Status status;
   std::string payload;
 };
 
-// Helper class that creates a `NetworkStatePropertiesPtr`.
-class NetworkBuilder {
- public:
-  explicit NetworkBuilder(NetworkType type) {
-    network_->type = type;
-    network_->guid = "<network-guid>";
-    network_->type_state = CreateTypeStateForType(type);
-  }
-  NetworkBuilder(NetworkBuilder&& other) = default;
-  NetworkBuilder& operator=(NetworkBuilder&&) = default;
-
-  NetworkBuilder(const NetworkBuilder& other)
-      : network_(other.network_.Clone()) {}
-  void operator=(const NetworkBuilder& other) {
-    this->network_ = other.network_.Clone();
-  }
-
-  ~NetworkBuilder() = default;
-
-  NetworkBuilder& SetOncSource(OncSource source) {
-    network_->source = source;
-    return *this;
-  }
-
-  NetworkStatePropertiesPtr Build() const { return network_.Clone(); }
-
- private:
-  NetworkTypeStatePropertiesPtr CreateTypeStateForType(NetworkType type) {
-    switch (type) {
-      case NetworkType::kCellular:
-        return NetworkTypeStateProperties::NewCellular(
-            chromeos::network_config::mojom::CellularStateProperties::New());
-      case NetworkType::kEthernet:
-        return NetworkTypeStateProperties::NewEthernet(
-            chromeos::network_config::mojom::EthernetStateProperties::New());
-      case NetworkType::kTether:
-        return NetworkTypeStateProperties::NewTether(
-            chromeos::network_config::mojom::TetherStateProperties::New());
-      case NetworkType::kVPN:
-        return NetworkTypeStateProperties::NewVpn(
-            chromeos::network_config::mojom::VPNStateProperties::New());
-      case NetworkType::kWiFi:
-        return NetworkTypeStateProperties::NewWifi(
-            chromeos::network_config::mojom::WiFiStateProperties::New());
-      case NetworkType::kAll:
-      case NetworkType::kMobile:
-      case NetworkType::kWireless:
-        // These are not actual network types, but just shorthands used while
-        // filtering.
-        NOTREACHED();
-        break;
-    }
-    NOTREACHED();
-    return nullptr;
-  }
-  NetworkStatePropertiesPtr network_ = NetworkStateProperties::New();
-};
-
-NetworkBuilder CreateNetwork(NetworkType type = NetworkType::kWiFi) {
-  return NetworkBuilder(type);
-}
-
-// Fake implementation of `CrosNetworkConfig` that simply stores a list of
-// networks that will be returned on each `GetNetworkStateList()` call.
-class FakeCrosNetworkConfig : public FakeCrosNetworkConfigBase {
- public:
-  FakeCrosNetworkConfig() = default;
-  FakeCrosNetworkConfig(const FakeCrosNetworkConfig&) = delete;
-  FakeCrosNetworkConfig& operator=(const FakeCrosNetworkConfig&) = delete;
-  ~FakeCrosNetworkConfig() override = default;
-
-  void SetActiveNetworks(std::vector<NetworkBuilder> networks) {
-    active_networks_.clear();
-    for (auto& builder : networks) {
-      active_networks_.push_back(builder.Build());
-    }
-  }
-
-  void AddActiveNetwork(NetworkBuilder builder) {
-    active_networks_.push_back(builder.Build());
-  }
-
-  void ClearActiveNetworks() { active_networks_.clear(); }
-
-  // `FakeCrosNetworkConfigBase` implementation:
-  void GetNetworkStateList(
-      chromeos::network_config::mojom::NetworkFilterPtr filter,
-      GetNetworkStateListCallback callback) override {
-    std::vector<NetworkStatePropertiesPtr> networks;
-    for (const auto& network : active_networks_) {
-      networks.push_back(network->Clone());
-    }
-
-    std::move(callback).Run(std::move(networks));
-  }
-
- private:
-  std::vector<NetworkStatePropertiesPtr> active_networks_;
-};
-
 class MockCrosNetworkConfig : public FakeCrosNetworkConfigBase {
  public:
   MockCrosNetworkConfig() = default;
@@ -293,14 +193,11 @@
     chromeos::SystemSaltGetter::Initialize();
     DeviceOAuth2TokenServiceFactory::Initialize(
         test_url_loader_factory_.GetSafeWeakWrapper(), &local_state_);
+    // The token service also requires local state.
     RegisterLocalState(local_state_.registry());
-
-    chromeos::network_config::OverrideInProcessInstanceForTesting(
-        &fake_cros_network_config_);
   }
 
   void TearDown() override {
-    chromeos::network_config::OverrideInProcessInstanceForTesting(nullptr);
     DeviceOAuth2TokenServiceFactory::Shutdown();
     chromeos::SystemSaltGetter::Shutdown();
 
@@ -444,7 +341,7 @@
     return launched;
   }
 
-  FakeCrosNetworkConfig& fake_cros_network_config() {
+  test::FakeCrosNetworkConfig& fake_cros_network_config() {
     return fake_cros_network_config_;
   }
 
@@ -473,7 +370,7 @@
   StubCrdHostDelegate crd_host_delegate_;
   std::unique_ptr<DeviceCommandStartCrdSessionJob> job_;
 
-  FakeCrosNetworkConfig fake_cros_network_config_;
+  test::ScopedFakeCrosNetworkConfig fake_cros_network_config_;
 
   // Future value that will be populated with the result once the remote
   // command job is completed.
diff --git a/chrome/browser/ash/policy/remote_commands/fake_cros_network_config.cc b/chrome/browser/ash/policy/remote_commands/fake_cros_network_config.cc
new file mode 100644
index 0000000..e357b98
--- /dev/null
+++ b/chrome/browser/ash/policy/remote_commands/fake_cros_network_config.cc
@@ -0,0 +1,134 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ash/policy/remote_commands/fake_cros_network_config.h"
+#include "base/strings/stringprintf.h"
+#include "chromeos/services/network_config/in_process_instance.h"
+
+namespace policy::test {
+
+namespace {
+
+using chromeos::network_config::mojom::NetworkStateProperties;
+using chromeos::network_config::mojom::NetworkStatePropertiesPtr;
+using chromeos::network_config::mojom::NetworkType;
+using chromeos::network_config::mojom::NetworkTypeStateProperties;
+using chromeos::network_config::mojom::NetworkTypeStatePropertiesPtr;
+using chromeos::network_config::mojom::OncSource;
+
+// Used to generate unique network GUIDs.
+static int g_unique_network_id = 1;
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+/// FakeCrosNetworkConfig
+////////////////////////////////////////////////////////////////////////////////
+
+FakeCrosNetworkConfig::FakeCrosNetworkConfig() = default;
+FakeCrosNetworkConfig::~FakeCrosNetworkConfig() = default;
+
+void FakeCrosNetworkConfig::SetActiveNetworks(
+    std::vector<NetworkBuilder> networks) {
+  active_networks_.clear();
+  for (auto& builder : networks) {
+    active_networks_.push_back(builder.Build());
+  }
+}
+
+void FakeCrosNetworkConfig::AddActiveNetwork(NetworkBuilder builder) {
+  active_networks_.push_back(builder.Build());
+}
+
+void FakeCrosNetworkConfig::ClearActiveNetworks() {
+  active_networks_.clear();
+}
+
+void FakeCrosNetworkConfig::GetNetworkStateList(
+    chromeos::network_config::mojom::NetworkFilterPtr filter,
+    GetNetworkStateListCallback callback) {
+  std::vector<NetworkStatePropertiesPtr> networks;
+  for (const auto& network : active_networks_) {
+    networks.push_back(network->Clone());
+  }
+
+  std::move(callback).Run(std::move(networks));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// NetworkBuilder
+////////////////////////////////////////////////////////////////////////////////
+
+NetworkBuilder::NetworkBuilder(NetworkBuilder&&) = default;
+NetworkBuilder& NetworkBuilder::operator=(NetworkBuilder&&) = default;
+NetworkBuilder::~NetworkBuilder() = default;
+NetworkBuilder::NetworkBuilder(const NetworkBuilder& other)
+    : network_(other.network_.Clone()) {}
+void NetworkBuilder::operator=(const NetworkBuilder& other) {
+  this->network_ = other.network_.Clone();
+}
+
+NetworkBuilder::NetworkBuilder(NetworkType type, const std::string& guid) {
+  network_->type = type;
+  if (guid.empty()) {
+    network_->guid =
+        base::StringPrintf("<network-guid-%i>", g_unique_network_id++);
+  } else {
+    network_->guid = guid;
+  }
+  network_->type_state = CreateTypeStateForType(type);
+}
+
+NetworkBuilder& NetworkBuilder::SetOncSource(OncSource source) {
+  network_->source = source;
+  return *this;
+}
+
+NetworkStatePropertiesPtr NetworkBuilder::Build() const {
+  return network_.Clone();
+}
+
+NetworkTypeStatePropertiesPtr NetworkBuilder::CreateTypeStateForType(
+    NetworkType type) {
+  switch (type) {
+    case NetworkType::kCellular:
+      return NetworkTypeStateProperties::NewCellular(
+          chromeos::network_config::mojom::CellularStateProperties::New());
+    case NetworkType::kEthernet:
+      return NetworkTypeStateProperties::NewEthernet(
+          chromeos::network_config::mojom::EthernetStateProperties::New());
+    case NetworkType::kTether:
+      return NetworkTypeStateProperties::NewTether(
+          chromeos::network_config::mojom::TetherStateProperties::New());
+    case NetworkType::kVPN:
+      return NetworkTypeStateProperties::NewVpn(
+          chromeos::network_config::mojom::VPNStateProperties::New());
+    case NetworkType::kWiFi:
+      return NetworkTypeStateProperties::NewWifi(
+          chromeos::network_config::mojom::WiFiStateProperties::New());
+    case NetworkType::kAll:
+    case NetworkType::kMobile:
+    case NetworkType::kWireless:
+      // These are not actual network types, but just shorthands used while
+      // filtering.
+      NOTREACHED();
+      break;
+  }
+  NOTREACHED();
+  return nullptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/// ScopedFakeCrosNetworkConfig
+////////////////////////////////////////////////////////////////////////////////
+
+ScopedFakeCrosNetworkConfig::ScopedFakeCrosNetworkConfig() {
+  chromeos::network_config::OverrideInProcessInstanceForTesting(this);
+}
+
+ScopedFakeCrosNetworkConfig::~ScopedFakeCrosNetworkConfig() {
+  chromeos::network_config::OverrideInProcessInstanceForTesting(nullptr);
+}
+
+}  // namespace policy::test
diff --git a/chrome/browser/ash/policy/remote_commands/fake_cros_network_config.h b/chrome/browser/ash/policy/remote_commands/fake_cros_network_config.h
new file mode 100644
index 0000000..13ebc0c
--- /dev/null
+++ b/chrome/browser/ash/policy/remote_commands/fake_cros_network_config.h
@@ -0,0 +1,81 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_ASH_POLICY_REMOTE_COMMANDS_FAKE_CROS_NETWORK_CONFIG_H_
+#define CHROME_BROWSER_ASH_POLICY_REMOTE_COMMANDS_FAKE_CROS_NETWORK_CONFIG_H_
+
+#include <string>
+#include <vector>
+
+#include "chrome/browser/ash/policy/remote_commands/fake_cros_network_config_base.h"
+#include "chromeos/services/network_config/public/mojom/cros_network_config.mojom-forward.h"
+
+namespace policy::test {
+
+class NetworkBuilder;
+
+// Fake implementation of `CrosNetworkConfig` that simply stores a list of
+// networks that will be returned on each `GetNetworkStateList()` call.
+class FakeCrosNetworkConfig : public FakeCrosNetworkConfigBase {
+ public:
+  FakeCrosNetworkConfig();
+  FakeCrosNetworkConfig(const FakeCrosNetworkConfig&) = delete;
+  FakeCrosNetworkConfig& operator=(const FakeCrosNetworkConfig&) = delete;
+  ~FakeCrosNetworkConfig() override;
+
+  void SetActiveNetworks(std::vector<NetworkBuilder> networks);
+  void AddActiveNetwork(NetworkBuilder builder);
+
+  void ClearActiveNetworks();
+
+  // `FakeCrosNetworkConfigBase` implementation:
+  // Warning: this implementation does *not* actually use the filter.
+  // Instead, it always returns all networks added through `SetActiveNetworks()`
+  // and `AddActiveNetwork()`.
+  void GetNetworkStateList(
+      chromeos::network_config::mojom::NetworkFilterPtr filter,
+      GetNetworkStateListCallback callback) override;
+
+ private:
+  std::vector<chromeos::network_config::mojom::NetworkStatePropertiesPtr>
+      active_networks_;
+};
+
+// Same as `FakeCrosNetworkConfig` but will install itself as a singleton in
+// its constructor (and uninstall itself in its destructor).
+class ScopedFakeCrosNetworkConfig : public FakeCrosNetworkConfig {
+ public:
+  ScopedFakeCrosNetworkConfig();
+  ~ScopedFakeCrosNetworkConfig() override;
+};
+
+// Helper class that creates a `NetworkStatePropertiesPtr`.
+// It will auto-assign an unique guid to the network if none was provided.
+class NetworkBuilder {
+ public:
+  explicit NetworkBuilder(chromeos::network_config::mojom::NetworkType type,
+                          const std::string& guid = "");
+  NetworkBuilder(NetworkBuilder&& other);
+  NetworkBuilder& operator=(NetworkBuilder&&);
+  NetworkBuilder(const NetworkBuilder& other);
+  void operator=(const NetworkBuilder& other);
+
+  ~NetworkBuilder();
+
+  NetworkBuilder& SetOncSource(
+      chromeos::network_config::mojom::OncSource source);
+
+  chromeos::network_config::mojom::NetworkStatePropertiesPtr Build() const;
+
+ private:
+  chromeos::network_config::mojom::NetworkTypeStatePropertiesPtr
+  CreateTypeStateForType(chromeos::network_config::mojom::NetworkType type);
+
+  chromeos::network_config::mojom::NetworkStatePropertiesPtr network_ =
+      chromeos::network_config::mojom::NetworkStateProperties::New();
+};
+
+}  // namespace policy::test
+
+#endif  // CHROME_BROWSER_ASH_POLICY_REMOTE_COMMANDS_FAKE_CROS_NETWORK_CONFIG_H_
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager.cc
index 1da3180..89c62b2 100644
--- a/chrome/browser/ash/system_web_apps/system_web_app_manager.cc
+++ b/chrome/browser/ash/system_web_apps/system_web_app_manager.cc
@@ -404,7 +404,7 @@
     SystemWebAppType type) const {
   if (!provider_->is_registry_ready())
     return absl::nullopt;
-  return web_app::GetAppIdForSystemApp(provider_->registrar(),
+  return web_app::GetAppIdForSystemApp(provider_->registrar_unsafe(),
                                        system_app_delegates_, type);
 }
 
@@ -412,7 +412,7 @@
     const web_app::AppId& app_id) const {
   if (!provider_->is_registry_ready())
     return absl::nullopt;
-  return web_app::GetSystemAppTypeForAppId(provider_->registrar(),
+  return web_app::GetSystemAppTypeForAppId(provider_->registrar_unsafe(),
                                            system_app_delegates_, app_id);
 }
 
@@ -435,8 +435,8 @@
 
 bool SystemWebAppManager::IsSystemWebApp(const web_app::AppId& app_id) const {
   DCHECK(provider_->is_registry_ready());
-  return web_app::IsSystemWebApp(provider_->registrar(), system_app_delegates_,
-                                 app_id);
+  return web_app::IsSystemWebApp(provider_->registrar_unsafe(),
+                                 system_app_delegates_, app_id);
 }
 
 const std::vector<std::string>* SystemWebAppManager::GetEnabledOriginTrials(
@@ -489,7 +489,7 @@
     return absl::nullopt;
 
   absl::optional<web_app::AppId> app_id =
-      provider_->registrar().FindAppWithUrlInScope(url);
+      provider_->registrar_unsafe().FindAppWithUrlInScope(url);
   if (!app_id.has_value())
     return absl::nullopt;
 
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc
index 1cbc911..04fbbf4 100644
--- a/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/ash/system_web_apps/system_web_app_manager_browsertest.cc
@@ -125,7 +125,7 @@
 
   Profile* profile = app_browser->profile();
   web_app::WebAppRegistrar& registrar =
-      web_app::WebAppProvider::GetForTest(profile)->registrar();
+      web_app::WebAppProvider::GetForTest(profile)->registrar_unsafe();
 
   EXPECT_EQ("Test System App", registrar.GetAppShortName(app_id));
   EXPECT_EQ(SkColorSetRGB(0, 0xFF, 0), registrar.GetAppThemeColor(app_id));
@@ -1271,7 +1271,7 @@
 
   Profile* profile = app_browser->profile();
   web_app::WebAppRegistrar& registrar =
-      web_app::WebAppProvider::GetForTest(profile)->registrar();
+      web_app::WebAppProvider::GetForTest(profile)->registrar_unsafe();
 
   EXPECT_EQ("Test System App", registrar.GetAppShortName(app_id));
   EXPECT_EQ(SkColorSetRGB(0, 0xFF, 0), registrar.GetAppThemeColor(app_id));
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
index 9a91382..6cf4d5c 100644
--- a/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
+++ b/chrome/browser/ash/system_web_apps/system_web_app_manager_unittest.cc
@@ -179,13 +179,13 @@
   }
 
   bool IsInstalled(const GURL& install_url) {
-    return provider().registrar().IsInstalled(
+    return provider().registrar_unsafe().IsInstalled(
         web_app::GenerateAppId(/*manifest_id=*/absl::nullopt, install_url));
   }
 
   void InitRegistrarWithSystemApps(
       const std::vector<SystemAppData>& system_app_data_list) {
-    DCHECK(provider().registrar().is_empty());
+    DCHECK(provider().registrar_unsafe().is_empty());
     DCHECK(!system_app_data_list.empty());
 
     for (const SystemAppData& data : system_app_data_list) {
@@ -1559,7 +1559,7 @@
 
   // Before Apps are synchronized, WebAppRegistry should know about the App.
   const web_app::WebApp* web_app =
-      provider().registrar().GetAppById(*opt_app_id);
+      provider().registrar_unsafe().GetAppById(*opt_app_id);
   ASSERT_TRUE(web_app);
   ASSERT_TRUE(web_app->client_data().system_web_app_data.has_value());
   ASSERT_EQ(SystemWebAppType::SETTINGS,
diff --git a/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.cc b/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.cc
index 80ccbc71..bb032e3 100644
--- a/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.cc
+++ b/chrome/browser/ash/system_web_apps/test_support/system_web_app_browsertest_base.cc
@@ -79,7 +79,7 @@
     // URL matches the start URL.
     params.override_url =
         web_app::WebAppProvider::GetForLocalAppsUnchecked(browser()->profile())
-            ->registrar()
+            ->registrar_unsafe()
             .GetAppStartUrl(params.app_id);
   }
 
@@ -132,7 +132,7 @@
              ? params.override_url
              : web_app::WebAppProvider::GetForLocalAppsUnchecked(
                    browser()->profile())
-                   ->registrar()
+                   ->registrar_unsafe()
                    .GetAppStartUrl(params.app_id);
 }
 
diff --git a/chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.cc b/chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.cc
index 1e31fd6d..aa88ac6 100644
--- a/chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.cc
+++ b/chrome/browser/ash/system_web_apps/test_support/system_web_app_integration_test.cc
@@ -48,7 +48,7 @@
   EXPECT_TRUE(GetManager().IsSystemWebApp(app_id));
 
   web_app::WebAppRegistrar& registrar =
-      web_app::WebAppProvider::GetForTest(profile())->registrar();
+      web_app::WebAppProvider::GetForTest(profile())->registrar_unsafe();
   EXPECT_EQ(title, registrar.GetAppShortName(app_id));
   EXPECT_EQ(base::ASCIIToUTF16(title),
             app_browser->window()->GetNativeWindow()->GetTitle());
diff --git a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc
index 977e28b..8b09640 100644
--- a/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc
+++ b/chrome/browser/ash/system_web_apps/test_support/test_system_web_app_installation.cc
@@ -894,7 +894,7 @@
 
 const GURL& TestSystemWebAppInstallation::GetAppUrl() {
   return web_app::WebAppProvider::GetForTest(profile_)
-      ->registrar()
+      ->registrar_unsafe()
       .GetAppStartUrl(GetAppId());
 }
 
diff --git a/chrome/browser/ash/telemetry_extension/diagnostics_service_ash.cc b/chrome/browser/ash/telemetry_extension/diagnostics_service_ash.cc
index f6b2087..d94872c 100644
--- a/chrome/browser/ash/telemetry_extension/diagnostics_service_ash.cc
+++ b/chrome/browser/ash/telemetry_extension/diagnostics_service_ash.cc
@@ -242,6 +242,18 @@
       std::move(callback)));
 }
 
+void DiagnosticsServiceAsh::RunEmmcLifetimeRoutine(
+    RunEmmcLifetimeRoutineCallback callback) {
+  GetService()->RunEmmcLifetimeRoutine(base::BindOnce(
+      [](crosapi::mojom::DiagnosticsService::RunEmmcLifetimeRoutineCallback
+             callback,
+         cros_healthd::mojom::RunRoutineResponsePtr ptr) {
+        std::move(callback).Run(
+            converters::ConvertDiagnosticsPtr(std::move(ptr)));
+      },
+      std::move(callback)));
+}
+
 void DiagnosticsServiceAsh::RunFingerprintAliveRoutine(
     RunFingerprintAliveRoutineCallback callback) {
   GetService()->RunFingerprintAliveRoutine(base::BindOnce(
diff --git a/chrome/browser/ash/telemetry_extension/diagnostics_service_ash.h b/chrome/browser/ash/telemetry_extension/diagnostics_service_ash.h
index 82971b4..ec14685 100644
--- a/chrome/browser/ash/telemetry_extension/diagnostics_service_ash.h
+++ b/chrome/browser/ash/telemetry_extension/diagnostics_service_ash.h
@@ -84,6 +84,7 @@
       RunDnsResolutionRoutineCallback callback) override;
   void RunDnsResolverPresentRoutine(
       RunDnsResolverPresentRoutineCallback callback) override;
+  void RunEmmcLifetimeRoutine(RunEmmcLifetimeRoutineCallback callback) override;
   void RunFingerprintAliveRoutine(
       RunFingerprintAliveRoutineCallback callback) override;
   void RunFloatingPointAccuracyRoutine(
diff --git a/chrome/browser/ash/telemetry_extension/diagnostics_service_ash_unittest.cc b/chrome/browser/ash/telemetry_extension/diagnostics_service_ash_unittest.cc
index 4474d6a9b..7aae2b0 100644
--- a/chrome/browser/ash/telemetry_extension/diagnostics_service_ash_unittest.cc
+++ b/chrome/browser/ash/telemetry_extension/diagnostics_service_ash_unittest.cc
@@ -311,6 +311,21 @@
       result, cros_healthd::mojom::DiagnosticRoutineEnum::kDnsResolverPresent);
 }
 
+TEST_F(DiagnosticsServiceAshTest, RunEmmcLifetimeRoutineSuccess) {
+  // Configure FakeCrosHealthd.
+  SetSuccessfulRoutineResponse();
+
+  base::test::TestFuture<crosapi::mojom::DiagnosticsRunRoutineResponsePtr>
+      future;
+
+  diagnostics_service()->RunEmmcLifetimeRoutine(future.GetCallback());
+
+  ASSERT_TRUE(future.Wait());
+  const auto& result = future.Get();
+  ValidateResponse(result,
+                   cros_healthd::mojom::DiagnosticRoutineEnum::kEmmcLifetime);
+}
+
 TEST_F(DiagnosticsServiceAshTest, RunFingerprintAliveRoutineSuccess) {
   // Configure FakeCrosHealthd.
   SetSuccessfulRoutineResponse();
diff --git a/chrome/browser/ash/telemetry_extension/diagnostics_service_converters.cc b/chrome/browser/ash/telemetry_extension/diagnostics_service_converters.cc
index 6a054dd..2e0fd69c 100644
--- a/chrome/browser/ash/telemetry_extension/diagnostics_service_converters.cc
+++ b/chrome/browser/ash/telemetry_extension/diagnostics_service_converters.cc
@@ -124,6 +124,8 @@
         kSmartctlCheckWithPercentageUsed:
       return crosapi::mojom::DiagnosticsRoutineEnum::
           kSmartctlCheckWithPercentageUsed;
+    case cros_healthd::mojom::DiagnosticRoutineEnum::kEmmcLifetime:
+      return crosapi::mojom::DiagnosticsRoutineEnum::kEmmcLifetime;
     default:
       return absl::nullopt;
   }
diff --git a/chrome/browser/ash/telemetry_extension/diagnostics_service_converters_unittest.cc b/chrome/browser/ash/telemetry_extension/diagnostics_service_converters_unittest.cc
index a86a86cd..4bbc503f 100644
--- a/chrome/browser/ash/telemetry_extension/diagnostics_service_converters_unittest.cc
+++ b/chrome/browser/ash/telemetry_extension/diagnostics_service_converters_unittest.cc
@@ -77,6 +77,8 @@
   EXPECT_EQ(Convert(cros_healthd::DiagnosticRoutineEnum::
                         kSmartctlCheckWithPercentageUsed),
             crosapi::DiagnosticsRoutineEnum::kSmartctlCheckWithPercentageUsed);
+  EXPECT_EQ(Convert(cros_healthd::DiagnosticRoutineEnum::kEmmcLifetime),
+            crosapi::DiagnosticsRoutineEnum::kEmmcLifetime);
 
   EXPECT_EQ(Convert(cros_healthd::DiagnosticRoutineEnum::kArcHttp),
             absl::nullopt);
diff --git a/chrome/browser/ash/web_applications/os_feedback_app_integration_browsertest.cc b/chrome/browser/ash/web_applications/os_feedback_app_integration_browsertest.cc
index 0f3cd661..f56f9ca 100644
--- a/chrome/browser/ash/web_applications/os_feedback_app_integration_browsertest.cc
+++ b/chrome/browser/ash/web_applications/os_feedback_app_integration_browsertest.cc
@@ -189,29 +189,13 @@
   // Check the correct attributes for Feedback App.
   auto* system_app =
       GetManager().GetSystemApp(ash::SystemWebAppType::OS_FEEDBACK);
-  EXPECT_TRUE(system_app->ShouldShowInLauncher());
-  EXPECT_TRUE(system_app->ShouldShowInSearch());
+  EXPECT_FALSE(system_app->ShouldShowInLauncher());
+  EXPECT_FALSE(system_app->ShouldShowInSearch());
   EXPECT_FALSE(system_app->ShouldShowNewWindowMenuOption());
   EXPECT_TRUE(system_app->ShouldAllowScriptsToCloseWindows());
   EXPECT_FALSE(system_app->ShouldAllowResize());
   EXPECT_FALSE(system_app->ShouldAllowMaximize());
 }
 
-// Test that when the policy UserFeedbackAllowed is false, the Feedback App
-//  1) does not show in launcher
-//  2) does not show in search
-IN_PROC_BROWSER_TEST_P(OSFeedbackAppIntegrationTest,
-                       HideInLauncherAndSearchWhenUserFeedbackNotAllowed) {
-  WaitForTestSystemAppInstall();
-  browser()->profile()->GetPrefs()->SetBoolean(prefs::kUserFeedbackAllowed,
-                                               false);
-
-  // Check the correct attributes for Feedback App.
-  auto* system_app =
-      GetManager().GetSystemApp(ash::SystemWebAppType::OS_FEEDBACK);
-  EXPECT_FALSE(system_app->ShouldShowInLauncher());
-  EXPECT_FALSE(system_app->ShouldShowInSearch());
-}
-
 INSTANTIATE_SYSTEM_WEB_APP_MANAGER_TEST_SUITE_REGULAR_PROFILE_P(
     OSFeedbackAppIntegrationTest);
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 d953464..144bb60 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
@@ -98,7 +98,7 @@
 }
 
 bool OSFeedbackAppDelegate::ShouldShowInLauncher() const {
-  return IsUserFeedbackAllowed(profile());
+  return false;
 }
 
 bool OSFeedbackAppDelegate::ShouldShowInSearch() const {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index b59d740..ada17eb 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -86,7 +86,6 @@
 #include "chrome/browser/net/profile_network_context_service.h"
 #include "chrome/browser/net/profile_network_context_service_factory.h"
 #include "chrome/browser/net/system_network_context_manager.h"
-#include "chrome/browser/optimization_guide/chrome_browser_main_extra_parts_optimization_guide.h"
 #include "chrome/browser/payments/payment_request_display_manager_factory.h"
 #include "chrome/browser/performance_manager/public/chrome_browser_main_extra_parts_performance_manager.h"
 #include "chrome/browser/performance_manager/public/chrome_content_browser_client_performance_manager_part.h"
@@ -1736,9 +1735,6 @@
   main_parts->AddParts(
       std::make_unique<ChromeBrowserMainExtraPartsSegmentationPlatform>());
 
-  main_parts->AddParts(
-      std::make_unique<ChromeBrowserMainExtraPartsOptimizationGuide>());
-
   return main_parts;
 }
 
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc b/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc
index f4a78de..877b209 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/base_telemetry_extension_api_guard_function_browsertest.cc
@@ -296,12 +296,21 @@
         );
         chrome.test.succeed();
       },
+      async function runEmmcLifetimeRoutine() {
+        await chrome.test.assertPromiseRejects(
+            chrome.os.diagnostics.runEmmcLifetimeRoutine(),
+            'Error: Unauthorized access to ' +
+            'chrome.os.diagnostics.runEmmcLifetimeRoutine. ' +
+            '%s'
+        );
+        chrome.test.succeed();
+      },
       async function runFingerprintAliveRoutine() {
         await chrome.test.assertPromiseRejects(
             chrome.os.diagnostics.runFingerprintAliveRoutine(),
             'Error: Unauthorized access to ' +
             'chrome.os.diagnostics.runFingerprintAliveRoutine. ' +
-            '%s'
+                        '%s'
         );
         chrome.test.succeed();
       },
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.cc b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.cc
index d136e95d..c8b95e18 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.cc
@@ -285,6 +285,20 @@
   GetRemoteService()->RunDnsResolverPresentRoutine(GetOnResult());
 }
 
+// OsDiagnosticsRunEmmcLifetimeRoutineFunction ---------------------------
+
+OsDiagnosticsRunEmmcLifetimeRoutineFunction::
+    OsDiagnosticsRunEmmcLifetimeRoutineFunction() = default;
+OsDiagnosticsRunEmmcLifetimeRoutineFunction::
+    ~OsDiagnosticsRunEmmcLifetimeRoutineFunction() = default;
+
+void OsDiagnosticsRunEmmcLifetimeRoutineFunction::RunIfAllowed() {
+  auto cb =
+      base::BindOnce(&DiagnosticsApiRunRoutineFunctionBase::OnResult, this);
+
+  GetRemoteService()->RunEmmcLifetimeRoutine(std::move(cb));
+}
+
 // OsDiagnosticsRunGatewayCanBePingedRoutineFunction ---------------------------
 
 void OsDiagnosticsRunGatewayCanBePingedRoutineFunction::RunIfAllowed() {
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.h b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.h
index 9f8daaa..0aa4bce 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.h
+++ b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api.h
@@ -234,6 +234,25 @@
   void RunIfAllowed() override;
 };
 
+class OsDiagnosticsRunEmmcLifetimeRoutineFunction
+    : public DiagnosticsApiRunRoutineFunctionBase {
+ public:
+  DECLARE_EXTENSION_FUNCTION("os.diagnostics.runEmmcLifetimeRoutine",
+                             OS_DIAGNOSTICS_RUNEMMCLIFETIMEROUTINE)
+
+  OsDiagnosticsRunEmmcLifetimeRoutineFunction();
+  OsDiagnosticsRunEmmcLifetimeRoutineFunction(
+      const OsDiagnosticsRunEmmcLifetimeRoutineFunction&) = delete;
+  OsDiagnosticsRunEmmcLifetimeRoutineFunction& operator=(
+      const OsDiagnosticsRunEmmcLifetimeRoutineFunction&) = delete;
+
+ private:
+  ~OsDiagnosticsRunEmmcLifetimeRoutineFunction() override;
+
+  // BaseTelemetryExtensionApiGuardFunction:
+  void RunIfAllowed() override;
+};
+
 class OsDiagnosticsRunGatewayCanBePingedRoutineFunction
     : public DiagnosticsApiRunRoutineFunctionBase {
   DECLARE_EXTENSION_FUNCTION("os.diagnostics.runGatewayCanBePingedRoutine",
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api_browsertest.cc b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api_browsertest.cc
index 6067ac3..e308cdd 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api_browsertest.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api_browsertest.cc
@@ -259,6 +259,14 @@
         );
         chrome.test.succeed();
       },
+      async function runEmmcLifetimeRoutine() {
+        await chrome.test.assertPromiseRejects(
+            chrome.os.diagnostics.runEmmcLifetimeRoutine(),
+            'Error: API chrome.os.diagnostics.runEmmcLifetimeRoutine ' +
+            'failed. Not supported by ash browser'
+        );
+        chrome.test.succeed();
+      },
       async function runFingerprintAliveRoutine() {
         await chrome.test.assertPromiseRejects(
             chrome.os.diagnostics.runFingerprintAliveRoutine(),
@@ -400,6 +408,7 @@
         crosapi::mojom::DiagnosticsRoutineEnum::kFingerprintAlive,
         crosapi::mojom::DiagnosticsRoutineEnum::
             kSmartctlCheckWithPercentageUsed,
+        crosapi::mojom::DiagnosticsRoutineEnum::kEmmcLifetime,
     });
 
     SetServiceForTesting(std::move(fake_service_impl));
@@ -434,7 +443,8 @@
               "sensitive_sensor",
               "nvme_self_test",
               "fingerprint_alive",
-              "smartctl_check_with_percentage_used"
+              "smartctl_check_with_percentage_used",
+              "emmc_lifetime"
             ]
           }, response);
         chrome.test.succeed();
@@ -1158,6 +1168,45 @@
 }
 
 IN_PROC_BROWSER_TEST_F(TelemetryExtensionDiagnosticsApiBrowserTest,
+                       RunEmmcLifetimeRoutineSuccess) {
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
+  // If Diagnostics interface is not available on this version of ash-chrome,
+  // this test suite will no-op.
+  if (!IsServiceAvailable()) {
+    return;
+  }
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+
+  // Configure FakeDiagnosticsService.
+  {
+    auto expected_response =
+        crosapi::mojom::DiagnosticsRunRoutineResponse::New();
+    expected_response->id = 0;
+    expected_response->status =
+        crosapi::mojom::DiagnosticsRoutineStatusEnum::kReady;
+
+    // Set the return value for a call to RunEmmcLifetimeRoutine.
+    auto fake_service_impl = std::make_unique<FakeDiagnosticsService>();
+    fake_service_impl->SetRunRoutineResponse(std::move(expected_response));
+
+    fake_service_impl->SetExpectedLastCalledRoutine(
+        crosapi::mojom::DiagnosticsRoutineEnum::kEmmcLifetime);
+    SetServiceForTesting(std::move(fake_service_impl));
+  }
+
+  CreateExtensionAndRunServiceWorker(R"(
+    chrome.test.runTests([
+      async function runEmmcLifetimeRoutine() {
+        const response =
+          await chrome.os.diagnostics.runEmmcLifetimeRoutine();
+          chrome.test.assertEq({id: 0, status: "ready"}, response);
+        chrome.test.succeed();
+      }
+    ]);
+  )");
+}
+
+IN_PROC_BROWSER_TEST_F(TelemetryExtensionDiagnosticsApiBrowserTest,
                        RunFingerprintAliveRoutineSuccess) {
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   // If Diagnostics interface is not available on this version of ash-chrome,
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api_converters.cc b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api_converters.cc
index 4d4e674..b2f0280 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api_converters.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/diagnostics_api_converters.cc
@@ -105,6 +105,9 @@
     case MojoRoutineType::kSmartctlCheckWithPercentageUsed:
       *out = RoutineType::ROUTINE_TYPE_SMARTCTL_CHECK_WITH_PERCENTAGE_USED;
       return true;
+    case MojoRoutineType::kEmmcLifetime:
+      *out = RoutineType::ROUTINE_TYPE_EMMC_LIFETIME;
+      return true;
     default:
       return false;
   }
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/fake_diagnostics_service.cc b/chrome/browser/chromeos/extensions/telemetry/api/fake_diagnostics_service.cc
index 590b5741..3706d6bf 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/fake_diagnostics_service.cc
+++ b/chrome/browser/chromeos/extensions/telemetry/api/fake_diagnostics_service.cc
@@ -200,6 +200,17 @@
       base::BindOnce(std::move(callback), run_routine_response_.Clone()));
 }
 
+void FakeDiagnosticsService::RunEmmcLifetimeRoutine(
+    RunEmmcLifetimeRoutineCallback callback) {
+  actual_passed_parameters_.clear();
+  actual_called_routine_ =
+      crosapi::mojom::DiagnosticsRoutineEnum::kEmmcLifetime;
+
+  base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), run_routine_response_.Clone()));
+}
+
 void FakeDiagnosticsService::RunFloatingPointAccuracyRoutine(
     uint32_t length_seconds,
     RunFloatingPointAccuracyRoutineCallback callback) {
diff --git a/chrome/browser/chromeos/extensions/telemetry/api/fake_diagnostics_service.h b/chrome/browser/chromeos/extensions/telemetry/api/fake_diagnostics_service.h
index 5133f9e..3c8f686 100644
--- a/chrome/browser/chromeos/extensions/telemetry/api/fake_diagnostics_service.h
+++ b/chrome/browser/chromeos/extensions/telemetry/api/fake_diagnostics_service.h
@@ -62,6 +62,7 @@
       RunDnsResolutionRoutineCallback callback) override;
   void RunDnsResolverPresentRoutine(
       RunDnsResolverPresentRoutineCallback callback) override;
+  void RunEmmcLifetimeRoutine(RunEmmcLifetimeRoutineCallback callback) override;
   void RunFloatingPointAccuracyRoutine(
       uint32_t length_seconds,
       RunFloatingPointAccuracyRoutineCallback callback) override;
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index c51d692..3aefe2b4 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -651,7 +651,7 @@
   {
     "name": "block-insecure-private-network-requests",
     "owners": [ "titouan", "chrome-security-owp-team@google.com" ],
-    "expiry_milestone": 110
+    "expiry_milestone": 111
   },
   {
     "name": "bluetooth-coredump",
diff --git a/chrome/browser/media/webrtc/capture_policy_utils.cc b/chrome/browser/media/webrtc/capture_policy_utils.cc
index 7d8c3e7..3b6f98f1 100644
--- a/chrome/browser/media/webrtc/capture_policy_utils.cc
+++ b/chrome/browser/media/webrtc/capture_policy_utils.cc
@@ -107,7 +107,7 @@
 bool IsGetDisplayMediaSetSelectAllScreensAllowed(
     content::BrowserContext* context,
     const GURL& url) {
-#if BUILDFLAG(IS_CHROMEOS_LACROS) || BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS)
   Profile* profile = Profile::FromBrowserContext(context);
   if (!profile)
     return false;
@@ -115,13 +115,11 @@
       HostContentSettingsMapFactory::GetForProfile(profile);
   if (!host_content_settings_map)
     return false;
-  const base::Value auto_accept_enabled =
-      host_content_settings_map->GetWebsiteSetting(
+  ContentSetting auto_accept_enabled =
+      host_content_settings_map->GetContentSetting(
           url, url,
-          ContentSettingsType::GET_DISPLAY_MEDIA_SET_SELECT_ALL_SCREENS,
-          /*info=*/nullptr);
-  return auto_accept_enabled.is_int() &&
-         auto_accept_enabled.GetInt() == ContentSetting::CONTENT_SETTING_ALLOW;
+          ContentSettingsType::GET_DISPLAY_MEDIA_SET_SELECT_ALL_SCREENS);
+  return auto_accept_enabled == ContentSetting::CONTENT_SETTING_ALLOW;
 #else
   // This API is currently only available on ChromeOS.
   return false;
diff --git a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
index cd3b5b0..4381681 100644
--- a/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
+++ b/chrome/browser/media/webrtc/webrtc_browsertest_base.cc
@@ -105,19 +105,20 @@
 
 std::vector<std::string> JsonArrayToVectorOfStrings(
     const std::string& json_array) {
-  std::unique_ptr<base::Value> value =
-      base::JSONReader::ReadDeprecated(json_array);
-  EXPECT_TRUE(value);
-  EXPECT_TRUE(value->is_list());
-  std::unique_ptr<base::ListValue> list =
-      base::ListValue::From(std::move(value));
-  std::vector<std::string> vector;
-  vector.reserve(list->GetList().size());
-  for (base::Value& item : list->GetList()) {
-    EXPECT_TRUE(item.is_string());
-    vector.push_back(std::move(item).TakeString());
+  std::vector<std::string> result;
+  absl::optional<base::Value> value = base::JSONReader::Read(json_array);
+  if (!value || !value->is_list()) {
+    ADD_FAILURE();
+    return result;
   }
-  return vector;
+
+  base::Value::List& list = value->GetList();
+  result.reserve(list.size());
+  for (base::Value& item : list) {
+    EXPECT_TRUE(item.is_string());
+    result.push_back(std::move(item).TakeString());
+  }
+  return result;
 }
 
 }  // namespace
diff --git a/chrome/browser/media/webrtc/webrtc_interactive_uitest.cc b/chrome/browser/media/webrtc/webrtc_interactive_uitest.cc
index 1744d5a..d413839 100644
--- a/chrome/browser/media/webrtc/webrtc_interactive_uitest.cc
+++ b/chrome/browser/media/webrtc/webrtc_interactive_uitest.cc
@@ -283,8 +283,10 @@
   EXPECT_EQ(0u, GetPeerToPeerConnectionsCountChangeFromNetworkService());
 }
 
-IN_PROC_BROWSER_TEST_F(WebRtcBrowserTest,
-                       RunsAudioVideoWebRTCCallInTwoTabsGetStatsPromise) {
+// TODO(https://crbug.com/1399865): Deflake and re-enable
+IN_PROC_BROWSER_TEST_F(
+    WebRtcBrowserTest,
+    DISABLED_RunsAudioVideoWebRTCCallInTwoTabsGetStatsPromise) {
   StartServerAndOpenTabs();
   SetupPeerconnectionWithLocalStream(left_tab_);
   SetupPeerconnectionWithLocalStream(right_tab_);
diff --git a/chrome/browser/optimization_guide/browser_test_util.cc b/chrome/browser/optimization_guide/browser_test_util.cc
index 0363081..2fbba18 100644
--- a/chrome/browser/optimization_guide/browser_test_util.cc
+++ b/chrome/browser/optimization_guide/browser_test_util.cc
@@ -48,39 +48,4 @@
   }
 }
 
-std::unique_ptr<optimization_guide::proto::GetModelsResponse>
-BuildGetModelsResponse() {
-  std::unique_ptr<optimization_guide::proto::GetModelsResponse>
-      get_models_response =
-          std::make_unique<optimization_guide::proto::GetModelsResponse>();
-
-  optimization_guide::proto::PredictionModel* prediction_model =
-      get_models_response->add_models();
-  optimization_guide::proto::ModelInfo* model_info =
-      prediction_model->mutable_model_info();
-  model_info->set_version(2);
-  model_info->set_optimization_target(
-      optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  model_info->add_supported_model_engine_versions(
-      optimization_guide::proto::ModelEngineVersion::
-          MODEL_ENGINE_VERSION_TFLITE_2_8);
-  prediction_model->mutable_model()->set_download_url(
-      "https://example.com/model");
-
-  return get_models_response;
-}
-
-ModelFileObserver::ModelFileObserver() = default;
-
-ModelFileObserver::~ModelFileObserver() = default;
-
-void ModelFileObserver::OnModelUpdated(
-    proto::OptimizationTarget optimization_target,
-    const ModelInfo& model_info) {
-  optimization_target_ = optimization_target;
-  model_info_ = model_info;
-  if (file_received_callback_)
-    std::move(file_received_callback_).Run(optimization_target, model_info);
-}
-
 }  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/browser_test_util.h b/chrome/browser/optimization_guide/browser_test_util.h
index 18d91c4..23d8a0d 100644
--- a/chrome/browser/optimization_guide/browser_test_util.h
+++ b/chrome/browser/optimization_guide/browser_test_util.h
@@ -7,10 +7,6 @@
 
 #include <string>
 
-#include "base/functional/callback.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
-#include "components/optimization_guide/proto/models.pb.h"
-
 namespace base {
 class HistogramTester;
 }  // namespace base
@@ -24,43 +20,6 @@
     const std::string& histogram_name,
     int count);
 
-// Builds a test models response.
-std::unique_ptr<optimization_guide::proto::GetModelsResponse>
-BuildGetModelsResponse();
-
-// Helper to receive modelinfo updates.
-class ModelFileObserver : public OptimizationTargetModelObserver {
- public:
-  using ModelFileReceivedCallback =
-      base::OnceCallback<void(proto::OptimizationTarget, const ModelInfo&)>;
-
-  ModelFileObserver();
-  ~ModelFileObserver() override;
-
-  void set_model_file_received_callback(ModelFileReceivedCallback callback) {
-    file_received_callback_ = std::move(callback);
-  }
-
-  absl::optional<proto::OptimizationTarget> optimization_target() const {
-    return optimization_target_;
-  }
-
-  absl::optional<ModelInfo> model_info() { return model_info_; }
-
-  // OptimizationTargetModelObserver implementation:
-  void OnModelUpdated(proto::OptimizationTarget optimization_target,
-                      const ModelInfo& model_info) override;
-
- private:
-  ModelFileReceivedCallback file_received_callback_;
-
-  // Holds the optimization target that was received from modelinfo updates.
-  absl::optional<proto::OptimizationTarget> optimization_target_;
-
-  // Holds the modelinfo that was received from modelinfo updates.
-  absl::optional<ModelInfo> model_info_;
-};
-
 }  // namespace optimization_guide
 
 #endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_BROWSER_TEST_UTIL_H_
diff --git a/chrome/browser/optimization_guide/chrome_browser_main_extra_parts_optimization_guide.cc b/chrome/browser/optimization_guide/chrome_browser_main_extra_parts_optimization_guide.cc
deleted file mode 100644
index 17cb5ca7..0000000
--- a/chrome/browser/optimization_guide/chrome_browser_main_extra_parts_optimization_guide.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/optimization_guide/chrome_browser_main_extra_parts_optimization_guide.h"
-
-#include "base/files/file_path.h"
-#include "base/path_service.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/chrome_paths.h"
-#include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/prediction_manager.h"
-#include "components/optimization_guide/core/prediction_model_store.h"
-
-namespace {
-
-// Prefix for the model store directory.
-const base::FilePath::CharType kOptimizationGuideModelStoreDirPrefix[] =
-    FILE_PATH_LITERAL("optimization_guide_model_store");
-
-}  // namespace
-
-void ChromeBrowserMainExtraPartsOptimizationGuide::PreCreateThreads() {
-  if (!optimization_guide::features::IsInstallWideModelStoreEnabled())
-    return;
-
-  base::FilePath model_downloads_dir;
-  base::PathService::Get(chrome::DIR_USER_DATA, &model_downloads_dir);
-  model_downloads_dir =
-      model_downloads_dir.Append(kOptimizationGuideModelStoreDirPrefix);
-  // Create and initialize the install-wide model store.
-  optimization_guide::PredictionModelStore::GetInstance()->Initialize(
-      g_browser_process->local_state(), model_downloads_dir);
-}
-
-void ChromeBrowserMainExtraPartsOptimizationGuide::PostProfileInit(
-    Profile* profile,
-    bool is_initial_profile) {
-  if (!optimization_guide::features::IsInstallWideModelStoreEnabled())
-    return;
-
-  if (profile->IsOffTheRecord())
-    return;
-
-  auto* optimization_guide_keyed_service =
-      OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
-  if (!optimization_guide_keyed_service)
-    return;
-
-  optimization_guide_keyed_service->GetPredictionManager()
-      ->MaybeInitializeModelDownloads(
-          optimization_guide_keyed_service
-              ->BackgroundDownloadServiceProvider());
-}
diff --git a/chrome/browser/optimization_guide/chrome_browser_main_extra_parts_optimization_guide.h b/chrome/browser/optimization_guide/chrome_browser_main_extra_parts_optimization_guide.h
deleted file mode 100644
index e93db7c..0000000
--- a/chrome/browser/optimization_guide/chrome_browser_main_extra_parts_optimization_guide.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_OPTIMIZATION_GUIDE_CHROME_BROWSER_MAIN_EXTRA_PARTS_OPTIMIZATION_GUIDE_H_
-#define CHROME_BROWSER_OPTIMIZATION_GUIDE_CHROME_BROWSER_MAIN_EXTRA_PARTS_OPTIMIZATION_GUIDE_H_
-
-#include "chrome/browser/chrome_browser_main_extra_parts.h"
-
-class Profile;
-
-// This class is used to initialize the optimization guide model store which is
-// install-wide, as part of Chrome browser process startup.
-class ChromeBrowserMainExtraPartsOptimizationGuide
-    : public ChromeBrowserMainExtraParts {
- public:
-  ChromeBrowserMainExtraPartsOptimizationGuide() = default;
-
-  // ChromeBrowserMainExtraParts implementation:
-  void PreCreateThreads() override;
-  void PostProfileInit(Profile* profile, bool is_initial_profile) override;
-};
-
-#endif  // CHROME_BROWSER_OPTIMIZATION_GUIDE_CHROME_BROWSER_MAIN_EXTRA_PARTS_OPTIMIZATION_GUIDE_H_
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index 70bf6ac..68e4ac3 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -36,7 +36,6 @@
 #include "components/optimization_guide/core/optimization_guide_store.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/core/prediction_manager.h"
-#include "components/optimization_guide/core/prediction_model_store.h"
 #include "components/optimization_guide/core/tab_url_provider.h"
 #include "components/optimization_guide/core/top_host_provider.h"
 #include "components/optimization_guide/proto/models.pb.h"
@@ -210,19 +209,16 @@
             : nullptr;
     hint_store = hint_store_ ? hint_store_->AsWeakPtr() : nullptr;
 
-    if (!optimization_guide::features::IsInstallWideModelStoreEnabled()) {
-      prediction_model_and_features_store_ =
-          std::make_unique<optimization_guide::OptimizationGuideStore>(
-              proto_db_provider,
-              profile_path.Append(
-                  optimization_guide::
-                      kOptimizationGuidePredictionModelMetadataStore),
-              base::ThreadPool::CreateSequencedTaskRunner(
-                  {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
-              profile->GetPrefs());
-      prediction_model_and_features_store =
-          prediction_model_and_features_store_->AsWeakPtr();
-    }
+    prediction_model_and_features_store_ = std::make_unique<
+        optimization_guide::OptimizationGuideStore>(
+        proto_db_provider,
+        profile_path.Append(
+            optimization_guide::kOptimizationGuidePredictionModelMetadataStore),
+        base::ThreadPool::CreateSequencedTaskRunner(
+            {base::MayBlock(), base::TaskPriority::BEST_EFFORT}),
+        profile->GetPrefs());
+    prediction_model_and_features_store =
+        prediction_model_and_features_store_->AsWeakPtr();
   }
 
   optimization_guide_logger_ = std::make_unique<OptimizationGuideLogger>();
@@ -232,8 +228,7 @@
       MaybeCreatePushNotificationManager(profile),
       optimization_guide_logger_.get());
   base::FilePath model_downloads_dir;
-  if (!optimization_guide::features::IsInstallWideModelStoreEnabled() &&
-      !profile->IsOffTheRecord()) {
+  if (!profile->IsOffTheRecord()) {
     // Do not explicitly hand off the model downloads directory to
     // off-the-record profiles. Underneath the hood, this variable is only used
     // in non off-the-record profiles to know where to download the model files
@@ -244,11 +239,8 @@
   }
 
   prediction_manager_ = std::make_unique<optimization_guide::PredictionManager>(
-      prediction_model_and_features_store,
-      optimization_guide::features::IsInstallWideModelStoreEnabled()
-          ? optimization_guide::PredictionModelStore::GetInstance()
-          : nullptr,
-      url_loader_factory, profile->GetPrefs(), profile->IsOffTheRecord(),
+      prediction_model_and_features_store, url_loader_factory,
+      profile->GetPrefs(), profile->IsOffTheRecord(),
       g_browser_process->GetApplicationLocale(), model_downloads_dir,
       optimization_guide_logger_.get(),
       base::BindOnce(
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
index 4331c1e..ef4cdf3f 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -35,18 +35,16 @@
 class OptimizationGuideBridge;
 }  // namespace android
 class ChromeHintsManager;
-class ModelInfo;
 class OptimizationGuideStore;
 class PredictionManager;
 class PredictionManagerBrowserTestBase;
 class PredictionModelDownloadClient;
-class PredictionModelStoreBrowserTest;
 class PushNotificationManager;
+class ModelInfo;
 class TabUrlProvider;
 class TopHostProvider;
 }  // namespace optimization_guide
 
-class ChromeBrowserMainExtraPartsOptimizationGuide;
 class GURL;
 class OptimizationGuideLogger;
 class OptimizationGuideNavigationData;
@@ -131,17 +129,15 @@
   }
 
  private:
-  friend class ChromeBrowserMainExtraPartsOptimizationGuide;
   friend class ChromeBrowsingDataRemoverDelegate;
   friend class HintsFetcherBrowserTest;
-  friend class OptimizationGuideInternalsUI;
   friend class OptimizationGuideKeyedServiceBrowserTest;
   friend class OptimizationGuideMessageHandler;
   friend class OptimizationGuideWebContentsObserver;
-  friend class optimization_guide::PredictionManagerBrowserTestBase;
   friend class optimization_guide::PredictionModelDownloadClient;
-  friend class optimization_guide::PredictionModelStoreBrowserTest;
+  friend class optimization_guide::PredictionManagerBrowserTestBase;
   friend class optimization_guide::android::OptimizationGuideBridge;
+  friend class OptimizationGuideInternalsUI;
 
   // Initializes |this|.
   void Initialize();
@@ -193,8 +189,6 @@
   // Manages the storing, loading, and fetching of hints.
   std::unique_ptr<optimization_guide::ChromeHintsManager> hints_manager_;
 
-  // TODO(crbug/1358568): Remove this old model store once the new model store
-  // is launched.
   // The store of optimization target prediction models and features.
   std::unique_ptr<optimization_guide::OptimizationGuideStore>
       prediction_model_and_features_store_;
diff --git a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
index 3152f14..0035797b 100644
--- a/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
+++ b/chrome/browser/optimization_guide/prediction/prediction_manager_browsertest.cc
@@ -58,9 +58,28 @@
 
 constexpr int kSuccessfulModelVersion = 123;
 
-// Timeout to allow the model file to be downloaded, unzipped and sent to the
-// model file observers.
-constexpr base::TimeDelta kModelFileDownloadTimeout = base::Seconds(60);
+std::unique_ptr<optimization_guide::proto::GetModelsResponse>
+BuildGetModelsResponse() {
+  std::unique_ptr<optimization_guide::proto::GetModelsResponse>
+      get_models_response =
+          std::make_unique<optimization_guide::proto::GetModelsResponse>();
+
+  std::unique_ptr<optimization_guide::proto::PredictionModel> prediction_model =
+      std::make_unique<optimization_guide::proto::PredictionModel>();
+  optimization_guide::proto::ModelInfo* model_info =
+      prediction_model->mutable_model_info();
+  model_info->set_version(2);
+  model_info->set_optimization_target(
+      optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
+  model_info->add_supported_model_engine_versions(
+      optimization_guide::proto::ModelEngineVersion::
+          MODEL_ENGINE_VERSION_TFLITE_2_8);
+  prediction_model->mutable_model()->set_download_url(
+      "https://example.com/model");
+  *get_models_response->add_models() = *prediction_model.get();
+
+  return get_models_response;
+}
 
 enum class PredictionModelsFetcherRemoteResponseType {
   kSuccessfulWithValidModelFile = 0,
@@ -74,6 +93,28 @@
 
 namespace optimization_guide {
 
+class ModelFileObserver : public OptimizationTargetModelObserver {
+ public:
+  using ModelFileReceivedCallback =
+      base::OnceCallback<void(proto::OptimizationTarget, const ModelInfo&)>;
+
+  ModelFileObserver() = default;
+  ~ModelFileObserver() override = default;
+
+  void set_model_file_received_callback(ModelFileReceivedCallback callback) {
+    file_received_callback_ = std::move(callback);
+  }
+
+  void OnModelUpdated(proto::OptimizationTarget optimization_target,
+                      const ModelInfo& model_info) override {
+    if (file_received_callback_)
+      std::move(file_received_callback_).Run(optimization_target, model_info);
+  }
+
+ private:
+  ModelFileReceivedCallback file_received_callback_;
+};
+
 // Abstract base class for browser testing Prediction Manager.
 // Actual class fixtures should implement InitializeFeatureList to set up
 // features used in tests.
@@ -103,6 +144,8 @@
   }
 
   void SetUpOnMainThread() override {
+    content::NetworkConnectionChangeSimulator().SetConnectionType(
+        network::mojom::ConnectionType::CONNECTION_2G);
     https_server_ = std::make_unique<net::EmbeddedTestServer>(
         net::EmbeddedTestServer::TYPE_HTTPS);
     https_server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
@@ -125,6 +168,8 @@
   }
 
   void SetUpCommandLine(base::CommandLine* cmd) override {
+    cmd->AppendSwitch("enable-spdy-proxy-auth");
+
     cmd->AppendSwitch(optimization_guide::switches::
                           kDisableCheckingUserPermissionsForTesting);
     cmd->AppendSwitchASCII(optimization_guide::switches::kFetchHintsOverride,
@@ -160,6 +205,9 @@
     return optimization_guide_keyed_service->GetPredictionManager();
   }
 
+  GURL https_url_with_content() { return https_url_with_content_; }
+  GURL https_url_without_content() { return https_url_without_content_; }
+
  protected:
   // Virtualize for testing different feature configurations.
   virtual void InitializeFeatureList() = 0;
@@ -230,8 +278,7 @@
       PredictionModelsFetcherRemoteResponseType::kSuccessfulWithValidModelFile;
 };
 
-class PredictionManagerBrowserTest : public testing::WithParamInterface<bool>,
-                                     public PredictionManagerBrowserTestBase {
+class PredictionManagerBrowserTest : public PredictionManagerBrowserTestBase {
  public:
   PredictionManagerBrowserTest() = default;
   ~PredictionManagerBrowserTest() override = default;
@@ -240,30 +287,21 @@
   PredictionManagerBrowserTest& operator=(const PredictionManagerBrowserTest&) =
       delete;
 
-  bool ShouldEnableInstallWideModelStore() const { return GetParam(); }
-
  private:
   void InitializeFeatureList() override {
-    std::vector<base::test::FeatureRefAndParams> enabled_features = {
-        {optimization_guide::features::kOptimizationHints, {}},
-        {optimization_guide::features::kRemoteOptimizationGuideFetching, {}},
-        {optimization_guide::features::kOptimizationTargetPrediction,
-         {{"fetch_startup_delay_ms", "2000"}}},
-    };
-    if (ShouldEnableInstallWideModelStore()) {
-      enabled_features.emplace_back(
-          features::kOptimizationGuideInstallWideModelStore,
-          base::FieldTrialParams());
-    }
-    scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, {});
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        {
+            {optimization_guide::features::kOptimizationHints, {}},
+            {optimization_guide::features::kRemoteOptimizationGuideFetching,
+             {}},
+            {optimization_guide::features::kOptimizationTargetPrediction,
+             {{"fetch_startup_delay_ms", "2000"}}},
+        },
+        {});
   }
 };
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         PredictionManagerBrowserTest,
-                         /*use_install_wide_model_store=*/testing::Bool());
-
-IN_PROC_BROWSER_TEST_P(PredictionManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerBrowserTest,
                        ComponentUpdatesPrefDisabled) {
   ModelFileObserver model_file_observer;
   SetResponseType(PredictionModelsFetcherRemoteResponseType::kUnsuccessful);
@@ -284,7 +322,7 @@
       0);
 }
 
-IN_PROC_BROWSER_TEST_P(PredictionManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerBrowserTest,
                        ModelsAndFeaturesStoreInitialized) {
   ModelFileObserver model_file_observer;
   SetResponseType(
@@ -310,7 +348,7 @@
       kSuccessfulModelVersion, 1);
 }
 
-IN_PROC_BROWSER_TEST_P(PredictionManagerBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerBrowserTest,
                        PredictionModelFetchFailed) {
   ModelFileObserver model_file_observer;
   SetResponseType(PredictionModelsFetcherRemoteResponseType::kUnsuccessful);
@@ -378,30 +416,22 @@
 
  private:
   void InitializeFeatureList() override {
-    std::vector<base::test::FeatureRefAndParams> enabled_features = {
-        {features::kOptimizationHints, {}},
-        {features::kRemoteOptimizationGuideFetching, {}},
-        {features::kOptimizationTargetPrediction, {}},
-        {features::kOptimizationGuideModelDownloading,
-         {{"unrestricted_model_downloading", "true"}}},
-    };
-    if (ShouldEnableInstallWideModelStore()) {
-      enabled_features.emplace_back(
-          features::kOptimizationGuideInstallWideModelStore,
-          base::FieldTrialParams());
-    }
-    scoped_feature_list_.InitWithFeaturesAndParameters(enabled_features, {});
+    scoped_feature_list_.InitWithFeaturesAndParameters(
+        {
+            {features::kOptimizationHints, {}},
+            {features::kRemoteOptimizationGuideFetching, {}},
+            {features::kOptimizationTargetPrediction, {}},
+            {features::kOptimizationGuideModelDownloading,
+             {{"unrestricted_model_downloading", "true"}}},
+        },
+        {});
   }
 
   std::unique_ptr<ModelFileObserver> model_file_observer_;
 };
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         PredictionManagerModelDownloadingBrowserTest,
-                         /*ShouldEnableInstallWideModelStore=*/testing::Bool());
-
 // Flaky on various bots. See https://crbug.com/1266318
-IN_PROC_BROWSER_TEST_P(PredictionManagerModelDownloadingBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerModelDownloadingBrowserTest,
                        DISABLED_TestIncognitoUsesModelFromRegularProfile) {
   SetResponseType(
       PredictionModelsFetcherRemoteResponseType::kSuccessfulWithValidModelFile);
@@ -425,8 +455,8 @@
     // Wait until the observer receives the file. We increase the timeout to 60
     // seconds here since the file is on the larger side.
     {
-      base::test::ScopedRunLoopTimeout file_download_timeout(
-          FROM_HERE, kModelFileDownloadTimeout);
+      base::test::ScopedRunLoopTimeout file_download_timeout(FROM_HERE,
+                                                             base::Seconds(60));
       run_loop->Run();
     }
 
@@ -474,7 +504,7 @@
 #else
 #define MAYBE_TestIncognitoDoesntFetchModels TestIncognitoDoesntFetchModels
 #endif
-IN_PROC_BROWSER_TEST_P(PredictionManagerModelDownloadingBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerModelDownloadingBrowserTest,
                        MAYBE_TestIncognitoDoesntFetchModels) {
   base::HistogramTester histogram_tester;
 
@@ -501,7 +531,7 @@
       "OptimizationGuide.PredictionModelUpdateVersion.PainfulPageLoad", 0);
 }
 
-IN_PROC_BROWSER_TEST_P(PredictionManagerModelDownloadingBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerModelDownloadingBrowserTest,
                        TestDownloadUrlAcceptedByDownloadServiceButInvalid) {
   base::HistogramTester histogram_tester;
 
@@ -540,7 +570,7 @@
       "OptimizationGuide.PredictionModelUpdateVersion.PainfulPageLoad", 0);
 }
 
-IN_PROC_BROWSER_TEST_P(PredictionManagerModelDownloadingBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerModelDownloadingBrowserTest,
                        TestSuccessfulModelFileFlow) {
   base::HistogramTester histogram_tester;
 
@@ -554,13 +584,11 @@
         EXPECT_EQ(optimization_target,
                   proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
 
-        if (!GetParam()) {
-          // Regression test for crbug/1327975.
-          // Make sure model file path downloaded into profile dir.
-          base::FilePath profile_dir =
-              g_browser_process->profile_manager()->GetLastUsedProfileDir();
-          EXPECT_TRUE(profile_dir.IsParent(model_info.GetModelFilePath()));
-        }
+        // Regression test for crbug/1327975.
+        // Make sure model file path downloaded into profile dir.
+        base::FilePath profile_dir =
+            g_browser_process->profile_manager()->GetLastUsedProfileDir();
+        EXPECT_TRUE(profile_dir.IsParent(model_info.GetModelFilePath()));
         run_loop->Quit();
       },
       run_loop.get()));
@@ -572,8 +600,8 @@
   // Wait until the observer receives the file. We increase the timeout to 60
   // seconds here since the file is on the larger side.
   {
-    base::test::ScopedRunLoopTimeout file_download_timeout(
-        FROM_HERE, kModelFileDownloadTimeout);
+    base::test::ScopedRunLoopTimeout file_download_timeout(FROM_HERE,
+                                                           base::Seconds(60));
     run_loop->Run();
   }
 
@@ -611,7 +639,7 @@
                        1)));
 }
 
-IN_PROC_BROWSER_TEST_P(PredictionManagerModelDownloadingBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerModelDownloadingBrowserTest,
                        TestSuccessfulModelFileFlowWithAdditionalFile) {
   base::HistogramTester histogram_tester;
 
@@ -639,8 +667,8 @@
   // Wait until the observer receives the file. We increase the timeout to 60
   // seconds here since the file is on the larger side.
   {
-    base::test::ScopedRunLoopTimeout file_download_timeout(
-        FROM_HERE, kModelFileDownloadTimeout);
+    base::test::ScopedRunLoopTimeout file_download_timeout(FROM_HERE,
+                                                           base::Seconds(60));
     run_loop->Run();
   }
 
@@ -678,7 +706,7 @@
                        1)));
 }
 
-IN_PROC_BROWSER_TEST_P(PredictionManagerModelDownloadingBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerModelDownloadingBrowserTest,
                        TestSuccessfulModelFileFlowWithInvalidAdditionalFile) {
   base::HistogramTester histogram_tester;
 
@@ -725,7 +753,7 @@
 #else
 #define MAYBE_TestSwitchProfileDoesntCrash TestSwitchProfileDoesntCrash
 #endif
-IN_PROC_BROWSER_TEST_P(PredictionManagerModelDownloadingBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerModelDownloadingBrowserTest,
                        MAYBE_TestSwitchProfileDoesntCrash) {
   ProfileManager* profile_manager = g_browser_process->profile_manager();
   base::FilePath other_path =
@@ -739,7 +767,7 @@
 
 #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
 // CreateGuestBrowser() is not supported for Android or ChromeOS out of the box.
-IN_PROC_BROWSER_TEST_P(PredictionManagerModelDownloadingBrowserTest,
+IN_PROC_BROWSER_TEST_F(PredictionManagerModelDownloadingBrowserTest,
                        GuestProfileReceivesModel) {
   SetResponseType(
       PredictionModelsFetcherRemoteResponseType::kSuccessfulWithValidModelFile);
diff --git a/chrome/browser/optimization_guide/prediction/prediction_model_store_browsertest.cc b/chrome/browser/optimization_guide/prediction/prediction_model_store_browsertest.cc
deleted file mode 100644
index bada62c8..0000000
--- a/chrome/browser/optimization_guide/prediction/prediction_model_store_browsertest.cc
+++ /dev/null
@@ -1,289 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/files/file_util.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_run_loop_timeout.h"
-#include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/optimization_guide/browser_test_util.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
-#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/profiles/profile_key.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/profiles/profile_test_util.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "components/optimization_guide/core/optimization_guide_constants.h"
-#include "components/optimization_guide/core/optimization_guide_features.h"
-#include "components/optimization_guide/core/optimization_guide_switches.h"
-#include "components/optimization_guide/core/prediction_manager.h"
-#include "components/optimization_guide/proto/models.pb.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "net/test/embedded_test_server/http_request.h"
-#include "net/test/embedded_test_server/http_response.h"
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "ash/constants/ash_switches.h"
-#include "chrome/browser/ash/profiles/profile_helper.h"
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
-
-namespace optimization_guide {
-
-namespace {
-
-constexpr int kSuccessfulModelVersion = 123;
-
-// Test locales.
-constexpr char kTestLocaleFoo[] = "en-CA";
-
-// Timeout to allow the model file to be downloaded, unzipped and sent to the
-// model file observers.
-constexpr base::TimeDelta kModelFileDownloadTimeout = base::Seconds(60);
-
-Profile* CreateProfile() {
-  ProfileManager* profile_manager = g_browser_process->profile_manager();
-  return profiles::testing::CreateProfileSync(
-      profile_manager, profile_manager->GenerateNextProfileDirectoryPath());
-}
-
-proto::ModelCacheKey GetModelCacheKey(const std::string& locale) {
-  proto::ModelCacheKey model_cache_key;
-  model_cache_key.set_locale(locale);
-  return model_cache_key;
-}
-
-}  // namespace
-
-class PredictionModelStoreBrowserTest : public InProcessBrowserTest {
- public:
-  PredictionModelStoreBrowserTest() = default;
-  ~PredictionModelStoreBrowserTest() override = default;
-
-  PredictionModelStoreBrowserTest(const PredictionModelStoreBrowserTest&) =
-      delete;
-  PredictionModelStoreBrowserTest& operator=(
-      const PredictionModelStoreBrowserTest&) = delete;
-
-  void SetUp() override {
-    scoped_feature_list_.InitWithFeatures(
-        {{features::kOptimizationGuideInstallWideModelStore}}, {});
-    models_server_ = std::make_unique<net::EmbeddedTestServer>(
-        net::EmbeddedTestServer::TYPE_HTTPS);
-    models_server_->ServeFilesFromSourceDirectory(
-        "chrome/test/data/optimization_guide");
-    models_server_->RegisterRequestHandler(base::BindRepeating(
-        &PredictionModelStoreBrowserTest::HandleGetModelsRequest,
-        base::Unretained(this)));
-
-    ASSERT_TRUE(models_server_->Start());
-    InProcessBrowserTest::SetUp();
-  }
-
-  void SetUpOnMainThread() override {
-    download_server_ = std::make_unique<net::EmbeddedTestServer>(
-        net::EmbeddedTestServer::TYPE_HTTPS);
-    download_server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
-    ASSERT_TRUE(download_server_->Start());
-    model_file_url_ = models_server_->GetURL("/signed_valid_model.crx3");
-
-    InProcessBrowserTest::SetUpOnMainThread();
-  }
-
-  void TearDownOnMainThread() override {
-    EXPECT_TRUE(download_server_->ShutdownAndWaitUntilComplete());
-    EXPECT_TRUE(models_server_->ShutdownAndWaitUntilComplete());
-    InProcessBrowserTest::TearDownOnMainThread();
-  }
-
-  void SetUpCommandLine(base::CommandLine* cmd) override {
-    cmd->AppendSwitch(switches::kDisableCheckingUserPermissionsForTesting);
-    cmd->AppendSwitchASCII(
-        switches::kOptimizationGuideServiceGetModelsURL,
-        models_server_
-            ->GetURL(GURL(kOptimizationGuideServiceGetModelsDefaultURL).host(),
-                     "/")
-            .spec());
-    cmd->AppendSwitchASCII("host-rules", "MAP * 127.0.0.1");
-    cmd->AppendSwitchASCII("force-variation-ids", "4");
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-    cmd->AppendSwitch(ash::switches::kIgnoreUserProfileMappingForTests);
-#endif
-  }
-
-  void RegisterModelFileObserverWithKeyedService(
-      ModelFileObserver* model_file_observer,
-      Profile* profile) {
-    OptimizationGuideKeyedServiceFactory::GetForProfile(profile)
-        ->AddObserverForOptimizationTargetModel(
-            proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD,
-            /*model_metadata=*/absl::nullopt, model_file_observer);
-  }
-
-  // Registers |model_file_observer| for model updates from the optimization
-  // guide service in |profile|. Default profile is used, when |profile| is
-  // null.
-  void RegisterAndWaitForModelUpdate(ModelFileObserver* model_file_observer,
-                                     Profile* profile = nullptr) {
-    std::unique_ptr<base::RunLoop> run_loop = std::make_unique<base::RunLoop>();
-    model_file_observer->set_model_file_received_callback(
-        base::BindOnce([](base::RunLoop* run_loop,
-                          proto::OptimizationTarget optimization_target,
-                          const ModelInfo& model_info) { run_loop->Quit(); },
-                       run_loop.get()));
-
-    RegisterModelFileObserverWithKeyedService(
-        model_file_observer, profile ? profile : browser()->profile());
-    base::test::ScopedRunLoopTimeout model_file_download_timeout(
-        FROM_HERE, kModelFileDownloadTimeout);
-    run_loop->Run();
-  }
-
-  void SetModelCacheKey(Profile* profile,
-                        const proto::ModelCacheKey& model_cache_key) {
-    OptimizationGuideKeyedServiceFactory::GetForProfile(profile)
-        ->GetPredictionManager()
-        ->SetModelCacheKeyForTesting(model_cache_key);
-  }
-
- protected:
-  std::unique_ptr<net::test_server::HttpResponse> HandleGetModelsRequest(
-      const net::test_server::HttpRequest& request) {
-    // Returning nullptr will cause the test server to fallback to serving the
-    // file from the test data directory.
-    if (request.GetURL() == model_file_url_)
-      return nullptr;
-    auto get_models_response = BuildGetModelsResponse();
-    get_models_response->mutable_models(0)->mutable_model()->set_download_url(
-        model_file_url_.spec());
-    std::string serialized_response;
-    get_models_response->SerializeToString(&serialized_response);
-    auto response = std::make_unique<net::test_server::BasicHttpResponse>();
-    response->set_content(serialized_response);
-    response->set_code(net::HTTP_OK);
-    return std::move(response);
-  }
-
-  base::test::ScopedFeatureList scoped_feature_list_;
-  GURL model_file_url_;
-  std::unique_ptr<net::EmbeddedTestServer> download_server_;
-  std::unique_ptr<net::EmbeddedTestServer> models_server_;
-  base::HistogramTester histogram_tester_;
-};
-
-IN_PROC_BROWSER_TEST_F(PredictionModelStoreBrowserTest, TestRegularProfile) {
-  ModelFileObserver model_file_observer;
-  RegisterAndWaitForModelUpdate(&model_file_observer);
-  EXPECT_EQ(model_file_observer.optimization_target(),
-            proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  EXPECT_TRUE(
-      model_file_observer.model_info()->GetModelFilePath().IsAbsolute());
-
-  histogram_tester_.ExpectUniqueSample(
-      "OptimizationGuide.PredictionModelDownloadManager.DownloadStatus",
-      PredictionModelDownloadStatus::kSuccess, 1);
-
-  histogram_tester_.ExpectUniqueSample(
-      "OptimizationGuide.PredictionModelUpdateVersion.PainfulPageLoad",
-      kSuccessfulModelVersion, 1);
-  histogram_tester_.ExpectUniqueSample(
-      "OptimizationGuide.PredictionModelLoadedVersion.PainfulPageLoad",
-      kSuccessfulModelVersion, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(PredictionModelStoreBrowserTest, TestIncognitoProfile) {
-  ModelFileObserver model_file_observer;
-  RegisterAndWaitForModelUpdate(&model_file_observer);
-  histogram_tester_.ExpectUniqueSample(
-      "OptimizationGuide.PredictionModelDownloadManager.DownloadStatus",
-      PredictionModelDownloadStatus::kSuccess, 1);
-  EXPECT_EQ(model_file_observer.optimization_target(),
-            proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  EXPECT_TRUE(
-      model_file_observer.model_info()->GetModelFilePath().IsAbsolute());
-
-  base::HistogramTester histogram_tester_otr;
-  ModelFileObserver model_file_observer_otr;
-  Browser* otr_browser = CreateIncognitoBrowser(browser()->profile());
-  RegisterAndWaitForModelUpdate(&model_file_observer_otr,
-                                otr_browser->profile());
-
-  // No more downloads should happen.
-  histogram_tester_otr.ExpectTotalCount(
-      "OptimizationGuide.PredictionModelDownloadManager.DownloadStatus", 0);
-  EXPECT_EQ(model_file_observer_otr.optimization_target(),
-            proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  EXPECT_EQ(model_file_observer.model_info()->GetModelFilePath(),
-            model_file_observer_otr.model_info()->GetModelFilePath());
-}
-
-// Tests that two similar profiles share the model, and the model is not
-// redownloaded.
-IN_PROC_BROWSER_TEST_F(PredictionModelStoreBrowserTest,
-                       TestSimilarProfilesShareModel) {
-  ModelFileObserver model_file_observer;
-  RegisterAndWaitForModelUpdate(&model_file_observer);
-
-  histogram_tester_.ExpectUniqueSample(
-      "OptimizationGuide.PredictionModelDownloadManager.DownloadStatus",
-      PredictionModelDownloadStatus::kSuccess, 1);
-  EXPECT_EQ(model_file_observer.optimization_target(),
-            proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  EXPECT_TRUE(
-      model_file_observer.model_info()->GetModelFilePath().IsAbsolute());
-
-  base::HistogramTester histogram_tester_foo;
-  ModelFileObserver model_file_observer_foo;
-  Profile* profile_foo = CreateProfile();
-  RegisterAndWaitForModelUpdate(&model_file_observer_foo, profile_foo);
-
-  // No more downloads should happen.
-  histogram_tester_foo.ExpectTotalCount(
-      "OptimizationGuide.PredictionModelDownloadManager.DownloadStatus", 0);
-  EXPECT_EQ(model_file_observer_foo.optimization_target(),
-            proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  EXPECT_EQ(model_file_observer.model_info()->GetModelFilePath(),
-            model_file_observer_foo.model_info()->GetModelFilePath());
-}
-
-// Tests that two dissimilar profiles do not share the model, and the model will
-// be redownloaded.
-IN_PROC_BROWSER_TEST_F(PredictionModelStoreBrowserTest,
-                       TestDissimilarProfilesNotShareModel) {
-  ModelFileObserver model_file_observer;
-  RegisterAndWaitForModelUpdate(&model_file_observer);
-
-  histogram_tester_.ExpectUniqueSample(
-      "OptimizationGuide.PredictionModelDownloadManager.DownloadStatus",
-      PredictionModelDownloadStatus::kSuccess, 1);
-  EXPECT_EQ(model_file_observer.optimization_target(),
-            proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-  EXPECT_TRUE(
-      model_file_observer.model_info()->GetModelFilePath().IsAbsolute());
-
-  {
-    base::HistogramTester histogram_tester_foo;
-    ModelFileObserver model_file_observer_foo;
-    Profile* profile_foo = CreateProfile();
-    SetModelCacheKey(profile_foo, GetModelCacheKey(kTestLocaleFoo));
-
-    RegisterAndWaitForModelUpdate(&model_file_observer_foo, profile_foo);
-    // Same model will be redownloaded.
-    histogram_tester_foo.ExpectUniqueSample(
-        "OptimizationGuide.PredictionModelDownloadManager.DownloadStatus",
-        PredictionModelDownloadStatus::kSuccess, 1);
-    EXPECT_EQ(model_file_observer_foo.optimization_target(),
-              proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-    EXPECT_NE(model_file_observer.model_info()->GetModelFilePath(),
-              model_file_observer_foo.model_info()->GetModelFilePath());
-    EXPECT_TRUE(base::ContentsEqual(
-        model_file_observer.model_info()->GetModelFilePath(),
-        model_file_observer_foo.model_info()->GetModelFilePath()));
-  }
-}
-
-}  // namespace optimization_guide
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 8a825be5..0a8029a 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -3622,9 +3622,12 @@
   // ensures that the PDF plugin has loaded and the right background color is
   // beign used.
   WaitForPluginServiceToLoad();
-  WebContents* guest_contents =
-      LoadPdfGetGuestContents(embedded_test_server()->GetURL("/pdf/test.pdf"));
-  ASSERT_TRUE(guest_contents);
+  MimeHandlerViewGuest* guest = LoadPdfGetMimeHandlerView(
+      embedded_test_server()->GetURL("/pdf/test.pdf"));
+  ASSERT_TRUE(guest);
+  content::RenderFrameHost* guest_mainframe = guest->GetGuestMainFrame();
+  ASSERT_TRUE(guest_mainframe);
+
   const std::string script =
       "window.domAutomationController.send("
       "    window.getComputedStyle(document.body, null)."
@@ -3634,7 +3637,7 @@
                                                      script, &outer));
   std::string inner;
   ASSERT_TRUE(
-      content::ExecuteScriptAndExtractString(guest_contents, script, &inner));
+      content::ExecuteScriptAndExtractString(guest_mainframe, script, &inner));
   EXPECT_EQ(inner, outer);
 }
 
diff --git a/chrome/browser/permissions/unused_site_permissions_service_browsertest.cc b/chrome/browser/permissions/unused_site_permissions_service_browsertest.cc
index e55f07b..5e4f7321 100644
--- a/chrome/browser/permissions/unused_site_permissions_service_browsertest.cc
+++ b/chrome/browser/permissions/unused_site_permissions_service_browsertest.cc
@@ -64,7 +64,7 @@
   map->GetWebsiteSetting(url, url, ContentSettingsType::GEOLOCATION, &info);
   ASSERT_FALSE(info.metadata.last_visited.is_null());
   EXPECT_GE(info.metadata.last_visited,
-            past - content_settings::GetCoarseTimePrecision());
+            past - content_settings::GetCoarseVisitedTimePrecision());
   EXPECT_LE(info.metadata.last_visited, past);
 
   // Navigate to |url|.
@@ -73,7 +73,7 @@
   // Check that the timestamp is updated after a navigation.
   map->GetWebsiteSetting(url, url, ContentSettingsType::GEOLOCATION, &info);
   EXPECT_GE(info.metadata.last_visited,
-            now - content_settings::GetCoarseTimePrecision());
+            now - content_settings::GetCoarseVisitedTimePrecision());
   EXPECT_LE(info.metadata.last_visited, now);
 
   map->SetClockForTesting(base::DefaultClock::GetInstance());
diff --git a/chrome/browser/privacy_guide/android/javatests/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragmentTest.java b/chrome/browser/privacy_guide/android/javatests/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragmentTest.java
index 258096e3..7521193 100644
--- a/chrome/browser/privacy_guide/android/javatests/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragmentTest.java
+++ b/chrome/browser/privacy_guide/android/javatests/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragmentTest.java
@@ -202,7 +202,7 @@
     @Test
     @SmallTest
     @Feature({"PrivacyGuide"})
-    public void testWelcomeCard_nextClickCompletionUserAction() {
+    public void testCompletionCard_nextClickCompletionUserAction() {
         launchPrivacyGuide();
         mActionTester = new UserActionTester();
         // Welcome page -> MSBB page
@@ -211,22 +211,18 @@
 
         // MSBB page -> Sync page
         ViewUtils.waitForView(withText(R.string.url_keyed_anonymized_data_title));
-        testButtons(true, false, false);
         onView(withText(R.string.next)).perform(click());
 
         // Sync page -> SB page
         ViewUtils.waitForView(withText(R.string.privacy_guide_sync_toggle));
-        testButtons(true, true, false);
         onView(withText(R.string.next)).perform(click());
 
         // SB page -> Cookies page
         ViewUtils.waitForView(withText(R.string.privacy_guide_safe_browsing_intro));
-        testButtons(true, true, false);
         onView(withText(R.string.next)).perform(click());
 
         // Cookies page -> Complete page
         ViewUtils.waitForView(withText(R.string.privacy_guide_cookies_intro));
-        testButtons(false, true, true);
         onView(withText(R.string.privacy_guide_finish_button)).perform(click());
 
         // Complete page -> EXIT
@@ -403,7 +399,7 @@
     @Test
     @SmallTest
     @Feature({"PrivacyGuide"})
-    public void testSyncCard_backButton() {
+    public void testSyncCard_backClickHistorySyncUserAction() {
         launchPrivacyGuide();
         mActionTester = new UserActionTester();
 
diff --git a/chrome/browser/privacy_guide/android/junit/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideMetricsDelegateTest.java b/chrome/browser/privacy_guide/android/junit/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideMetricsDelegateTest.java
index 67ab87f..096dc31a 100644
--- a/chrome/browser/privacy_guide/android/junit/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideMetricsDelegateTest.java
+++ b/chrome/browser/privacy_guide/android/junit/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideMetricsDelegateTest.java
@@ -60,10 +60,9 @@
                 .thenReturn(initialMSBBState, finalMSBBState);
     }
 
-    private void triggerMSBBMetricsOnNext() {
-        mPrivacyGuideMetricsDelegate.setInitialStateForCard(PrivacyGuideFragment.FragmentType.MSBB);
-        mPrivacyGuideMetricsDelegate.recordMetricsOnNextForCard(
-                PrivacyGuideFragment.FragmentType.MSBB);
+    private void triggerMetricsOnNext(@PrivacyGuideFragment.FragmentType int fragmentType) {
+        mPrivacyGuideMetricsDelegate.setInitialStateForCard(fragmentType);
+        mPrivacyGuideMetricsDelegate.recordMetricsOnNextForCard(fragmentType);
     }
 
     @Test
@@ -73,7 +72,7 @@
         assertEquals(0,
                 RecordHistogram.getHistogramValueCountForTesting(
                         SETTINGS_STATES_HISTOGRAM, PrivacyGuideSettingsStates.MSBB_OFF_TO_OFF));
-        triggerMSBBMetricsOnNext();
+        triggerMetricsOnNext(PrivacyGuideFragment.FragmentType.MSBB);
         assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         SETTINGS_STATES_HISTOGRAM, PrivacyGuideSettingsStates.MSBB_OFF_TO_OFF));
@@ -86,7 +85,7 @@
         assertEquals(0,
                 RecordHistogram.getHistogramValueCountForTesting(
                         SETTINGS_STATES_HISTOGRAM, PrivacyGuideSettingsStates.MSBB_OFF_TO_ON));
-        triggerMSBBMetricsOnNext();
+        triggerMetricsOnNext(PrivacyGuideFragment.FragmentType.MSBB);
         assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         SETTINGS_STATES_HISTOGRAM, PrivacyGuideSettingsStates.MSBB_OFF_TO_ON));
@@ -99,7 +98,7 @@
         assertEquals(0,
                 RecordHistogram.getHistogramValueCountForTesting(
                         SETTINGS_STATES_HISTOGRAM, PrivacyGuideSettingsStates.MSBB_ON_TO_OFF));
-        triggerMSBBMetricsOnNext();
+        triggerMetricsOnNext(PrivacyGuideFragment.FragmentType.MSBB);
         assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         SETTINGS_STATES_HISTOGRAM, PrivacyGuideSettingsStates.MSBB_ON_TO_OFF));
@@ -112,7 +111,7 @@
         assertEquals(0,
                 RecordHistogram.getHistogramValueCountForTesting(
                         SETTINGS_STATES_HISTOGRAM, PrivacyGuideSettingsStates.MSBB_ON_TO_ON));
-        triggerMSBBMetricsOnNext();
+        triggerMetricsOnNext(PrivacyGuideFragment.FragmentType.MSBB);
         assertEquals(1,
                 RecordHistogram.getHistogramValueCountForTesting(
                         SETTINGS_STATES_HISTOGRAM, PrivacyGuideSettingsStates.MSBB_ON_TO_ON));
@@ -122,7 +121,7 @@
     @SmallTest
     public void testMSBB_nextClickUserAction() {
         mockMSBBState(false, false);
-        triggerMSBBMetricsOnNext();
+        triggerMetricsOnNext(PrivacyGuideFragment.FragmentType.MSBB);
         assertTrue(mActionTester.getActions().contains("Settings.PrivacyGuide.NextClickMSBB"));
     }
 
diff --git a/chrome/browser/privacy_sandbox/android/BUILD.gn b/chrome/browser/privacy_sandbox/android/BUILD.gn
index 62f8471..d7f53597 100644
--- a/chrome/browser/privacy_sandbox/android/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/android/BUILD.gn
@@ -87,6 +87,7 @@
     "javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxTestUtils.java",
     "javatests/src/org/chromium/chrome/browser/privacy_sandbox/v4/AdMeasurementFragmentV4Test.java",
     "javatests/src/org/chromium/chrome/browser/privacy_sandbox/v4/FledgeFragmentV4Test.java",
+    "javatests/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxSettingsFragmentV4Test.java",
     "javatests/src/org/chromium/chrome/browser/privacy_sandbox/v4/TopicsFragmentV4Test.java",
   ]
   deps = [
diff --git a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_consent_eea_v4.xml b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_consent_eea_v4.xml
index a5d16367..4be5754 100644
--- a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_consent_eea_v4.xml
+++ b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_consent_eea_v4.xml
@@ -39,11 +39,12 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
+        android:layout_gravity="center"
         app:maxWidthLandscape="@dimen/privacy_sandbox_dialog_max_width"
         app:maxWidthPortrait="@dimen/privacy_sandbox_dialog_max_width">
 
         <org.chromium.components.browser_ui.widget.FadingEdgeScrollView
-            android:id="@+id/privacy_sandbox_consent_eea_scroll_view"
+            android:id="@+id/privacy_sandbox_dialog_scroll_view"
             android:layout_width="match_parent"
             android:layout_height="0dp"
             android:layout_weight="1">
diff --git a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice_eea_v4.xml b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice_eea_v4.xml
index a93c46f6..996e3c6 100644
--- a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice_eea_v4.xml
+++ b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice_eea_v4.xml
@@ -17,22 +17,24 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
+        android:layout_gravity="center"
         app:maxWidthLandscape="@dimen/privacy_sandbox_dialog_max_width"
         app:maxWidthPortrait="@dimen/privacy_sandbox_dialog_max_width">
 
         <org.chromium.components.browser_ui.widget.FadingEdgeScrollView
-            android:id="@+id/privacy_sandbox_notice_eea_scroll_view"
+            android:id="@+id/privacy_sandbox_dialog_scroll_view"
             android:layout_width="match_parent"
             android:layout_height="0dp"
-            android:layout_weight="1">
+            android:fillViewport="true"
+            android:layout_weight="1"
+            android:visibility="invisible">
 
             <LinearLayout
                 android:id="@+id/privacy_sandbox_notice_eea_content"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:layout_marginHorizontal="@dimen/list_item_default_margin"
-                android:layout_gravity="center_vertical"
-                android:gravity="center_horizontal"
+                android:gravity="center_vertical"
                 android:orientation="vertical">
 
                 <ImageView
@@ -40,6 +42,7 @@
                     android:layout_marginBottom="@dimen/privacy_sandbox_dialog_illustration_margin_bottom"
                     android:layout_height="wrap_content"
                     android:layout_width="wrap_content"
+                    android:layout_gravity="center"
                     app:srcCompat="@drawable/privacy_sandbox_notice_eea_illustration_v4"
                     android:importantForAccessibility="no" />
 
@@ -49,7 +52,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:text="@string/privacy_sandbox_m1_notice_eea_title"
-                    android:gravity="center"
+                    android:layout_gravity="center"
                     style="@style/TextAppearance.Headline.Primary" />
 
                 <TextView
@@ -78,7 +81,6 @@
                     android:id="@+id/dropdown_element"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
-                    android:layout_gravity="center"
                     android:orientation="horizontal">
 
                     <TextView
@@ -107,6 +109,7 @@
                     android:layout_marginBottom="@dimen/privacy_sandbox_notice_margin_bottom"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:layout_gravity="center_vertical"
                     android:text="@string/privacy_sandbox_m1_notice_eea_description_2"
                     style="@style/TextAppearance.TextMedium.Secondary" />
 
@@ -114,12 +117,28 @@
 
         </org.chromium.components.browser_ui.widget.FadingEdgeScrollView>
 
+        <org.chromium.ui.widget.ButtonCompat
+            android:id="@+id/more_button"
+            android:focusable="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/privacy_sandbox_m1_dialog_more_button"
+            android:visibility="gone"
+            android:layout_marginHorizontal="24dp"
+            android:layout_marginVertical="@dimen/promo_dialog_padding"
+            style="@style/FilledButton.Flat" />
+
         <LinearLayout
+            android:id="@+id/action_buttons"
             android:orientation="vertical"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:weightSum="2"
-            android:layout_marginHorizontal="24dp">
+            android:layout_marginTop="@dimen/promo_dialog_padding"
+            android:layout_marginBottom="@dimen/promo_dialog_padding"
+            android:layout_marginHorizontal="24dp"
+            android:visibility="invisible" >
+
 
             <org.chromium.ui.widget.ButtonCompat
                 android:id="@+id/ack_button"
diff --git a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice_row_v4.xml b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice_row_v4.xml
index 2c712c4e..3a7fc24 100644
--- a/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice_row_v4.xml
+++ b/chrome/browser/privacy_sandbox/android/java/res/layout/privacy_sandbox_notice_row_v4.xml
@@ -17,20 +17,22 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
+        android:layout_gravity="center"
         app:maxWidthLandscape="@dimen/privacy_sandbox_dialog_max_width"
         app:maxWidthPortrait="@dimen/privacy_sandbox_dialog_max_width">
 
         <org.chromium.components.browser_ui.widget.FadingEdgeScrollView
-            android:id="@+id/privacy_sandbox_notice_row_scroll_view"
+            android:id="@+id/privacy_sandbox_dialog_scroll_view"
             android:layout_width="match_parent"
             android:layout_height="0dp"
+            android:fillViewport="true"
             android:layout_weight="1">
 
             <LinearLayout
                 android:id="@+id/privacy_sandbox_notice_row_content"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:gravity="center_horizontal"
+                android:gravity="center_vertical"
                 android:orientation="vertical">
 
                 <ImageView
@@ -39,6 +41,7 @@
                     android:layout_marginHorizontal="@dimen/list_item_default_margin"
                     android:layout_height="@dimen/privacy_sandbox_chrome_logo_height"
                     android:layout_width="@dimen/privacy_sandbox_chrome_logo_width"
+                    android:layout_gravity="center"
                     app:srcCompat="@drawable/chrome_sync_logo"
                     android:importantForAccessibility="no" />
 
@@ -48,6 +51,7 @@
                     android:layout_marginHorizontal="@dimen/list_item_default_margin"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
+                    android:layout_gravity="center"
                     android:text="@string/privacy_sandbox_m1_notice_row_title"
                     android:gravity="center"
                     style="@style/TextAppearance.Headline.Primary" />
@@ -127,13 +131,26 @@
 
         </org.chromium.components.browser_ui.widget.FadingEdgeScrollView>
 
+        <org.chromium.ui.widget.ButtonCompat
+            android:id="@+id/more_button"
+            android:focusable="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/privacy_sandbox_m1_dialog_more_button"
+            android:visibility="gone"
+            android:layout_marginHorizontal="24dp"
+            android:layout_marginVertical="@dimen/promo_dialog_padding"
+            style="@style/FilledButton.Flat" />
+
         <LinearLayout
+            android:id="@+id/action_buttons"
             android:orientation="vertical"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:weightSum="2"
-            android:layout_marginTop="@dimen/privacy_sandbox_dialog_buttons_margin_top"
-            android:layout_marginHorizontal="24dp">
+            android:layout_marginVertical="@dimen/promo_dialog_padding"
+            android:layout_marginHorizontal="24dp"
+            android:visibility="invisible">
 
             <org.chromium.ui.widget.ButtonCompat
                 android:id="@+id/ack_button"
diff --git a/chrome/browser/privacy_sandbox/android/java/res/values/dimens.xml b/chrome/browser/privacy_sandbox/android/java/res/values/dimens.xml
index 7fee6f6c..edd30b0 100644
--- a/chrome/browser/privacy_sandbox/android/java/res/values/dimens.xml
+++ b/chrome/browser/privacy_sandbox/android/java/res/values/dimens.xml
@@ -17,7 +17,6 @@
   <dimen name="privacy_sandbox_notice_dialog_dropdown_button_height">24dp</dimen>
   <dimen name="privacy_sandbox_notice_margin_bottom">32dp</dimen>
   <dimen name="privacy_sandbox_dialog_title_margin">20dp</dimen>
-  <dimen name="privacy_sandbox_dialog_buttons_margin_top">24dp</dimen>
   <dimen name="privacy_sandbox_chrome_logo_width">60dp</dimen>
   <dimen name="privacy_sandbox_chrome_logo_height">61dp</dimen>
   <dimen name="privacy_sandbox_interests_illustration_height">185dp</dimen>
diff --git a/chrome/browser/privacy_sandbox/android/java/res/xml/privacy_sandbox_preferences_v4.xml b/chrome/browser/privacy_sandbox/android/java/res/xml/privacy_sandbox_preferences_v4.xml
index 5390a0d7..650f91e 100644
--- a/chrome/browser/privacy_sandbox/android/java/res/xml/privacy_sandbox_preferences_v4.xml
+++ b/chrome/browser/privacy_sandbox/android/java/res/xml/privacy_sandbox_preferences_v4.xml
@@ -11,21 +11,18 @@
     <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="topics"
         android:title="@string/ad_privacy_page_topics_link_row_label"
-        android:summary="@string/ad_privacy_page_topics_link_row_sub_label_enabled"
         android:icon="@drawable/ic_interests_24dp"
         android:fragment="org.chromium.chrome.browser.privacy_sandbox.v4.TopicsFragmentV4"
         app:iconTint="@macro/default_icon_color" />
     <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="fledge"
         android:title="@string/ad_privacy_page_fledge_link_row_label"
-        android:summary="@string/ad_privacy_page_fledge_link_row_sub_label_enabled"
         android:icon="@drawable/ic_checklist_24dp"
         android:fragment="org.chromium.chrome.browser.privacy_sandbox.v4.FledgeFragmentV4"
         app:iconTint="@macro/default_icon_color" />
     <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="ad_measurement"
         android:title="@string/ad_privacy_page_ad_measurement_link_row_label"
-        android:summary="@string/ad_privacy_page_ad_measurement_link_row_sub_label_enabled"
         android:icon="@drawable/ic_bar_chart_24dp"
         android:fragment="org.chromium.chrome.browser.privacy_sandbox.v4.AdMeasurementFragmentV4"
         app:iconTint="@macro/default_icon_color"
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogConsentEEAV4.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogConsentEEAV4.java
index 008f43e..28f9e58 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogConsentEEAV4.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogConsentEEAV4.java
@@ -60,7 +60,7 @@
         noButton.setOnClickListener(this);
         mMoreButton = mContentView.findViewById(R.id.more_button);
         mActionButtons = mContentView.findViewById(R.id.action_buttons);
-        mScrollView = mContentView.findViewById(R.id.privacy_sandbox_consent_eea_scroll_view);
+        mScrollView = mContentView.findViewById(R.id.privacy_sandbox_dialog_scroll_view);
 
         mProgressBarContainer = mContentView.findViewById(R.id.progress_bar_container);
         mConsentViewContainer = mContentView.findViewById(R.id.privacy_sandbox_consent_eea_view);
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeEEAV4.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeEEAV4.java
index 4917577..1365cc7a 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeEEAV4.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeEEAV4.java
@@ -6,10 +6,9 @@
 
 import android.app.Dialog;
 import android.content.Context;
-import android.view.Gravity;
+import android.content.DialogInterface;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 
@@ -27,10 +26,16 @@
 /**
  * Dialog in the form of a notice shown for the Privacy Sandbox.
  */
-public class PrivacySandboxDialogNoticeEEAV4 extends Dialog implements View.OnClickListener {
+public class PrivacySandboxDialogNoticeEEAV4
+        extends Dialog implements View.OnClickListener, DialogInterface.OnShowListener {
     private SettingsLauncher mSettingsLauncher;
     private View mContentView;
 
+    private ButtonCompat mMoreButton;
+    private LinearLayout mActionButtons;
+    private ScrollView mScrollView;
+    private LinearLayout mDropdownElement;
+
     private final CheckableImageView mExpandArrowView;
     private LinearLayout mDropdownContainer;
 
@@ -48,15 +53,30 @@
         ButtonCompat settingsButton = mContentView.findViewById(R.id.settings_button);
         settingsButton.setOnClickListener(this);
 
+        mMoreButton = mContentView.findViewById(R.id.more_button);
+        mActionButtons = mContentView.findViewById(R.id.action_buttons);
+        mScrollView = mContentView.findViewById(R.id.privacy_sandbox_dialog_scroll_view);
+
         // Controls for the expanding section.
-        LinearLayout dropdownElement = mContentView.findViewById(R.id.dropdown_element);
-        dropdownElement.setOnClickListener(this);
+        mDropdownElement = mContentView.findViewById(R.id.dropdown_element);
+        mDropdownElement.setOnClickListener(this);
         mDropdownContainer = mContentView.findViewById(R.id.dropdown_container);
         mExpandArrowView = mContentView.findViewById(R.id.expand_arrow);
         mExpandArrowView.setImageDrawable(PrivacySandboxDialogUtils.createExpandDrawable(context));
         mExpandArrowView.setChecked(isDropdownExpanded());
 
         setBulletsDescription();
+
+        mMoreButton.setOnClickListener(this);
+        setOnShowListener(this);
+
+        mScrollView.getViewTreeObserver().addOnScrollChangedListener(() -> {
+            if (!mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
+                mMoreButton.setVisibility(View.GONE);
+                mActionButtons.setVisibility(View.VISIBLE);
+                mScrollView.post(() -> { mScrollView.pageScroll(ScrollView.FOCUS_DOWN); });
+            }
+        });
     }
 
     @Override
@@ -75,21 +95,21 @@
         } else if (id == R.id.settings_button) {
             PrivacySandboxBridge.promptActionOccurred(PromptAction.NOTICE_OPEN_SETTINGS);
             dismiss();
-            // TODO(b/254408752): Update the referrer.
             PrivacySandboxSettingsBaseFragment.launchPrivacySandboxSettings(
                     getContext(), mSettingsLauncher, PrivacySandboxReferrer.PRIVACY_SANDBOX_NOTICE);
+        } else if (id == R.id.more_button) {
+            if (mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
+                mScrollView.post(() -> { mScrollView.pageScroll(ScrollView.FOCUS_DOWN); });
+            } else {
+                mMoreButton.setVisibility(View.GONE);
+                mActionButtons.setVisibility(View.VISIBLE);
+                mScrollView.post(() -> { mScrollView.pageScroll(ScrollView.FOCUS_DOWN); });
+            }
         } else if (id == R.id.dropdown_element) {
-            var content = mContentView.findViewById(R.id.privacy_sandbox_notice_eea_content);
-            ScrollView scrollView =
-                    mContentView.findViewById(R.id.privacy_sandbox_notice_eea_scroll_view);
-
             if (isDropdownExpanded()) {
                 PrivacySandboxBridge.promptActionOccurred(PromptAction.NOTICE_MORE_INFO_CLOSED);
                 mDropdownContainer.setVisibility(View.GONE);
                 mDropdownContainer.removeAllViews();
-
-                ((FrameLayout.LayoutParams) content.getLayoutParams()).gravity =
-                        Gravity.CENTER_VERTICAL;
             } else {
                 mDropdownContainer.setVisibility(View.VISIBLE);
                 PrivacySandboxBridge.promptActionOccurred(PromptAction.NOTICE_MORE_INFO_OPENED);
@@ -110,12 +130,7 @@
                         R.id.privacy_sandbox_m1_notice_eea_learn_more_bullet_three,
                         R.string.privacy_sandbox_m1_notice_eea_learn_more_bullet_3);
 
-                ((FrameLayout.LayoutParams) content.getLayoutParams()).gravity = Gravity.TOP;
-
-                scrollView.post(() -> {
-                    scrollView.setSmoothScrollingEnabled(true);
-                    scrollView.fullScroll(ScrollView.FOCUS_DOWN);
-                });
+                mScrollView.post(() -> { mScrollView.scrollTo(0, mDropdownElement.getTop()); });
             }
 
             mExpandArrowView.setChecked(isDropdownExpanded());
@@ -128,6 +143,18 @@
         }
     }
 
+    @Override
+    public void onShow(DialogInterface dialogInterface) {
+        if (mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
+            mMoreButton.setVisibility(View.VISIBLE);
+            mActionButtons.setVisibility(View.GONE);
+        } else {
+            mMoreButton.setVisibility(View.GONE);
+            mActionButtons.setVisibility(View.VISIBLE);
+        }
+        mScrollView.setVisibility(View.VISIBLE);
+    }
+
     private void setBulletsDescription() {
         PrivacySandboxDialogUtils.setBulletText(getContext(), mContentView,
                 R.id.privacy_sandbox_m1_notice_eea_bullet_one,
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeROWV4.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeROWV4.java
index 3b340d7c..f2fcde1 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeROWV4.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxDialogNoticeROWV4.java
@@ -6,6 +6,7 @@
 
 import android.app.Dialog;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -25,13 +26,17 @@
 /**
  * Dialog in the form of a notice shown for the Privacy Sandbox.
  */
-public class PrivacySandboxDialogNoticeROWV4 extends Dialog implements View.OnClickListener {
+public class PrivacySandboxDialogNoticeROWV4
+        extends Dialog implements View.OnClickListener, DialogInterface.OnShowListener {
     private SettingsLauncher mSettingsLauncher;
     private View mContentView;
 
     private final CheckableImageView mExpandArrowView;
     private LinearLayout mDropdownContainer;
     private LinearLayout mDropdownElement;
+    private ButtonCompat mMoreButton;
+    private LinearLayout mActionButtons;
+    private ScrollView mScrollView;
 
     public PrivacySandboxDialogNoticeROWV4(
             Context context, @NonNull SettingsLauncher settingsLauncher) {
@@ -46,6 +51,10 @@
         ButtonCompat settingsButton = mContentView.findViewById(R.id.settings_button);
         settingsButton.setOnClickListener(this);
 
+        mMoreButton = mContentView.findViewById(R.id.more_button);
+        mActionButtons = mContentView.findViewById(R.id.action_buttons);
+        mScrollView = mContentView.findViewById(R.id.privacy_sandbox_dialog_scroll_view);
+
         // Controls for the expanding section.
         mDropdownElement = mContentView.findViewById(R.id.dropdown_element);
         mDropdownElement.setOnClickListener(this);
@@ -53,6 +62,17 @@
         mExpandArrowView = mContentView.findViewById(R.id.expand_arrow);
         mExpandArrowView.setImageDrawable(PrivacySandboxDialogUtils.createExpandDrawable(context));
         mExpandArrowView.setChecked(isDropdownExpanded());
+
+        mMoreButton.setOnClickListener(this);
+        setOnShowListener(this);
+
+        mScrollView.getViewTreeObserver().addOnScrollChangedListener(() -> {
+            if (!mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
+                mMoreButton.setVisibility(View.GONE);
+                mActionButtons.setVisibility(View.VISIBLE);
+                mScrollView.post(() -> { mScrollView.pageScroll(ScrollView.FOCUS_DOWN); });
+            }
+        });
     }
 
     @Override
@@ -71,19 +91,21 @@
         } else if (id == R.id.settings_button) {
             PrivacySandboxBridge.promptActionOccurred(PromptAction.NOTICE_OPEN_SETTINGS);
             dismiss();
-            // TODO(b/254408752): Update the referrer.
             PrivacySandboxSettingsBaseFragment.launchPrivacySandboxSettings(
                     getContext(), mSettingsLauncher, PrivacySandboxReferrer.PRIVACY_SANDBOX_NOTICE);
+        } else if (id == R.id.more_button) {
+            if (mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
+                mScrollView.post(() -> { mScrollView.pageScroll(ScrollView.FOCUS_DOWN); });
+            } else {
+                mMoreButton.setVisibility(View.GONE);
+                mActionButtons.setVisibility(View.VISIBLE);
+                mScrollView.post(() -> { mScrollView.pageScroll(ScrollView.FOCUS_DOWN); });
+            }
         } else if (id == R.id.dropdown_element) {
-            var content = mContentView.findViewById(R.id.privacy_sandbox_notice_row_content);
-            ScrollView scrollView =
-                    mContentView.findViewById(R.id.privacy_sandbox_notice_row_scroll_view);
-
             if (isDropdownExpanded()) {
                 PrivacySandboxBridge.promptActionOccurred(PromptAction.NOTICE_MORE_INFO_CLOSED);
                 mDropdownContainer.setVisibility(View.GONE);
                 mDropdownContainer.removeAllViews();
-                scrollView.post(() -> { scrollView.fullScroll(ScrollView.FOCUS_UP); });
             } else {
                 mDropdownContainer.setVisibility(View.VISIBLE);
                 PrivacySandboxBridge.promptActionOccurred(PromptAction.NOTICE_MORE_INFO_OPENED);
@@ -98,7 +120,7 @@
                         R.id.privacy_sandbox_m1_notice_row_learn_more_bullet_two,
                         R.string.privacy_sandbox_m1_notice_row_learn_more_bullet_2);
 
-                scrollView.post(() -> { scrollView.scrollTo(0, mDropdownElement.getTop()); });
+                mScrollView.post(() -> { mScrollView.scrollTo(0, mDropdownElement.getTop()); });
             }
 
             mExpandArrowView.setChecked(isDropdownExpanded());
@@ -111,6 +133,18 @@
         }
     }
 
+    @Override
+    public void onShow(DialogInterface dialogInterface) {
+        if (mScrollView.canScrollVertically(ScrollView.FOCUS_DOWN)) {
+            mMoreButton.setVisibility(View.VISIBLE);
+            mActionButtons.setVisibility(View.GONE);
+        } else {
+            mMoreButton.setVisibility(View.GONE);
+            mActionButtons.setVisibility(View.VISIBLE);
+        }
+        mScrollView.setVisibility(View.VISIBLE);
+    }
+
     private boolean isDropdownExpanded() {
         return mDropdownContainer != null && mDropdownContainer.getVisibility() == View.VISIBLE;
     }
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxSettingsFragmentV4.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxSettingsFragmentV4.java
index b494786..6bfdc7c 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxSettingsFragmentV4.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxSettingsFragmentV4.java
@@ -11,12 +11,21 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxSettingsBaseFragment;
 import org.chromium.chrome.browser.privacy_sandbox.R;
+import org.chromium.components.browser_ui.settings.ChromeBasePreference;
 import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 /**
  * Settings fragment for privacy sandbox settings.
  */
 public class PrivacySandboxSettingsFragmentV4 extends PrivacySandboxSettingsBaseFragment {
+    public static final String TOPICS_PREF = "topics";
+    public static final String FLEDGE_PREF = "fledge";
+    public static final String AD_MEASUREMENT_PREF = "ad_measurement";
+
+    private ChromeBasePreference mTopicsPref;
+    private ChromeBasePreference mFledgePref;
+    private ChromeBasePreference mAdMeasurementPref;
+
     @Override
     public void onCreatePreferences(@Nullable Bundle bundle, @Nullable String s) {
         super.onCreatePreferences(bundle, s);
@@ -25,7 +34,31 @@
         // Add all preferences and set the title
         getActivity().setTitle(R.string.ad_privacy_page_title);
         SettingsUtils.addPreferencesFromResource(this, R.xml.privacy_sandbox_preferences_v4);
-
         parseAndRecordReferrer();
+
+        mTopicsPref = findPreference(TOPICS_PREF);
+        mFledgePref = findPreference(FLEDGE_PREF);
+        mAdMeasurementPref = findPreference(AD_MEASUREMENT_PREF);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        updatePrefDescription();
+    }
+
+    private void updatePrefDescription() {
+        mTopicsPref.setSummary(TopicsFragmentV4.isTopicsPrefEnabled()
+                        ? R.string.ad_privacy_page_topics_link_row_sub_label_enabled
+                        : R.string.ad_privacy_page_topics_link_row_sub_label_disabled);
+
+        mFledgePref.setSummary(FledgeFragmentV4.isFledgePrefEnabled()
+                        ? R.string.ad_privacy_page_fledge_link_row_sub_label_enabled
+                        : R.string.ad_privacy_page_fledge_link_row_sub_label_disabled);
+
+        mAdMeasurementPref.setSummary(AdMeasurementFragmentV4.isAdMeasurementPrefEnabled()
+                        ? R.string.ad_privacy_page_ad_measurement_link_row_sub_label_enabled
+                        : R.string.ad_privacy_page_ad_measurement_link_row_sub_label_disabled);
     }
 }
diff --git a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
index c523826..58053e1f 100644
--- a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
+++ b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogTest.java
@@ -9,7 +9,6 @@
 import static androidx.test.espresso.action.ViewActions.scrollTo;
 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
@@ -170,7 +169,7 @@
 
     private ScrollView getScrollView() {
         ScrollView[] scrollViews = {null};
-        onView(withId(R.id.privacy_sandbox_consent_eea_scroll_view)).check(((v, e) -> {
+        onView(withId(R.id.privacy_sandbox_dialog_scroll_view)).check(((v, e) -> {
             scrollViews[0] = ((ScrollView) v);
         }));
         return scrollViews[0];
@@ -465,7 +464,7 @@
         assertEquals("Last dialog action", PromptAction.NOTICE_SHOWN,
                 (int) mFakePrivacySandboxBridge.getLastPromptAction());
         // Ack the notice and verify it worked correctly.
-        onView(withId(R.id.ack_button)).perform(click());
+        tryClickOn(withId(R.id.ack_button));
         assertEquals("Last dialog action", PromptAction.NOTICE_ACKNOWLEDGE,
                 (int) mFakePrivacySandboxBridge.getLastPromptAction());
         onView(withId(R.id.privacy_sandbox_notice_title)).check(doesNotExist());
@@ -485,7 +484,7 @@
         onView(withId(R.id.privacy_sandbox_notice_eea_dropdown)).check(doesNotExist());
 
         // Click on the settings button and verify it worked correctly.
-        onView(withId(R.id.settings_button)).perform(click());
+        tryClickOn(withId(R.id.settings_button));
         onView(withId(R.id.privacy_sandbox_notice_title)).check(doesNotExist());
         assertEquals("Last dialog action", PromptAction.NOTICE_OPEN_SETTINGS,
                 (int) mFakePrivacySandboxBridge.getLastPromptAction());
@@ -505,7 +504,7 @@
         assertEquals("Last dialog action", PromptAction.NOTICE_SHOWN,
                 (int) mFakePrivacySandboxBridge.getLastPromptAction());
         // Ack the notice and verify it worked correctly.
-        onView(withId(R.id.ack_button)).perform(click());
+        tryClickOn(withId(R.id.ack_button));
         assertEquals("Last dialog action", PromptAction.NOTICE_ACKNOWLEDGE,
                 (int) mFakePrivacySandboxBridge.getLastPromptAction());
         onView(withId(R.id.privacy_sandbox_notice_title)).check(doesNotExist());
@@ -517,7 +516,6 @@
         assertEquals("Last dialog action", PromptAction.NOTICE_MORE_INFO_OPENED,
                 (int) mFakePrivacySandboxBridge.getLastPromptAction());
 
-        scrollToPosition(0);
         onView(withId(R.id.privacy_sandbox_notice_row_dropdown)).check(matches(isDisplayed()));
         onView(withId(R.id.dropdown_element)).perform(scrollTo(), click());
         assertEquals("Last dialog action", PromptAction.NOTICE_MORE_INFO_CLOSED,
@@ -525,7 +523,7 @@
         onView(withId(R.id.privacy_sandbox_notice_row_dropdown)).check(doesNotExist());
 
         // Click on the settings button and verify it worked correctly.
-        onView(withId(R.id.settings_button)).perform(click());
+        tryClickOn(withId(R.id.settings_button));
         assertEquals("Last dialog action", PromptAction.NOTICE_OPEN_SETTINGS,
                 (int) mFakePrivacySandboxBridge.getLastPromptAction());
         onView(withId(R.id.privacy_sandbox_notice_title)).check(doesNotExist());
diff --git a/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxSettingsFragmentV4Test.java b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxSettingsFragmentV4Test.java
new file mode 100644
index 0000000..feac108
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/android/javatests/src/org/chromium/chrome/browser/privacy_sandbox/v4/PrivacySandboxSettingsFragmentV4Test.java
@@ -0,0 +1,155 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+package org.chromium.chrome.browser.privacy_sandbox.v4;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxTestUtils.getRootViewSanitized;
+import static org.chromium.chrome.browser.privacy_sandbox.v4.AdMeasurementFragmentV4.setAdMeasurementPrefEnabled;
+import static org.chromium.chrome.browser.privacy_sandbox.v4.FledgeFragmentV4.setFledgePrefEnabled;
+import static org.chromium.chrome.browser.privacy_sandbox.v4.TopicsFragmentV4.setTopicsPrefEnabled;
+import static org.chromium.content_public.browser.test.util.TestThreadUtils.runOnUiThreadBlocking;
+import static org.chromium.ui.test.util.ViewUtils.onViewWaiting;
+
+import android.os.Bundle;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.Batch;
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
+import org.chromium.chrome.browser.flags.ChromeSwitches;
+import org.chromium.chrome.browser.preferences.Pref;
+import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxReferrer;
+import org.chromium.chrome.browser.privacy_sandbox.PrivacySandboxSettingsFragment;
+import org.chromium.chrome.browser.privacy_sandbox.R;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.settings.SettingsActivityTestRule;
+import org.chromium.chrome.test.ChromeBrowserTestRule;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.chrome.test.util.ChromeRenderTestRule;
+import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.prefs.PrefService;
+import org.chromium.components.user_prefs.UserPrefs;
+import org.chromium.ui.test.util.RenderTestRule;
+
+import java.io.IOException;
+
+/**
+ * Tests {@link PrivacySandboxSettingsFragmentV4}
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@Batch(Batch.PER_CLASS)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+@Features.EnableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4)
+public final class PrivacySandboxSettingsFragmentV4Test {
+    @Rule
+    public ChromeBrowserTestRule mChromeBrowserTestRule = new ChromeBrowserTestRule();
+
+    @Rule
+    public ChromeRenderTestRule mRenderTestRule =
+            ChromeRenderTestRule.Builder.withPublicCorpus()
+                    .setBugComponent(RenderTestRule.Component.UI_SETTINGS_PRIVACY)
+                    .build();
+
+    @Rule
+    public SettingsActivityTestRule<PrivacySandboxSettingsFragmentV4> mSettingsActivityTestRule =
+            new SettingsActivityTestRule<>(PrivacySandboxSettingsFragmentV4.class);
+
+    @After
+    public void tearDown() {
+        runOnUiThreadBlocking(() -> {
+            PrefService prefService = UserPrefs.get(Profile.getLastUsedRegularProfile());
+            prefService.clearPref(Pref.PRIVACY_SANDBOX_M1_TOPICS_ENABLED);
+            prefService.clearPref(Pref.PRIVACY_SANDBOX_M1_FLEDGE_ENABLED);
+            prefService.clearPref(Pref.PRIVACY_SANDBOX_M1_AD_MEASUREMENT_ENABLED);
+        });
+    }
+
+    private void startPrivacySandboxSettingsV4() {
+        Bundle fragmentArgs = new Bundle();
+        fragmentArgs.putInt(PrivacySandboxSettingsFragment.PRIVACY_SANDBOX_REFERRER,
+                PrivacySandboxReferrer.PRIVACY_SETTINGS);
+        mSettingsActivityTestRule.startSettingsActivity(fragmentArgs);
+        onViewWaiting(withText(R.string.ad_privacy_page_title));
+    }
+
+    @Test
+    @SmallTest
+    @Feature({"RenderTest"})
+    public void testRenderPrivacySandboxSettingsV4() throws IOException {
+        startPrivacySandboxSettingsV4();
+        mRenderTestRule.render(getRootViewSanitized(R.string.ad_privacy_page_title),
+                "privacy_sandbox_settings_v4");
+    }
+
+    @Test
+    @SmallTest
+    public void testTopicsPrefDisabledDescription() {
+        runOnUiThreadBlocking(() -> setTopicsPrefEnabled(false));
+        startPrivacySandboxSettingsV4();
+
+        onView(withText(R.string.ad_privacy_page_topics_link_row_sub_label_disabled))
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    @SmallTest
+    public void testTopicsPrefEnabledDescription() {
+        runOnUiThreadBlocking(() -> setTopicsPrefEnabled(true));
+        startPrivacySandboxSettingsV4();
+
+        onView(withText(R.string.ad_privacy_page_topics_link_row_sub_label_enabled))
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    @SmallTest
+    public void testFledgePrefDisabledDescription() {
+        runOnUiThreadBlocking(() -> setFledgePrefEnabled(false));
+        startPrivacySandboxSettingsV4();
+
+        onView(withText(R.string.ad_privacy_page_fledge_link_row_sub_label_disabled))
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    @SmallTest
+    public void testFledgePrefEnabledDescription() {
+        runOnUiThreadBlocking(() -> setFledgePrefEnabled(true));
+        startPrivacySandboxSettingsV4();
+
+        onView(withText(R.string.ad_privacy_page_fledge_link_row_sub_label_enabled))
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    @SmallTest
+    public void testAdMeasurementPrefDisabledDescription() {
+        runOnUiThreadBlocking(() -> setAdMeasurementPrefEnabled(false));
+        startPrivacySandboxSettingsV4();
+
+        onView(withText(R.string.ad_privacy_page_ad_measurement_link_row_sub_label_disabled))
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    @SmallTest
+    public void testAdMeasurementPrefEnabledDescription() {
+        runOnUiThreadBlocking(() -> setAdMeasurementPrefEnabled(true));
+        startPrivacySandboxSettingsV4();
+
+        onView(withText(R.string.ad_privacy_page_ad_measurement_link_row_sub_label_enabled))
+                .check(matches(isDisplayed()));
+    }
+}
diff --git a/chrome/browser/resources/chromeos/login/debug/debug.js b/chrome/browser/resources/chromeos/login/debug/debug.js
index d487ff0..fd4d258 100644
--- a/chrome/browser/resources/chromeos/login/debug/debug.js
+++ b/chrome/browser/resources/chromeos/login/debug/debug.js
@@ -1530,6 +1530,27 @@
       kind: ScreenKind.NORMAL,
     },
     {
+      id: 'choobe',
+      kind: ScreenKind.NORMAL,
+      handledSteps: 'overview',
+      states: [
+        {
+          id: 'overview',
+          data: {
+            screens: [
+              {
+                title: 'choobeThemeSelectionTileTitle',
+                icon: 'oobe-32:stars',
+                selected: false,
+                screenID: 'screenID',
+              },
+            ],
+          },
+        },
+      ],
+    },
+
+    {
       id: 'marketing-opt-in',
       kind: ScreenKind.NORMAL,
       handledSteps: 'overview',
diff --git a/chrome/browser/resources/password_manager/dialogs/passwords_export_dialog.html b/chrome/browser/resources/password_manager/dialogs/passwords_export_dialog.html
index 08d7279d..f586d44 100644
--- a/chrome/browser/resources/password_manager/dialogs/passwords_export_dialog.html
+++ b/chrome/browser/resources/password_manager/dialogs/passwords_export_dialog.html
@@ -2,7 +2,22 @@
  .action-button {
    margin-inline-start: 8px;
  }
+
+  paper-progress {
+    --paper-progress-active-color: var(--google-blue-500);
+    width: 100%;
+  }
+
+  @media (prefers-color-scheme: dark) {
+    paper-progress {
+          /* TODO(dbeam): this is the same as downloads (and probably anywhere
+           * else that uses paper-progress). Should we make something like a
+           * paper_progress_style_css.html? */
+          --paper-progress-active-color: var(--google-blue-300);
+        }
+      }
 </style>
+
 <template is="dom-if" if="[[showStartDialog_]]" restamp>
  <cr-dialog id="dialogStart" close-text="$i18n{close}" show-on-attach>
    <h1 slot="title" class="dialog-title">$i18n{exportPasswords}</h1>
@@ -21,3 +36,20 @@
    </div>
  </cr-dialog>
 </template>
+
+<template is="dom-if" if="[[showProgressDialog_]]" restamp>
+  <cr-dialog id="dialogProgress" no-cancel="true" show-on-attach>
+    <h1 slot="title" class="dialog-title">
+      $i18n{exportingPasswordsTitle}
+    </h1>
+    <div slot="body">
+      <paper-progress indeterminate class="blue"></paper-progress>
+    </div>
+    <div slot="button-container">
+      <cr-button id="cancelProgressButton" class="header-aligned-button"
+          on-click="onCancelProgressButtonTap_">
+        $i18n{cancel}
+      </cr-button>
+    </div>
+  </cr-dialog>
+</template>
diff --git a/chrome/browser/resources/password_manager/dialogs/passwords_export_dialog.ts b/chrome/browser/resources/password_manager/dialogs/passwords_export_dialog.ts
index 27d60768..4881ccd 100644
--- a/chrome/browser/resources/password_manager/dialogs/passwords_export_dialog.ts
+++ b/chrome/browser/resources/password_manager/dialogs/passwords_export_dialog.ts
@@ -7,6 +7,8 @@
  * passwords.
  */
 
+import 'chrome://resources/polymer/v3_0/paper-progress/paper-progress.js';
+
 import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
 import {assert} from 'chrome://resources/js/assert_ts.js';
 import {microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
@@ -26,6 +28,18 @@
 
 const ProgressStatus = chrome.passwordsPrivate.ExportProgressStatus;
 
+/**
+ * The amount of time (ms) between the start of the export and the moment we
+ * start showing the progress bar.
+ */
+const progressBarDelayMs: number = 100;
+
+/**
+ * The minimum amount of time (ms) that the progress bar will be visible.
+ */
+const progressBarBlockMs: number = 1000;
+
+
 const PasswordsExportDialogElementBase = I18nMixin(PolymerElement);
 
 export class PasswordsExportDialogElement extends
@@ -41,14 +55,23 @@
   static get properties() {
     return {
       showStartDialog_: Boolean,
+      showProgressDialog_: Boolean,
     };
   }
 
   private showStartDialog_: boolean;
+  private showProgressDialog_: boolean;
   private passwordManager_: PasswordManagerProxy =
       PasswordManagerImpl.getInstance();
   private onPasswordsFileExportProgressListener_:
       PasswordsFileExportProgressListener|null = null;
+  // Token for the timeout used to ensure that the progress bar is not shown if
+  // the export takes less than |progressBarDelayMs|.
+  private progressBarDelayToken_: number|null;
+  // Token for the timeout used to ensure that the progress bar is visible for
+  // at least |progressBarBlockMs|.
+  private progressBarBlockToken_: number|null;
+  private delayedProgress_: chrome.passwordsPrivate.PasswordExportProgress|null;
 
   override ready() {
     super.ready();
@@ -68,8 +91,7 @@
     // busy UI.
     this.passwordManager_.requestExportProgressStatus().then(status => {
       if (status === ProgressStatus.IN_PROGRESS) {
-        // TODO(crbug.com/1394416): Show progress dialog once implemented.
-        return;
+        this.switchToDialog_(States.IN_PROGRESS);
       }
     });
 
@@ -83,14 +105,54 @@
    */
   private onPasswordsFileExportProgress_(
       progress: chrome.passwordsPrivate.PasswordExportProgress) {
-    this.processProgress_(progress);
-    // TODO(crbug/1394416): Handle the minimum time the progress bar needs to be
-    // displayed once it's implemented.
+    // If Chrome has already started displaying the progress bar
+    // (|progressBarDelayToken_ is null) and hasn't completed its minimum
+    // display time (|progressBarBlockToken_| is not null) progress should be
+    // cached for consumption when the blocking time ends.
+    const progressBlocked =
+        !this.progressBarDelayToken_ && this.progressBarBlockToken_;
+    if (!progressBlocked) {
+      clearTimeout(this.progressBarDelayToken_!);
+      this.progressBarDelayToken_ = null;
+      this.processProgress_(progress);
+    } else {
+      this.delayedProgress_ = progress;
+    }
+  }
+
+  /**
+   * Displays the progress bar and suspends further UI updates for
+   * |progressBarBlockMs|.
+   */
+  private handleProgressBarDisplay_() {
+    this.progressBarDelayToken_ = null;
+    this.switchToDialog_(States.IN_PROGRESS);
+    this.progressBarBlockToken_ =
+        setTimeout(() => this.processDelayedProgress_(), progressBarBlockMs);
+  }
+
+  /**
+   * Unblocks progress after showing the progress bar for |progressBarBlock|ms
+   * and processes any progress that was delayed.
+   */
+  private processDelayedProgress_() {
+    this.progressBarBlockToken_ = null;
+    if (this.delayedProgress_) {
+      this.processProgress_(this.delayedProgress_);
+      this.delayedProgress_ = null;
+    }
   }
 
   /** Closes the dialog. */
   private close_() {
+    clearTimeout(this.progressBarDelayToken_!);
+    clearTimeout(this.progressBarBlockToken_!);
+    this.progressBarDelayToken_ = null;
+    this.progressBarBlockToken_ = null;
+    this.passwordManager_.removePasswordsFileExportProgressListener(
+        this.onPasswordsFileExportProgressListener_!);
     this.showStartDialog_ = false;
+    this.showProgressDialog_ = false;
     assert(this.onPasswordsFileExportProgressListener_);
     this.passwordManager_.removePasswordsFileExportProgressListener(
         this.onPasswordsFileExportProgressListener_);
@@ -121,15 +183,19 @@
    */
   private processProgress_(progress:
                                chrome.passwordsPrivate.PasswordExportProgress) {
-    switch (progress.status) {
-      case ProgressStatus.SUCCEEDED: {
-        this.close_();
-        break;
-      }
-      case ProgressStatus.FAILED_WRITE_FAILED: {
-        // TODO(crbug/1394416): Show error message once implemneted.
-        break;
-      }
+    if (progress.status === ProgressStatus.IN_PROGRESS) {
+      this.progressBarDelayToken_ = setTimeout(
+          () => this.handleProgressBarDisplay_(), progressBarDelayMs);
+      return;
+    }
+    if (progress.status === ProgressStatus.SUCCEEDED) {
+      // TODO(crbug/1394416): Maybe notify the user of successful completion.
+      this.close_();
+      return;
+    }
+    if (progress.status === ProgressStatus.FAILED_WRITE_FAILED) {
+      // TODO(crbug/1394416): Show error message once implemneted.
+      return;
     }
   }
 
@@ -139,6 +205,7 @@
    */
   private switchToDialog_(state: States) {
     this.showStartDialog_ = state === States.START;
+    this.showProgressDialog_ = state === States.IN_PROGRESS;
   }
 
   /**
@@ -147,6 +214,15 @@
   private onCancelButtonTap_() {
     this.close_();
   }
+
+  /**
+   * Handler for tapping the 'cancel' button on the progress dialog. It should
+   * cancel the export and dismiss the dialog.
+   */
+  private onCancelProgressButtonTap_() {
+    this.passwordManager_.cancelExportPasswords();
+    this.close_();
+  }
 }
 
 
diff --git a/chrome/browser/resources/password_manager/password_manager_proxy.ts b/chrome/browser/resources/password_manager/password_manager_proxy.ts
index 3e185db..5d210bc 100644
--- a/chrome/browser/resources/password_manager/password_manager_proxy.ts
+++ b/chrome/browser/resources/password_manager/password_manager_proxy.ts
@@ -180,6 +180,11 @@
    */
   removePasswordsFileExportProgressListener(
       listener: PasswordsFileExportProgressListener): void;
+
+  /**
+   * Cancels the export in progress.
+   */
+  cancelExportPasswords(): void;
 }
 
 /**
@@ -292,6 +297,10 @@
         listener);
   }
 
+  cancelExportPasswords() {
+    chrome.passwordsPrivate.cancelExportPasswords();
+  }
+
   static getInstance(): PasswordManagerProxy {
     return instance || (instance = new PasswordManagerImpl());
   }
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.ts b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.ts
index 9dee35b..eba4560 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.ts
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_browser_proxy.ts
@@ -273,6 +273,28 @@
    * @return Returns a filepath to the selected file.
    */
   openContainerFileSelector(): Promise<string>;
+
+  /**
+   * Fetches vmdevice sharing info for all known containers and invokes listener
+   * callback.
+   */
+  requestSharedVmDevices(): void;
+
+  /**
+   * @param id GuestId in question.
+   * @param device VmDevice which might be shared.
+   * @return Whether the device is shared.
+   */
+  isVmDeviceShared(id: GuestId, device: string): Promise<boolean>;
+
+  /**
+   * @param id GuestId in question.
+   * @param device VmDevice which might be shared.
+   * @param shared Whether to share the device with the guest.
+   * @return Whether the sharing could be applied.
+   */
+  setVmDeviceShared(id: GuestId, device: string, shared: boolean):
+      Promise<boolean>;
 }
 
 let instance: CrostiniBrowserProxy|null = null;
@@ -430,4 +452,17 @@
   openContainerFileSelector(): Promise<string> {
     return sendWithPromise('openContainerFileSelector');
   }
+
+  requestSharedVmDevices() {
+    chrome.send('requestSharedVmDevices');
+  }
+
+  isVmDeviceShared(id: GuestId, device: string): Promise<boolean> {
+    return sendWithPromise('isVmDeviceShared', id, device);
+  }
+
+  setVmDeviceShared(id: GuestId, device: string, shared: boolean):
+      Promise<boolean> {
+    return sendWithPromise('setVmDeviceShared', id, device, shared);
+  }
 }
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers.html b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers.html
index 5a210b0..4d9f4f2 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers.html
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers.html
@@ -9,10 +9,26 @@
     word-break: break-all;
   }
 
+  .vm-name {
+    color: var(--cros-text-color-primary);
+  }
+
   #createExtraContainer {
     padding-top: 10px;
   }
 
+  .expand-button {
+    --ink-color: var(--cros-text-color-primary);
+    background: none;
+    border: none;
+    box-shadow: none;
+    color: var(--cros-text-color-primary);
+    font-weight: 400;
+    margin-bottom: 12px;
+    min-height: 32px;
+    padding-inline-start: 0;
+  }
+
 </style>
 <div  class="settings-box first">
   <div id="extraContainersDescription" class="start">
@@ -29,55 +45,80 @@
   </cr-button>
 </div>
 
-<div class="list-frame vertical-list">
-  <div class="list-item">
-    <div id="containerName"
-        class="start column-title"
-        aria-hidden="true">
-      $i18n{crostiniExtraContainersContainerNameLabel}
-    </div>
-    <div id="vmName"
-        class="column-title vm-column"
-        aria-hidden="true">
-      $i18n{crostiniExtraContainersVmNameLabel}
-    </div>
-    <div id="IPAddress"
-        class="column-title vm-column"
-        aria-hidden="true">
-      $i18n{crostiniExtraContainersContainerIpLabel}
-    </div>
-    <div id="color"
-        class="column-title vm-column"
-        aria-hidden="true">
+
+
+<template is="dom-repeat"
+    items="[[allVms_]]" as="vmName" index-as="vmIndex"
+    sort="byVmName_" mutable-data>
+  <div id="vm-heading" class="settings-box hr">
+    <div class="list-item">
+      <div class="start column-title">
+        $i18n{crostiniExtraContainersVmNameLabel}:
+        <span class="vm-name">[[vmName]]</span>
+      </div>
     </div>
   </div>
-  <template is="dom-repeat" items="[[allContainers_]]" mutable-data>
-    <div class="list-item">
-      <div class="start" aria-hidden="true">
-        [[item.id.container_name]]
-      </div>
-      <div class="vm-column" aria-hidden="true">
-        [[item.id.vm_name]]
-      </div>
-       <div class="vm-column" aria-hidden="true">
-        [[item.ipv4]]
-      </div>
-      <div class="vm-column"
-          aria-hidden="true">
-        <input type="color"
+  <div class="list-frame vertical-list">
+    <template is="dom-repeat" items="[[allContainers_]]"
+        filter="[[infoHasVmName_(vmName)]]" sort="byGuestId_" mutable-data>
+      <div class="list-item">
+        <div id="containerName" class="start column-title">
+          $i18n{crostiniExtraContainersContainerNameLabel}:
+          <span class="vm-name">[[item.id.container_name]]</span>
+          <cr-button
+              id="expand-button-[[item.id.vm_name]]-[[item.id.container_name]]"
+              class="expand-button"
+              on-click="expandButtonClicked_"
+              data-container-id="[[item.id]]">
+              <iron-icon icon="[[getArrowIcon_(item.id)]]"></iron-icon>
+          </cr-button>
+        </div>
+        <cr-icon-button id="showContainerMenu[[index]]"
+            class="icon-more-vert"
+            title="$i18n{moreActions}"
             data-container-id="[[item.id]]"
-            value="[[item.badge_color]]"
-            on-change="onContainerColorChange_">
+            on-click="onContainerMenuClick_">
+        </cr-icon-button>
       </div>
-      <cr-icon-button id="showContainerMenu[[index]]"
-          class="icon-more-vert"
-          title="$i18n{moreActions}"
-          data-container-id="[[item.id]]"
-          on-click="onContainerMenuClick_">
-      </cr-icon-button>
-    </div>
-  </template>
-</div>
+      <iron-collapse
+          id="collapse-[[item.id.vm_name]]-[[item.id.container_name]]">
+        <div id="details-[[item.id.vm_name]]-[[item.id.container_name]]">
+
+          <div class="settings-box continuation embedded">
+            <div class="start column-title" aria-hidden="true">
+              $i18n{crostiniExtraContainersAppBadgeColor}
+            </div>
+            <input type="color"
+                data-container-id="[[item.id]]"
+                value="[[item.badge_color]]"
+                on-change="onContainerColorChange_">
+          </div>
+          <div class="settings-box continuation embedded">
+            <div class="start column-title" aria-hidden="true">
+              $i18n{crostiniExtraContainersShareMicrophone}
+            </div>
+            <cr-toggle
+                id="microphone-[[item.id.vm_name]]-[[item.id.container_name]]"
+                data-container-id="[[item.id]]"
+                checked="[[isMicrophoneShared_(item.id)]]"
+                on-change="onMicrophoneSharingChanged_">
+            </cr-toggle>
+          </div>
+          <div class="settings-box continuation embedded"
+               hidden="[[!showIp_(item)]]"
+               id="ip-[[item.id.vm_name]]-[[item.id.container_name]]">
+            <div class="start column-title" aria-hidden="true">
+              $i18n{crostiniExtraContainersContainerIpLabel}
+            </div>
+            <div class="vm-column">
+              [[item.ipv4]]
+            </div>
+          </div>
+        </div>
+      </iron-collapse>
+    </template>
+  </div>
+</template>
 
 <template is="dom-if" if="[[showCreateContainerDialog_]]" restamp>
   <settings-crostini-create-container-dialog
diff --git a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers.ts b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers.ts
index f57912fc..a381b6e 100644
--- a/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers.ts
+++ b/chrome/browser/resources/settings/chromeos/crostini_page/crostini_extra_containers.ts
@@ -11,18 +11,23 @@
 import 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
 import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
+import 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
+import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
 import 'chrome://resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './crostini_extra_containers_create_dialog.js';
 import '../../settings_shared.css.js';
 
 import {CrActionMenuElement} from 'chrome://resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
+import {CrToggleElement} from 'chrome://resources/cr_elements/cr_toggle/cr_toggle.js';
 import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
 import {assert} from 'chrome://resources/js/assert_ts.js';
 import {hexColorToSkColor} from 'chrome://resources/js/color_utils.js';
+import {IronCollapseElement} from 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {ContainerInfo, GuestId} from '../guest_os/guest_os_browser_proxy.js';
+import {ContainerInfo, GuestId, ShareableDevices, VM_DEVICE_MICROPHONE} from '../guest_os/guest_os_browser_proxy.js';
+import {equalContainerId} from '../guest_os/guest_os_container_select.js';
 
 import {CrostiniBrowserProxy, CrostiniBrowserProxyImpl, DEFAULT_CROSTINI_CONTAINER, DEFAULT_CROSTINI_VM} from './crostini_browser_proxy.js';
 import {getTemplate} from './crostini_extra_containers.html.js';
@@ -37,6 +42,11 @@
   };
 }
 
+interface SharedVmDevices {
+  id: GuestId;
+  vmDevices: ShareableDevices;
+}
+
 const ExtraContainersElementBase = WebUiListenerMixin(PolymerElement);
 
 class ExtraContainersElement extends ExtraContainersElementBase {
@@ -63,6 +73,20 @@
         },
       },
 
+      allSharedVmDevices_: {
+        type: Array,
+        value() {
+          return [];
+        },
+      },
+
+      allVms_: {
+        type: Array,
+        value() {
+          return [];
+        },
+      },
+
       lastMenuContainerInfo_: {
         type: Object,
       },
@@ -92,6 +116,8 @@
   }
 
   private allContainers_: ContainerInfo[];
+  private allSharedVmDevices_: SharedVmDevices[];
+  private allVms_: string[];
   private browserProxy_: CrostiniBrowserProxy;
   private exportImportInProgress_: boolean;
   private installerShowing_: boolean;
@@ -113,7 +139,12 @@
     this.addWebUiListener(
         'crostini-container-info',
         (infos: ContainerInfo[]) => this.onContainerInfo_(infos));
+    this.addWebUiListener(
+        'crostini-shared-vmdevices',
+        (sharedVmDevices: SharedVmDevices[]) =>
+            this.onSharedVmDevices_(sharedVmDevices));
     this.browserProxy_.requestContainerInfo();
+    this.browserProxy_.requestSharedVmDevices();
   }
 
   override connectedCallback() {
@@ -132,7 +163,49 @@
     this.browserProxy_.requestCrostiniInstallerStatus();
   }
 
+  private setMicrophoneToggle_(id: GuestId, checked: boolean) {
+    const crToggle: CrToggleElement|null =
+        this.shadowRoot!.querySelector<CrToggleElement>(
+            `#microphone-${id.vm_name}-${id.container_name}`);
+    if (!crToggle) {
+      // The toggles may not yet have been added to the DOM.
+      return;
+    }
+    if (crToggle.checked !== checked) {
+      crToggle.set('checked', checked);
+    }
+  }
+
+  private onSharedVmDevices_(sharedVmDevices: SharedVmDevices[]) {
+    this.set('allSharedVmDevices_', sharedVmDevices);
+    for (const sharing of sharedVmDevices) {
+      this.setMicrophoneToggle_(
+          sharing.id, sharing.vmDevices[VM_DEVICE_MICROPHONE]);
+    }
+  }
+
+  private async updateSharedVmDevices_(id: GuestId) {
+    let idx = this.allSharedVmDevices_.findIndex(
+        sharing => equalContainerId(sharing.id, id));
+
+    if (idx < 0) {
+      idx = this.allSharedVmDevices_.push(
+                {id: id, vmDevices: {[VM_DEVICE_MICROPHONE]: false}}) -
+          1;
+    }
+    const result: boolean =
+        await this.browserProxy_.isVmDeviceShared(id, VM_DEVICE_MICROPHONE);
+
+    this.allSharedVmDevices_[idx].vmDevices[VM_DEVICE_MICROPHONE] = result;
+    this.setMicrophoneToggle_(id, result);
+  }
+
   private onContainerInfo_(containerInfos: ContainerInfo[]) {
+    const vmNames: Set<string> = new Set();
+    for (const info of containerInfos) {
+      vmNames.add(info.id.vm_name);
+    }
+    this.set('allVms_', Array.from(vmNames.values()));
     this.set('allContainers_', containerInfos);
   }
 
@@ -218,6 +291,95 @@
       installerShowing: boolean, exportImportInProgress: boolean): boolean {
     return !(installerShowing || exportImportInProgress);
   }
+
+  private byNameWithDefault_(name1: string, name2: string, defaultName: string):
+      number {
+    if (name1 === name2) {
+      return 0;
+    }
+    // defaultName sorts first.
+    if (name1 === defaultName) {
+      return -1;
+    }
+    if (name2 === defaultName) {
+      return 1;
+    }
+    return name1 < name2 ? -1 : 1;
+  }
+
+  private byVmName_(name1: string, name2: string) {
+    return this.byNameWithDefault_(name1, name2, DEFAULT_CROSTINI_VM);
+  }
+
+  private byGuestId_(id1: GuestId, id2: GuestId): number {
+    const result = this.byVmName_(id1.vm_name, id2.vm_name);
+    if (result !== 0) {
+      return result;
+    }
+    return this.byNameWithDefault_(
+        id1.container_name, id2.container_name, DEFAULT_CROSTINI_CONTAINER);
+  }
+
+  private infoHasVmName_(vmName: string): (info: ContainerInfo) => boolean {
+    return info => vmName === info.id.vm_name;
+  }
+
+  private isMicrophoneShared_(id: GuestId): boolean {
+    const deviceSharing: SharedVmDevices|undefined =
+        this.allSharedVmDevices_.find(
+            (sharing: SharedVmDevices) => equalContainerId(sharing.id, id));
+    if (!deviceSharing) {
+      return false;
+    }
+    return deviceSharing.vmDevices[VM_DEVICE_MICROPHONE];
+  }
+
+  private async onMicrophoneSharingChanged_(event: Event) {
+    const target = event.currentTarget as HtmlElementWithData<HTMLInputElement>;
+    const id = target['dataContainerId'];
+    const shared = target.checked;
+
+    await this.browserProxy_.setVmDeviceShared(
+        id, VM_DEVICE_MICROPHONE, shared);
+    await this.updateSharedVmDevices_(id);
+  }
+
+  private expandButtonClicked_(event: Event) {
+    const target = event.currentTarget as HtmlElementWithData;
+    const id = target['dataContainerId'];
+
+    const collapse: IronCollapseElement|null =
+        this.shadowRoot!.querySelector<IronCollapseElement>(
+            `#collapse-${id.vm_name}-${id.container_name}`);
+    if (collapse) {
+      collapse.toggle();
+
+      const icon = target.querySelector('iron-icon');
+      if (icon) {
+        icon.set(
+            'icon',
+            collapse.opened ? 'cr:arrow-drop-down' : 'cr:arrow-drop-up');
+      }
+    }
+  }
+
+  private isExpanded_(id: GuestId): boolean {
+    const collapse: IronCollapseElement|null =
+        this.shadowRoot!.querySelector<IronCollapseElement>(
+            `#collapse-${id.vm_name}-${id.container_name}`);
+    if (collapse) {
+      return collapse.opened;
+    }
+    return false;
+  }
+
+  private getArrowIcon_(id: GuestId): string {
+    return this.isExpanded_(id) ? 'cr:arrow-drop-down' : 'cr:arrow-drop-up';
+  }
+
+  private showIp_(info: ContainerInfo): boolean {
+    return !!info.ipv4 && info.ipv4.length > 0;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/settings/chromeos/guest_os/guest_os_browser_proxy.ts b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_browser_proxy.ts
index fe71a41..e6a481b 100644
--- a/chrome/browser/resources/settings/chromeos/guest_os/guest_os_browser_proxy.ts
+++ b/chrome/browser/resources/settings/chromeos/guest_os/guest_os_browser_proxy.ts
@@ -44,6 +44,12 @@
   }[guestOs];
 }
 
+export const VM_DEVICE_MICROPHONE = 'microphone';
+
+export interface ShareableDevices {
+  [VM_DEVICE_MICROPHONE]: boolean;
+}
+
 /**
  * |ipv4| below is null if the guest is not currently running.
  */
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
index b31a2374..eb9ac53 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
@@ -316,6 +316,14 @@
     }
   }
 
+  // Dispatch an event that signal that the auth token is invalid. This will
+  // reopen the password prompt.
+  dispatchAuthTokenInvalidEvent_() {
+    const authTokenInvalid =
+        new CustomEvent('auth-token-invalid', {bubbles: true, composed: true});
+    this.dispatchEvent(authTokenInvalid);
+  }
+
   /**
    * @param {!Event} event
    * @private
@@ -331,9 +339,7 @@
         this.authToken.token, target.checked, (success) => {
           if (!success) {
             target.checked = !target.checked;
-            const authTokenInvalid = new CustomEvent(
-                'auth-token-invalid', {bubbles: true, composed: true});
-            this.dispatchEvent(authTokenInvalid);
+            this.dispatchAuthTokenInvalidEvent_();
           }
         });
   }
@@ -628,15 +634,22 @@
     }
     try {
       if (!this.authToken) {
-        console.error('Recovery changed with expired token.');
+        this.dispatchAuthTokenInvalidEvent_();
         return;
       }
 
       const {result} = await this.recoveryFactorEditor.configure(
           this.authToken.token, shouldEnable);
-      if (result !== RecoveryFactorEditor_ConfigureResult.kSuccess) {
-        console.error('RecoveryFactorEditor::Configure failed:', result);
-        return;
+      switch (result) {
+        case RecoveryFactorEditor_ConfigureResult.kSuccess:
+          break;
+        case RecoveryFactorEditor_ConfigureResult.kInvalidTokenError:
+          // This will open the password prompt.
+          this.dispatchAuthTokenInvalidEvent_();
+          return;
+        case RecoveryFactorEditor_ConfigureResult.kClientError:
+          console.error('Error configuring recovery');
+          return;
       }
     } finally {
       this.recoveryChangeInProcess_ = false;
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_sandbox/privacy_sandbox_topics_subpage.html b/chrome/browser/resources/settings/privacy_page/privacy_sandbox/privacy_sandbox_topics_subpage.html
index 2d67ab1..bf29717b 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_sandbox/privacy_sandbox_topics_subpage.html
+++ b/chrome/browser/resources/settings/privacy_page/privacy_sandbox/privacy_sandbox_topics_subpage.html
@@ -1,6 +1,63 @@
+<style include="cr-shared-style">
+  #currentTopicsSection {
+    align-items: center;
+    display: flex;
+    padding: 0 var(--cr-section-padding);
+  }
+
+  #currentTopicsSectionWrapper {
+    width: 100%;
+  }
+
+  #currentTopicsHeading {
+    color: var(--cr-secondary-text-color);
+    font-size: 100%;
+    font-weight: 500;
+    margin: 0;
+    padding-block-start: var(--cr-section-vertical-padding);
+  }
+
+  #currentTopicsDescription {
+    padding-block-end: var(--cr-section-vertical-padding);
+  }
+
+  .no-topics {
+    padding-block-end: 32px;
+    padding-block-start: 16px;
+    padding-inline-start: 40px;
+  }
+</style>
+
 <settings-toggle-button
     id="topicsToggle"
     pref="{{prefs.privacy_sandbox.m1.topics_enabled}}"
     label="$i18n{topicsPageToggleLabel}"
     sub-label="$i18n{topicsPageToggleSubLabel}">
 </settings-toggle-button>
+<div id="currentTopicsSection">
+  <div id="currentTopicsSectionWrapper" class="hr">
+    <h2 id="currentTopicsHeading">
+      $i18n{topicsPageCurrentTopicsHeading}
+    </h2>
+    <template is="dom-if" if="[[isTopicsEnabledAndLoaded_(
+        prefs.privacy_sandbox.m1.topics_enabled.value, isTopicsListLoaded_)]]"
+        restamp>
+        <!-- TODO(b/254413439): Add "Learn more" link. -->
+        <div id="currentTopicsDescription" class="cr-secondary-text"
+            hidden="[[isTopicsListEmpty_(topicsList_.length)]]">
+          $i18n{topicsPageCurrentTopicsDescription}
+        </div>
+        <!-- TODO(b/254412594): Add Topics list. -->
+        <div id="currentTopicsDescriptionEmpty"
+            class="no-topics cr-secondary-text"
+            hidden="[[!isTopicsListEmpty_(topicsList_.length)]]">
+          $i18n{topicsPageCurrentTopicsDescriptionEmpty}
+        </div>
+    </template>
+    <div id="currentTopicsDescriptionDisabled"
+        class="no-topics cr-secondary-text"
+        hidden="[[prefs.privacy_sandbox.m1.topics_enabled.value]]">
+      $i18n{topicsPageCurrentTopicsDescriptionDisabled}
+    </div>
+  </div>
+</div>
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_sandbox/privacy_sandbox_topics_subpage.ts b/chrome/browser/resources/settings/privacy_page/privacy_sandbox/privacy_sandbox_topics_subpage.ts
index 1727edc0..f6d0015 100644
--- a/chrome/browser/resources/settings/privacy_page/privacy_sandbox/privacy_sandbox_topics_subpage.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_sandbox/privacy_sandbox_topics_subpage.ts
@@ -2,6 +2,7 @@
 // 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_shared_style.css.js';
 import '../../controls/settings_toggle_button.js';
 import '../../prefs/prefs.js';
 
@@ -9,6 +10,7 @@
 
 import {SettingsToggleButtonElement} from '../../controls/settings_toggle_button.js';
 import {PrefsMixin} from '../../prefs/prefs_mixin.js';
+import {PrivacySandboxBrowserProxy, PrivacySandboxBrowserProxyImpl, PrivacySandboxInterest, TopicsState} from '../../privacy_sandbox/privacy_sandbox_browser_proxy.js';
 
 import {getTemplate} from './privacy_sandbox_topics_subpage.html.js';
 
@@ -40,8 +42,55 @@
         type: Object,
         notify: true,
       },
+
+      topicsList_: {
+        type: Array,
+        value() {
+          return [];
+        },
+      },
+
+      /**
+       * Used to determine that the Topics list was already fetched and to
+       * display the current topics description only after the list is loaded,
+       * to avoid displaying first the description for an empty list since the
+       * array is empty at first when the page is loaded and switching to the
+       * default description once the list is fetched.
+       */
+      isTopicsListLoaded_: {
+        type: Boolean,
+        value: false,
+      },
     };
   }
+
+  private topicsList_: PrivacySandboxInterest[];
+  private isTopicsListLoaded_: boolean;
+  private privacySandboxBrowserProxy_: PrivacySandboxBrowserProxy =
+      PrivacySandboxBrowserProxyImpl.getInstance();
+
+  override ready() {
+    super.ready();
+
+    this.privacySandboxBrowserProxy_.getTopicsState().then(
+        state => this.onTopicsStateChanged_(state));
+  }
+
+  private onTopicsStateChanged_(state: TopicsState) {
+    this.topicsList_ = state.topTopics.map(topic => {
+      return {topic, removed: false};
+    });
+    this.isTopicsListLoaded_ = true;
+  }
+
+  private isTopicsEnabledAndLoaded_(): boolean {
+    return this.getPref('privacy_sandbox.m1.topics_enabled').value &&
+        this.isTopicsListLoaded_;
+  }
+
+  private isTopicsListEmpty_(): boolean {
+    return this.topicsList_.length === 0;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/settings/site_settings/constants.ts b/chrome/browser/resources/settings/site_settings/constants.ts
index 6ff4e7a7..459a7668 100644
--- a/chrome/browser/resources/settings/site_settings/constants.ts
+++ b/chrome/browser/resources/settings/site_settings/constants.ts
@@ -168,3 +168,11 @@
  * match for SiteExceptions.
  */
 export const SITE_EXCEPTION_WILDCARD: string = '*';
+
+/**
+ * Corresponds to the animation-duration CSS parameter defined in
+ * chrome/browser/resources/settings/site_settings_page/site_review_shared.css.
+ * Set to be slightly higher, as we want to ensure that the animation is
+ * finished before updating the model for the right visual effect.
+ */
+export const MODEL_UPDATE_DELAY_MS = 300;
diff --git a/chrome/browser/resources/settings/site_settings/review_notification_permissions.ts b/chrome/browser/resources/settings/site_settings/review_notification_permissions.ts
index 6e1800b..9844aa1 100644
--- a/chrome/browser/resources/settings/site_settings/review_notification_permissions.ts
+++ b/chrome/browser/resources/settings/site_settings/review_notification_permissions.ts
@@ -20,6 +20,7 @@
 
 import {BaseMixin} from '../base_mixin.js';
 import {MetricsBrowserProxy, MetricsBrowserProxyImpl, SafetyCheckNotificationsModuleInteractions} from '../metrics_browser_proxy.js';
+import {MODEL_UPDATE_DELAY_MS} from '../site_settings/constants.js';
 
 import {getTemplate} from './review_notification_permissions.html.js';
 import {SiteSettingsMixin} from './site_settings_mixin.js';
@@ -45,14 +46,6 @@
 const SettingsReviewNotificationPermissionsElementBase =
     WebUiListenerMixin(BaseMixin(SiteSettingsMixin(I18nMixin(PolymerElement))));
 
-/**
- * Corresponds to the animation-duration CSS parameter defined
- * in review_notification_permissions.html. Set to be slightly higher, as we
- * want to ensure that the animation is finished before updating the model for
- * the right visual effect.
- */
-const MODEL_UPDATE_DELAY_MS = 300;
-
 export class SettingsReviewNotificationPermissionsElement extends
     SettingsReviewNotificationPermissionsElementBase {
   static get is() {
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_permissions_browser_proxy.ts b/chrome/browser/resources/settings/site_settings/site_settings_permissions_browser_proxy.ts
index 0ee32c1..e19739e 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_permissions_browser_proxy.ts
+++ b/chrome/browser/resources/settings/site_settings/site_settings_permissions_browser_proxy.ts
@@ -24,6 +24,14 @@
  */
 export interface SiteSettingsPermissionsBrowserProxy {
   /**
+   * Allow permissions again for an unused site where permissions were
+   * auto-revoked. The origin will not appear again for the user to review and
+   * permissions will not be auto-revoked for this origin in the future.
+   */
+  allowPermissionsAgainForUnusedSite(unusedSitePermissions:
+                                         UnusedSitePermissions): void;
+
+  /**
    * Gets the unused origins along with the permissions they have been granted.
    */
   getRevokedUnusedSitePermissionsList(): Promise<UnusedSitePermissions[]>;
@@ -31,6 +39,11 @@
 
 export class SiteSettingsPermissionsBrowserProxyImpl implements
     SiteSettingsPermissionsBrowserProxy {
+  allowPermissionsAgainForUnusedSite(unusedSitePermissions:
+                                         UnusedSitePermissions) {
+    chrome.send('allowPermissionsAgainForUnusedSite', [unusedSitePermissions]);
+  }
+
   getRevokedUnusedSitePermissionsList() {
     return sendWithPromise('getRevokedUnusedSitePermissionsList');
   }
diff --git a/chrome/browser/resources/settings/site_settings_page/unused_site_permissions.ts b/chrome/browser/resources/settings/site_settings_page/unused_site_permissions.ts
index 496356b..9433c99 100644
--- a/chrome/browser/resources/settings/site_settings_page/unused_site_permissions.ts
+++ b/chrome/browser/resources/settings/site_settings_page/unused_site_permissions.ts
@@ -22,7 +22,7 @@
 import {PluralStringProxyImpl} from 'chrome://resources/js/plural_string_proxy.js';
 import {DomRepeatEvent, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {ContentSettingsTypes} from '../site_settings/constants.js';
+import {ContentSettingsTypes, MODEL_UPDATE_DELAY_MS} from '../site_settings/constants.js';
 import {SiteSettingsMixin} from '../site_settings/site_settings_mixin.js';
 import {SiteSettingsPermissionsBrowserProxy, SiteSettingsPermissionsBrowserProxyImpl, UnusedSitePermissions} from '../site_settings/site_settings_permissions_browser_proxy.js';
 
@@ -104,6 +104,7 @@
       SiteSettingsPermissionsBrowserProxyImpl.getInstance();
   private headerString_: string;
   private lastOrigin_: string;
+  private modelUpdateDelayMsForTesting_: number|null = null;
   private sites_: UnusedSitePermissionsDisplay[]|null;
   private shouldShowCompletionInfo_: boolean;
   private subtitleString_: string;
@@ -133,6 +134,14 @@
         'safetyCheckUnusedSitePermissionsAllowAgainAriaLabel', origin);
   }
 
+  // TODO(crbug.com/1393005): Refactor common code across this and
+  // review_notification_permissions.ts.
+  private getModelUpdateDelayMs_() {
+    return this.modelUpdateDelayMsForTesting_ === null ?
+        MODEL_UPDATE_DELAY_MS :
+        this.modelUpdateDelayMsForTesting_;
+  }
+
   /**
    * Text that describes which permissions have been revoked for an origin.
    * Permissions are listed explicitly when there are up to and including 3. For
@@ -198,7 +207,10 @@
     this.lastOrigin_ = item.origin;
     this.showUndoToast_();
     this.hideItem_(this.lastOrigin_);
-    // TODO(crbug.com/1345920): Trigger action in backend.
+    setTimeout(
+        this.browserProxy_.allowPermissionsAgainForUnusedSite.bind(
+            this.browserProxy_, item),
+        this.getModelUpdateDelayMs_());
   }
 
   /* Repopulate the list when unused site permission list is updated. */
@@ -257,6 +269,12 @@
     this.toastText_ = this.i18n(
         'safetyCheckUnusedSitePermissionsToastLabel', this.lastOrigin_);
   }
+
+  // TODO(crbug.com/1393005): Refactor common code across this and
+  // review_notification_permissions.ts.
+  setModelUpdateDelayMsForTesting(delayMs: number) {
+    this.modelUpdateDelayMsForTesting_ = delayMs;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/side_panel/customize_chrome/app.html b/chrome/browser/resources/side_panel/customize_chrome/app.html
index 74d04a2..7edafd6 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/app.html
+++ b/chrome/browser/resources/side_panel/customize_chrome/app.html
@@ -37,7 +37,7 @@
   </div>
   <customize-chrome-categories on-back-click="onBackClick_"
       on-collection-select="onCollectionSelect_" page-name="categories"
-      id="categoriesPage">
+      id="categoriesPage" on-theme-select="onThemeSelect_">
   </customize-chrome-categories>
   <customize-chrome-themes on-back-click="onBackClick_"
       on-theme-select="onThemeSelect_" page-name="themes"
diff --git a/chrome/browser/resources/side_panel/customize_chrome/categories.html b/chrome/browser/resources/side_panel/customize_chrome/categories.html
index 46f3147..28f60e2 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/categories.html
+++ b/chrome/browser/resources/side_panel/customize_chrome/categories.html
@@ -32,11 +32,15 @@
   .tile {
     cursor: pointer;
     grid-column: span 2;
+    outline-width: 0;
     place-self: stretch;
   }
 
+  .tile:focus {
+    box-shadow: 0 0 0 2px var(--cr-focus-outline-color);
+  }
+
   .image-container {
-    /* TODO(crbug.com/1384246): use i18n for title */
     background-color: var(--google-grey-100);
     overflow: hidden;
     padding-top: 100%;
@@ -44,6 +48,13 @@
     width: 100%;
   }
 
+  @media (prefers-color-scheme: dark) {
+    .image-container {
+      background-color: var(--google-grey-900);
+      border: 1px solid var(--google-grey-700);
+    }
+  }
+
   .image-container img {
     height: 100%;
     left: 50%;
@@ -97,7 +108,9 @@
     <h1>Themes</h1>
   </div>
   <cr-grid columns="6">
-    <div class="tile" tabindex="0" role="button">
+    <div class="tile" tabindex="0" id="classicChromeTile"
+        role="button" on-click="onClassicChromeClick_">
+      <!-- TODO(crbug.com/1384245): add image for classic chrome -->
       <div class="image-container"></div>
       <!-- TODO(crbug.com/1399596): use i18n for label -->
       <div class="label">Classic Chrome</div>
diff --git a/chrome/browser/resources/side_panel/customize_chrome/categories.ts b/chrome/browser/resources/side_panel/customize_chrome/categories.ts
index 33f4e5f..9b83f56 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/categories.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/categories.ts
@@ -16,6 +16,7 @@
 export interface CategoriesElement {
   $: {
     backButton: HTMLElement,
+    classicChromeTile: HTMLElement,
   };
 }
 
@@ -46,6 +47,11 @@
     });
   }
 
+  private onClassicChromeClick_() {
+    this.pageHandler_.setClassicChromeDefaultTheme();
+    this.dispatchEvent(new Event('theme-select'));
+  }
+
   private onCollectionClick_(e: DomRepeatEvent<BackgroundCollection>) {
     this.dispatchEvent(new CustomEvent<BackgroundCollection>(
         'collection-select', {detail: e.model.item}));
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
index cdc20b8..78bce993 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.cc
@@ -1201,10 +1201,11 @@
 
   ASSERT_EQ(1, test_event_router_->GetEventCount(
                    OnPolicySpecifiedPasswordReuseDetected::kEventName));
-  auto captured_args = event_observer.PassEventArgs().GetList()[0].Clone();
-  EXPECT_EQ(kPasswordReuseURL, captured_args.FindKey("url")->GetString());
-  EXPECT_EQ(kUserName, captured_args.FindKey("userName")->GetString());
-  EXPECT_TRUE(captured_args.FindKey("isPhishingUrl")->GetBool());
+  const auto captured_args =
+      std::move(event_observer.PassEventArgs().GetList()[0].GetDict());
+  EXPECT_EQ(kPasswordReuseURL, *captured_args.FindString("url"));
+  EXPECT_EQ(kUserName, *captured_args.FindString("userName"));
+  EXPECT_TRUE(*captured_args.FindBool("isPhishingUrl"));
 
   // If the reused password is not Enterprise password but the account is
   // GSuite, event should be sent.
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc
index 921033f..f8e17a2 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_test_utils.cc
@@ -595,21 +595,21 @@
       machine_scope ? policy::POLICY_SCOPE_MACHINE : policy::POLICY_SCOPE_USER);
 }
 
-base::Value CreateOptInEventsValue(
+base::Value::List CreateOptInEventsList(
     const std::map<std::string, std::vector<std::string>>&
         enabled_opt_in_events) {
-  base::Value enabled_opt_in_events_list(base::Value::Type::LIST);
+  base::Value::List enabled_opt_in_events_list;
   for (const auto& enabled_opt_in_event : enabled_opt_in_events) {
-    base::Value event_value(base::Value::Type::DICTIONARY);
-    event_value.SetStringKey(enterprise_connectors::kKeyOptInEventName,
-                             enabled_opt_in_event.first);
+    base::Value::Dict event_value;
+    event_value.Set(enterprise_connectors::kKeyOptInEventName,
+                    enabled_opt_in_event.first);
 
-    base::Value url_patterns_list(base::Value::Type::LIST);
+    base::Value::List url_patterns_list;
     for (const auto& url_pattern : enabled_opt_in_event.second) {
       url_patterns_list.Append(url_pattern);
     }
-    event_value.SetKey(enterprise_connectors::kKeyOptInEventUrlPatterns,
-                       std::move(url_patterns_list));
+    event_value.Set(enterprise_connectors::kKeyOptInEventUrlPatterns,
+                    std::move(url_patterns_list));
 
     enabled_opt_in_events_list.Append(std::move(event_value));
   }
@@ -632,22 +632,22 @@
   }
 
   if (settings_list->empty()) {
-    base::Value settings(base::Value::Type::DICTIONARY);
+    base::Value::Dict settings;
 
-    settings.SetKey(enterprise_connectors::kKeyServiceProvider,
-                    base::Value("google"));
+    settings.Set(enterprise_connectors::kKeyServiceProvider,
+                 base::Value("google"));
     if (!enabled_event_names.empty()) {
-      base::Value enabled_event_name_list(base::Value::Type::LIST);
+      base::Value::List enabled_event_name_list;
       for (const auto& enabled_event_name : enabled_event_names) {
         enabled_event_name_list.Append(enabled_event_name);
       }
-      settings.SetKey(enterprise_connectors::kKeyEnabledEventNames,
-                      std::move(enabled_event_name_list));
+      settings.Set(enterprise_connectors::kKeyEnabledEventNames,
+                   std::move(enabled_event_name_list));
     }
 
     if (!enabled_opt_in_events.empty()) {
-      settings.SetKey(enterprise_connectors::kKeyEnabledOptInEvents,
-                      CreateOptInEventsValue(enabled_opt_in_events));
+      settings.Set(enterprise_connectors::kKeyEnabledOptInEvents,
+                   CreateOptInEventsList(enabled_opt_in_events));
     }
 
     settings_list->Append(std::move(settings));
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
index d9e7b51..eb0c05a 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service_unittest.cc
@@ -3112,13 +3112,12 @@
   download_service_->MaybeSendDangerousDownloadOpenedReport(&item, false);
   ASSERT_EQ(1, test_event_router_->GetEventCount(
                    OnDangerousDownloadOpened::kEventName));
-  auto captured_args = event_observer.PassEventArgs().GetList()[0].Clone();
-  EXPECT_EQ("http://example.com/a.exe",
-            captured_args.FindKey("url")->GetString());
+  const auto captured_args =
+      std::move(event_observer.PassEventArgs().GetList()[0].GetDict());
+  EXPECT_EQ("http://example.com/a.exe", *captured_args.FindString("url"));
   EXPECT_EQ(base::HexEncode(hash.data(), hash.size()),
-            captured_args.FindKey("downloadDigestSha256")->GetString());
-  EXPECT_EQ(target_path.MaybeAsASCII(),
-            captured_args.FindKey("fileName")->GetString());
+            *captured_args.FindString("downloadDigestSha256"));
+  EXPECT_EQ(target_path.MaybeAsASCII(), *captured_args.FindString("fileName"));
 
   // No event is triggered if in incognito mode..
   content::DownloadItemUtils::AttachInfoForTesting(
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
index 8ffdbd6..cedbb5c 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
@@ -245,10 +245,11 @@
   // without safe browsing enabled. An incident will be created within
   // PreProfileAdd if requested. |incidents_sent|, if provided, will be set
   // in the profile's preference.
-  TestingProfile* CreateProfile(const std::string& profile_name,
-                                SafeBrowsingDisposition safe_browsing_opt_in,
-                                OnProfileAdditionAction on_addition_action,
-                                std::unique_ptr<base::Value> incidents_sent) {
+  TestingProfile* CreateProfile(
+      const std::string& profile_name,
+      SafeBrowsingDisposition safe_browsing_opt_in,
+      OnProfileAdditionAction on_addition_action,
+      absl::optional<base::Value::Dict> incidents_sent) {
     // Create prefs for the profile with safe browsing enabled or not.
     std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> prefs(
         new sync_preferences::TestingPrefServiceSyncable);
@@ -262,7 +263,8 @@
         safe_browsing_opt_in == EXTENDED_REPORTING_ONLY ||
             safe_browsing_opt_in == SAFE_BROWSING_AND_EXTENDED_REPORTING);
     if (incidents_sent)
-      prefs->Set(prefs::kSafeBrowsingIncidentsSent, *incidents_sent);
+      prefs->SetDict(prefs::kSafeBrowsingIncidentsSent,
+                     std::move(*incidents_sent));
 
     // Remember whether or not to create an incident.
     profile_properties_[profile_name].on_addition_action = on_addition_action;
@@ -627,7 +629,7 @@
 
   // Create the profile, thereby causing the test to begin.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -656,7 +658,7 @@
 
   // Create the profile, thereby causing the test to begin.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_ADD_TWO_INCIDENTS, nullptr);
+                ON_PROFILE_ADDITION_ADD_TWO_INCIDENTS, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -685,7 +687,7 @@
   CreateIncidentReportingService();
   // Create the profile, thereby causing the test to begin.
   CreateProfile("profile1", EXTENDED_REPORTING_ONLY,
-                ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -702,8 +704,9 @@
 TEST_F(IncidentReportingServiceTest, NoUploadBeforeExtendedReporting) {
   CreateIncidentReportingService();
   // Create the profile, thereby causing the test to begin.
-  Profile* profile = CreateProfile("profile1", SAFE_BROWSING_ONLY,
-                                   ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+  Profile* profile =
+      CreateProfile("profile1", SAFE_BROWSING_ONLY,
+                    ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
       instance_->GetIncidentReceiver());
@@ -757,7 +760,7 @@
 
   // Create the profile, thereby causing the test to begin.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -782,7 +785,7 @@
   // Create the profile, thereby causing the test to begin.
   Profile* profile =
       CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                    ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                    ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -817,7 +820,7 @@
   // Create the profile, thereby causing the test to begin.
   Profile* profile =
       CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                    ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                    ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -850,7 +853,7 @@
 
   // Create the profile, thereby causing the test to begin.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -873,7 +876,7 @@
   // Create the profile, thereby causing the test to begin.
   Profile* profile =
       CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                    ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                    ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -902,7 +905,7 @@
   // Create the profile, thereby causing the test to begin.
   Profile* profile =
       CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                    ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                    ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -931,7 +934,7 @@
   CreateIncidentReportingService();
   // Create the profile, thereby causing the test to begin.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -942,7 +945,7 @@
 
   // Create a second profile with its own incident on addition.
   CreateProfile("profile2", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -961,7 +964,7 @@
   // Create a profile for which an incident will be added.
   Profile* profile =
       CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                    ON_PROFILE_ADDITION_ADD_INCIDENT, nullptr);
+                    ON_PROFILE_ADDITION_ADD_INCIDENT, absl::nullopt);
 
   // Hook up a callback to run when the upload is started that will post a task
   // to delete the profile. This task will run before the upload finishes.
@@ -1004,7 +1007,7 @@
   CreateIncidentReportingService();
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Add the test incident.
   AddTestIncident(nullptr);
@@ -1034,7 +1037,7 @@
   CreateIncidentReportingService();
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Add the test incident.
   std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
@@ -1075,7 +1078,7 @@
 
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -1103,7 +1106,7 @@
 
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -1133,7 +1136,7 @@
 
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -1152,7 +1155,7 @@
   CreateIncidentReportingService();
   // Add a profile that participates in safe browsing.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Register a callback.
   RegisterAnalysis(ON_DELAYED_ANALYSIS_NO_ACTION);
@@ -1177,7 +1180,7 @@
   // Add a profile that does not participate in safe browsing extended
   // reporting.
   CreateProfile("profile1", SAFE_BROWSING_ONLY, ON_PROFILE_ADDITION_NO_ACTION,
-                nullptr);
+                absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -1201,7 +1204,7 @@
 
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -1237,7 +1240,7 @@
   // Add a profile that participates in safe browsing extended reporting.
   Profile* profile =
       CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                    ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                    ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Add an incident.
   AddTestIncident(profile);
@@ -1267,7 +1270,7 @@
 
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -1293,7 +1296,7 @@
 
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   // Let all tasks run.
   mock_time_task_runner_->FastForwardUntilNoTasksRemain();
@@ -1317,16 +1320,13 @@
       static_cast<int>(safe_browsing::IncidentType::TRACKED_PREFERENCE)));
 
   // Set up a prune state dict with data to be cleared (and not).
-  std::unique_ptr<base::DictionaryValue> incidents_sent(
-      new base::DictionaryValue());
-  auto type_dict = std::make_unique<base::DictionaryValue>();
-  type_dict->SetKey("foo", base::Value("47"));
-  incidents_sent->SetKey(blocklist_load_type,
-                         base::Value::FromUniquePtrValue(std::move(type_dict)));
-  type_dict = std::make_unique<base::DictionaryValue>();
-  type_dict->SetKey("bar", base::Value("43"));
-  incidents_sent->SetKey(preference_type,
-                         base::Value::FromUniquePtrValue(std::move(type_dict)));
+  base::Value::Dict incidents_sent;
+  base::Value::Dict type_dict;
+  type_dict.Set("foo", "47");
+  incidents_sent.Set(blocklist_load_type, std::move(type_dict));
+  type_dict = base::Value::Dict();
+  type_dict.Set("bar", "43");
+  incidents_sent.Set(preference_type, std::move(type_dict));
 
   // Add a profile.
   Profile* profile =
@@ -1350,7 +1350,7 @@
   CreateIncidentReportingService();
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
       instance_->GetIncidentReceiver());
@@ -1390,7 +1390,7 @@
   CreateIncidentReportingService();
   // Add a profile that participates in safe browsing extended reporting.
   CreateProfile("profile1", SAFE_BROWSING_AND_EXTENDED_REPORTING,
-                ON_PROFILE_ADDITION_NO_ACTION, nullptr);
+                ON_PROFILE_ADDITION_NO_ACTION, absl::nullopt);
 
   std::unique_ptr<safe_browsing::IncidentReceiver> receiver(
       instance_->GetIncidentReceiver());
diff --git a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
index 8d3c5e4..0aa841d8 100644
--- a/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate_unittest.cc
@@ -153,16 +153,16 @@
       case Value::Type::STRING:
         return Value("i have a spleen");
       case Value::Type::DICTIONARY: {
-        Value value(base::Value::Type::DICTIONARY);
-        value.SetKey("twenty-two", Value(22));
-        value.SetKey("forty-seven", Value(47));
-        return value;
+        Value::Dict dict;
+        dict.Set("twenty-two", 22);
+        dict.Set("forty-seven", 47);
+        return base::Value(std::move(dict));
       }
       case Value::Type::LIST: {
-        Value value(base::Value::Type::LIST);
-        value.Append(22);
-        value.Append(47);
-        return value;
+        Value::List list;
+        list.Append(22);
+        list.Append(47);
+        return base::Value(std::move(list));
       }
       default:
         ADD_FAILURE() << "unsupported value type " << value_type;
diff --git a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_config.cc b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_config.cc
index a46dfa8..bbf5407 100644
--- a/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_config.cc
+++ b/chrome/browser/safe_browsing/settings_reset_prompt/settings_reset_prompt_config.cc
@@ -195,11 +195,12 @@
     return CONFIG_ERROR_MISSING_DOMAIN_HASHES_PARAM;
 
   // Is the input parseable JSON?
-  std::unique_ptr<base::DictionaryValue> domains_dict =
-      base::DictionaryValue::From(
-          base::JSONReader::ReadDeprecated(domain_hashes_json));
-  if (!domains_dict || domains_dict->DictEmpty())
+  absl::optional<base::Value> domains_dict =
+      base::JSONReader::Read(domain_hashes_json);
+  if (!domains_dict || !domains_dict->is_dict() ||
+      domains_dict->GetDict().empty()) {
     return CONFIG_ERROR_BAD_DOMAIN_HASHES_PARAM;
+  }
 
   // The input JSON should be a hash object with hex-encoded 32-byte
   // hashes as keys and integer IDs as values. For example,
diff --git a/chrome/browser/safe_browsing/test_extension_event_observer.cc b/chrome/browser/safe_browsing/test_extension_event_observer.cc
index c58ad14..af7b919 100644
--- a/chrome/browser/safe_browsing/test_extension_event_observer.cc
+++ b/chrome/browser/safe_browsing/test_extension_event_observer.cc
@@ -51,18 +51,16 @@
     const std::string& expected_username,
     int expected_net_error_code) {
   EXPECT_EQ(expected_event_name, latest_event_name_);
-  auto captured_args = PassEventArgs().GetList()[0].Clone();
-  EXPECT_EQ(expected_page_url.spec(),
-            captured_args.FindKey("url")->GetString());
-  EXPECT_EQ(expected_reason, captured_args.FindKey("reason")->GetString());
+  const auto captured_args = std::move(PassEventArgs().GetList()[0].GetDict());
+  EXPECT_EQ(expected_page_url.spec(), *captured_args.FindString("url"));
+  EXPECT_EQ(expected_reason, *captured_args.FindString("reason"));
   if (!expected_username.empty())
-    EXPECT_EQ(expected_username,
-              captured_args.FindKey("userName")->GetString());
+    EXPECT_EQ(expected_username, *captured_args.FindString("userName"));
   if (expected_net_error_code == 0)
-    EXPECT_FALSE(captured_args.FindKey("netErrorCode"));
+    EXPECT_FALSE(captured_args.Find("netErrorCode"));
   else
     EXPECT_EQ(base::NumberToString(expected_net_error_code),
-              captured_args.FindKey("netErrorCode")->GetString());
+              *captured_args.FindString("netErrorCode"));
 }
 
 std::unique_ptr<KeyedService> BuildSafeBrowsingPrivateEventRouter(
diff --git a/chrome/browser/supervised_user/supervised_user_regional_url_filter_browsertest.cc b/chrome/browser/supervised_user/supervised_user_regional_url_filter_browsertest.cc
new file mode 100644
index 0000000..4768c9a
--- /dev/null
+++ b/chrome/browser/supervised_user/supervised_user_regional_url_filter_browsertest.cc
@@ -0,0 +1,134 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "base/command_line.h"
+#include "base/functional/bind.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_piece_forward.h"
+#include "chrome/browser/ash/login/test/logged_in_user_mixin.h"
+#include "chrome/browser/supervised_user/kids_chrome_management/kids_chrome_management_client.h"
+#include "chrome/browser/supervised_user/kids_chrome_management/kids_chrome_management_client_factory.h"
+#include "chrome/browser/supervised_user/kids_chrome_management/kidschromemanagement_messages.pb.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/mixin_based_in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/variations/variations_switches.h"
+#include "content/public/test/browser_test.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/network/public/cpp/network_switches.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "url/gurl.h"
+
+namespace {
+
+using ::kids_chrome_management::ClassifyUrlRequest;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Pointee;
+
+// Surprisingly, we don't have proto-comparators from gtest available. Remove
+// once they're available.
+MATCHER_P(EqualsProto,
+          message,
+          "Match a proto Message equal to the matcher's argument.") {
+  std::string expected_serialized, actual_serialized;
+  message.SerializeToString(&expected_serialized);
+  arg.SerializeToString(&actual_serialized);
+  return expected_serialized == actual_serialized;
+}
+
+// The region code for variations service (any should work).
+constexpr base::StringPiece kRegionCode = "jp";
+
+// Tests custom filtering logic based on regions, for supervised users.
+class SupervisedUserRegionalURLFilterTest
+    : public MixinBasedInProcessBrowserTest {
+ public:
+  SupervisedUserRegionalURLFilterTest() = default;
+  ~SupervisedUserRegionalURLFilterTest() override = default;
+
+ protected:
+  class MockKidsChromeManagementClient : public KidsChromeManagementClient {
+   public:
+    explicit MockKidsChromeManagementClient(Profile* profile)
+        : KidsChromeManagementClient(profile) {
+      // Without forwarding the call to the real implementation, the browser
+      // hangs and the test times out.
+      ON_CALL(*this, ClassifyURL)
+          .WillByDefault(
+              [this](std::unique_ptr<ClassifyUrlRequest> request_proto,
+                     ::KidsChromeManagementClient::KidsChromeManagementCallback
+                         callback) {
+                KidsChromeManagementClient::ClassifyURL(
+                    std::move(request_proto), std::move(callback));
+              });
+    }
+
+    MOCK_METHOD(
+        void,
+        ClassifyURL,
+        (std::unique_ptr<ClassifyUrlRequest> request_proto,
+         ::KidsChromeManagementClient::KidsChromeManagementCallback callback),
+        (override));
+
+    static std::unique_ptr<KeyedService> MakeUnique(
+        content::BrowserContext* context) {
+      return std::make_unique<NiceMock<MockKidsChromeManagementClient>>(
+          static_cast<Profile*>(context));
+    }
+  };
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    ASSERT_TRUE(embedded_test_server()->Started());
+    std::string host_port = embedded_test_server()->host_port_pair().ToString();
+
+    // Remap all URLs in context of this test to the test server.
+    command_line->AppendSwitchASCII(network::switches::kHostResolverRules,
+                                    "MAP *.example.com " + host_port);
+
+    command_line->AppendSwitchASCII(
+        variations::switches::kVariationsOverrideCountry, kRegionCode);
+    MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line);
+  }
+
+  void SetUpOnMainThread() override {
+    MixinBasedInProcessBrowserTest::SetUpOnMainThread();
+    logged_in_user_mixin_.LogInUser();
+
+    kids_chrome_management_client_ =
+        static_cast<MockKidsChromeManagementClient*>(
+            KidsChromeManagementClientFactory::GetInstance()
+                ->SetTestingFactoryAndUse(
+                    browser()->profile(),
+                    base::BindRepeating(
+                        &MockKidsChromeManagementClient::MakeUnique)));
+  }
+
+  MockKidsChromeManagementClient* kids_chrome_management_client_;
+  ash::LoggedInUserMixin logged_in_user_mixin_{
+      &mixin_host_, ash::LoggedInUserMixin::LogInType::kChild,
+      embedded_test_server(), this};
+};
+
+// Verifies that the regional setting is passed to the RPC backend.
+IN_PROC_BROWSER_TEST_F(SupervisedUserRegionalURLFilterTest, RegionIsAdded) {
+  std::string url_to_classify =
+      "http://www.example.com/simple.html";  // The hostname must be handled by
+                                             // embedded server, see {@link
+                                             // SetUpCommandLine}.
+
+  ClassifyUrlRequest expected;
+  expected.set_region_code(std::string(kRegionCode));
+  expected.set_url(url_to_classify);
+  EXPECT_CALL(*kids_chrome_management_client_,
+              ClassifyURL(Pointee(EqualsProto(expected)), /* callback= */ _));
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL(url_to_classify)));
+}
+
+}  // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
index 6fb8218a..c671c2a 100644
--- a/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_bookmarks_sync_test.cc
@@ -2206,13 +2206,20 @@
 IN_PROC_BROWSER_TEST_F(
     SingleClientBookmarksSyncTestWithEnforcedBookmarksCountLimit,
     ShouldReportErrorIfInitialUpdatesCrossMaxCountLimit) {
-  // Create a bookmark on the server under BookmarkBar with a truncated title.
+  // Create two bookmarks on the server under BookmarkBar with a truncated
+  // title.
+  fake_server::EntityBuilderFactory entity_builder_factory;
   const std::string kTitle1 = "title1";
   const std::string kUrl1 = "http://www.url1.com";
-  fake_server::EntityBuilderFactory entity_builder_factory;
-  fake_server::BookmarkEntityBuilder bookmark_builder =
-      entity_builder_factory.NewBookmarkEntityBuilder(kTitle1);
-  fake_server_->InjectEntity(bookmark_builder.BuildBookmark(GURL(kUrl1)));
+  fake_server_->InjectEntity(
+      entity_builder_factory.NewBookmarkEntityBuilder(kTitle1).BuildBookmark(
+          GURL(kUrl1)));
+
+  const std::string kTitle2 = "title2";
+  const std::string kUrl2 = "http://www.url2.com";
+  fake_server_->InjectEntity(
+      entity_builder_factory.NewBookmarkEntityBuilder(kTitle2).BuildBookmark(
+          GURL(kUrl2)));
 
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   // Set a limit of 4 bookmarks. This should result in an error when we get an
diff --git a/chrome/browser/touch_to_fill/payments/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/payments/android/internal/BUILD.gn
index 2f5c242..9e00bc7c 100644
--- a/chrome/browser/touch_to_fill/payments/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/payments/android/internal/BUILD.gn
@@ -42,6 +42,7 @@
 android_resources("java_resources") {
   sources = [
     "java/res/drawable/touch_to_fill_credit_card_background.xml",
+    "java/res/layout/touch_to_fill_credit_card_header_item.xml",
     "java/res/layout/touch_to_fill_credit_card_sheet.xml",
     "java/res/layout/touch_to_fill_credit_card_sheet_item.xml",
     "java/res/values/dimens.xml",
diff --git a/chrome/browser/touch_to_fill/payments/android/internal/java/res/layout/touch_to_fill_credit_card_header_item.xml b/chrome/browser/touch_to_fill/payments/android/internal/java/res/layout/touch_to_fill_credit_card_header_item.xml
new file mode 100644
index 0000000..43c782f
--- /dev/null
+++ b/chrome/browser/touch_to_fill/payments/android/internal/java/res/layout/touch_to_fill_credit_card_header_item.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2022 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:layout_marginBottom="16dp"
+    android:orientation="vertical">
+
+  <ImageView
+      android:id="@+id/branding_icon"
+      android:layout_width="wrap_content"
+      android:layout_height="@dimen/ttf_for_payments_product_icon_height"
+      android:layout_marginVertical="@dimen/ttf_for_payments_product_icon_margin_vertical"
+      android:importantForAccessibility="no"
+      android:layout_gravity="center_horizontal"/>
+
+    <org.chromium.ui.widget.TextViewWithLeading
+        android:id="@+id/touch_to_fill_sheet_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_horizontal"
+        android:textAlignment="center"
+        android:textAppearance="@style/TextAppearance.Headline.Primary"
+        android:text="@string/autofill_credit_card_bottom_sheet_title"/>
+</LinearLayout>
diff --git a/chrome/browser/touch_to_fill/payments/android/internal/java/res/layout/touch_to_fill_credit_card_sheet.xml b/chrome/browser/touch_to_fill/payments/android/internal/java/res/layout/touch_to_fill_credit_card_sheet.xml
index 92985963..d72035d 100644
--- a/chrome/browser/touch_to_fill/payments/android/internal/java/res/layout/touch_to_fill_credit_card_sheet.xml
+++ b/chrome/browser/touch_to_fill/payments/android/internal/java/res/layout/touch_to_fill_credit_card_sheet.xml
@@ -20,60 +20,57 @@
       android:importantForAccessibility="no"
       app:srcCompat="@drawable/drag_handlebar" />
 
-  <ImageView
-      android:id="@+id/branding_icon"
-      android:layout_below="@id/drag_handlebar"
-      android:layout_centerHorizontal="true"
-      android:layout_width="wrap_content"
-      android:layout_height="@dimen/ttf_for_payments_product_icon_height"
-      android:layout_marginVertical="@dimen/ttf_for_payments_product_icon_margin_vertical"
-      android:importantForAccessibility="no"/>
-
   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/sheet_item_list"
-      android:layout_below="@id/branding_icon"
+      android:layout_below="@id/drag_handlebar"
+      android:layout_above="@id/touch_to_fill_footer"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:layout_marginHorizontal="@dimen/ttf_for_payments_sheet_padding_horizontal"
       android:clipToPadding="false"
       android:divider="@null"
       tools:listitem="@layout/touch_to_fill_credit_card_sheet_item"/>
-  <!-- Divider -->
-  <View style="@style/HorizontalDivider"
+
+  <LinearLayout
+      android:id="@+id/touch_to_fill_footer"
       android:layout_width="match_parent"
-      android:layout_height="@dimen/divider_height"
-      android:layout_above="@id/scan_new_card"
-      android:layout_marginHorizontal="16dp"
-      android:layout_marginBottom="@dimen/ttf_for_payments_buttons_vertical_margin"/>
-  <TextView
-      android:id="@+id/scan_new_card"
-      android:layout_width="wrap_content"
+      android:orientation="vertical"
       android:layout_height="wrap_content"
-      android:layout_above="@id/manage_payment_methods"
-      android:layout_marginBottom="@dimen/ttf_for_payments_buttons_vertical_margin"
-      android:clickable="true"
-      android:drawablePadding="@dimen/ttf_for_payments_menu_item_icon_padding"
-      android:focusable="true"
-      android:gravity="center_vertical|start"
-      android:paddingHorizontal="@dimen/ttf_for_payments_sheet_padding_horizontal"
-      android:textAppearance="@style/TextAppearance.TextMedium.Primary"
-      android:text="@string/autofill_scan_credit_card"
-      android:visibility="gone"
-      app:drawableStartCompat="@drawable/ic_photo_camera"
-      app:drawableTint="@color/default_icon_color_tint_list"/>
-  <TextView
-      android:id="@+id/manage_payment_methods"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:layout_alignParentBottom="true"
-      android:layout_marginBottom="@dimen/ttf_for_payments_buttons_vertical_margin"
-      android:clickable="true"
-      android:drawablePadding="@dimen/ttf_for_payments_menu_item_icon_padding"
-      android:focusable="true"
-      android:gravity="center_vertical|start"
-      android:paddingHorizontal="@dimen/ttf_for_payments_sheet_padding_horizontal"
-      android:textAppearance="@style/TextAppearance.TextMedium.Primary"
-      android:text="@string/autofill_bottom_sheet_manage_payment_methods"
-      app:drawableStartCompat="@drawable/infobar_chrome"
-      app:drawableTint="@color/default_icon_color_tint_list"/>
-</RelativeLayout>
\ No newline at end of file
+      android:layout_alignParentBottom="true">
+    <!-- Divider -->
+    <View style="@style/HorizontalDivider"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/divider_height"
+        android:layout_marginHorizontal="16dp"
+        android:layout_marginBottom="@dimen/ttf_for_payments_buttons_vertical_margin"/>
+    <TextView
+        android:id="@+id/scan_new_card"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/ttf_for_payments_buttons_vertical_margin"
+        android:clickable="true"
+        android:drawablePadding="@dimen/ttf_for_payments_menu_item_icon_padding"
+        android:focusable="true"
+        android:gravity="center_vertical|start"
+        android:paddingHorizontal="@dimen/ttf_for_payments_sheet_padding_horizontal"
+        android:textAppearance="@style/TextAppearance.TextMedium.Primary"
+        android:text="@string/autofill_scan_credit_card"
+        android:visibility="gone"
+        app:drawableStartCompat="@drawable/ic_photo_camera"
+        app:drawableTint="@color/default_icon_color_tint_list"/>
+    <TextView
+        android:id="@+id/manage_payment_methods"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/ttf_for_payments_buttons_vertical_margin"
+        android:clickable="true"
+        android:drawablePadding="@dimen/ttf_for_payments_menu_item_icon_padding"
+        android:focusable="true"
+        android:gravity="center_vertical|start"
+        android:paddingHorizontal="@dimen/ttf_for_payments_sheet_padding_horizontal"
+        android:textAppearance="@style/TextAppearance.TextMedium.Primary"
+        android:text="@string/autofill_bottom_sheet_manage_payment_methods"
+        app:drawableStartCompat="@drawable/infobar_chrome"
+        app:drawableTint="@color/default_icon_color_tint_list"/>
+  </LinearLayout>
+</RelativeLayout>
diff --git a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardControllerRobolectricTest.java b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardControllerRobolectricTest.java
index 8455ac5..2d16b0f2 100644
--- a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardControllerRobolectricTest.java
+++ b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardControllerRobolectricTest.java
@@ -15,6 +15,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.CreditCardProperties.ON_CLICK_ACTION;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.DISMISS_HANDLER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.ItemType.CREDIT_CARD;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.ItemType.HEADER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SCAN_CREDIT_CARD_CALLBACK;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SHOW_CREDIT_CARD_SETTINGS_CALLBACK;
@@ -94,11 +95,13 @@
         mMediator.showSheet(new CreditCard[] {VISA}, false);
 
         ModelList itemList = mTouchToFillCreditCardModel.get(SHEET_ITEMS);
-        assertThat(itemList.size(), is(1));
+        assertThat(itemList.size(), is(2));
 
-        assertThat(itemList.get(0).type, is(CREDIT_CARD));
-        assertThat(itemList.get(0).model.get(CARD_NAME), is(VISA.getCardNameForAutofillDisplay()));
-        assertThat(itemList.get(0).model.get(CARD_NUMBER), is(VISA.getObfuscatedLastFourDigits()));
+        assertThat(itemList.get(0).type, is(HEADER));
+
+        assertThat(itemList.get(1).type, is(CREDIT_CARD));
+        assertThat(itemList.get(1).model.get(CARD_NAME), is(VISA.getCardNameForAutofillDisplay()));
+        assertThat(itemList.get(1).model.get(CARD_NUMBER), is(VISA.getObfuscatedLastFourDigits()));
     }
 
     @Test
@@ -107,16 +110,18 @@
         mMediator.showSheet(new CreditCard[] {VISA, MASTER_CARD}, false);
 
         ModelList itemList = mTouchToFillCreditCardModel.get(SHEET_ITEMS);
-        assertThat(itemList.size(), is(2));
+        assertThat(itemList.size(), is(3));
 
-        assertThat(itemList.get(0).type, is(CREDIT_CARD));
-        assertThat(itemList.get(0).model.get(CARD_NAME), is(VISA.getCardNameForAutofillDisplay()));
-        assertThat(itemList.get(0).model.get(CARD_NUMBER), is(VISA.getObfuscatedLastFourDigits()));
+        assertThat(itemList.get(0).type, is(HEADER));
 
         assertThat(itemList.get(1).type, is(CREDIT_CARD));
-        assertThat(itemList.get(0).model.get(CARD_NAME),
+        assertThat(itemList.get(1).model.get(CARD_NAME), is(VISA.getCardNameForAutofillDisplay()));
+        assertThat(itemList.get(1).model.get(CARD_NUMBER), is(VISA.getObfuscatedLastFourDigits()));
+
+        assertThat(itemList.get(2).type, is(CREDIT_CARD));
+        assertThat(itemList.get(2).model.get(CARD_NAME),
                 is(MASTER_CARD.getCardNameForAutofillDisplay()));
-        assertThat(itemList.get(0).model.get(CARD_NUMBER),
+        assertThat(itemList.get(2).model.get(CARD_NUMBER),
                 is(MASTER_CARD.getObfuscatedLastFourDigits()));
     }
 
@@ -142,9 +147,9 @@
         mMediator.showSheet(new CreditCard[] {VISA}, false);
         assertThat(mTouchToFillCreditCardModel.get(VISIBLE), is(true));
         assertNotNull(
-                mTouchToFillCreditCardModel.get(SHEET_ITEMS).get(0).model.get(ON_CLICK_ACTION));
+                mTouchToFillCreditCardModel.get(SHEET_ITEMS).get(1).model.get(ON_CLICK_ACTION));
 
-        mTouchToFillCreditCardModel.get(SHEET_ITEMS).get(0).model.get(ON_CLICK_ACTION).run();
+        mTouchToFillCreditCardModel.get(SHEET_ITEMS).get(1).model.get(ON_CLICK_ACTION).run();
         verify(mDelegateMock).suggestionSelected(VISA.getGUID());
     }
 
diff --git a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardCoordinator.java b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardCoordinator.java
index c13025a..4fb52a48 100644
--- a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardCoordinator.java
+++ b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardCoordinator.java
@@ -6,6 +6,7 @@
 
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.DISMISS_HANDLER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.ItemType.CREDIT_CARD;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.ItemType.HEADER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SCAN_CREDIT_CARD_CALLBACK;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SHOW_CREDIT_CARD_SETTINGS_CALLBACK;
@@ -65,6 +66,8 @@
         SimpleRecyclerViewAdapter adapter = new SimpleRecyclerViewAdapter(model.get(SHEET_ITEMS));
         adapter.registerType(CREDIT_CARD, TouchToFillCreditCardViewBinder::createCardItemView,
                 TouchToFillCreditCardViewBinder::bindCardItemView);
+        adapter.registerType(HEADER, TouchToFillCreditCardViewBinder::createHeaderItemView,
+                TouchToFillCreditCardViewBinder::bindHeaderView);
         view.setSheetItemListAdapter(adapter);
     }
 
diff --git a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardMediator.java b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardMediator.java
index c2d0311..4ffa41cf 100644
--- a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardMediator.java
+++ b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardMediator.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.touch_to_fill.payments;
 
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.CreditCardProperties.ON_CLICK_ACTION;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.ItemType.CREDIT_CARD;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SHOULD_SHOW_SCAN_CREDIT_CARD;
@@ -13,6 +14,7 @@
 import android.content.Context;
 
 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
+import org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.HeaderProperties;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
@@ -40,6 +42,9 @@
     void showSheet(CreditCard[] cards, boolean shouldShowScanCreditCard) {
         assert cards != null;
 
+        // TODO(1247698): Show GPay logo if there is at least one card coming from
+        // GPay, if there are only local cards show Chrome logo.
+        boolean hasOnlyLocalCards = true;
         ModelList sheetItems = mModel.get(SHEET_ITEMS);
         sheetItems.clear();
 
@@ -47,6 +52,9 @@
             final PropertyModel model = createCardModel(card);
             sheetItems.add(new ListItem(CREDIT_CARD, model));
         }
+
+        sheetItems.add(0, buildHeader(hasOnlyLocalCards));
+
         mModel.set(VISIBLE, true);
         mModel.set(SHOULD_SHOW_SCAN_CREDIT_CARD, shouldShowScanCreditCard);
     }
@@ -89,4 +97,13 @@
                 .with(ON_CLICK_ACTION, () -> { this.onSelectedCreditCard(card.getGUID()); })
                 .build();
     }
+
+    private ListItem buildHeader(boolean hasOnlyLocalCards) {
+        return new ListItem(TouchToFillCreditCardProperties.ItemType.HEADER,
+                new PropertyModel.Builder(HeaderProperties.ALL_KEYS)
+                        .with(IMAGE_DRAWABLE_ID,
+                                hasOnlyLocalCards ? R.drawable.fre_product_logo
+                                                  : R.drawable.google_pay)
+                        .build());
+    }
 }
diff --git a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardProperties.java b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardProperties.java
index 24bb2e1b..0e1f9ae 100644
--- a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardProperties.java
+++ b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardProperties.java
@@ -33,8 +33,11 @@
             SHOW_CREDIT_CARD_SETTINGS_CALLBACK};
 
     @interface ItemType {
+        // The header at the top of the touch to fill sheet.
+        int HEADER = 0;
+
         // A section containing the credit card data.
-        int CREDIT_CARD = 0;
+        int CREDIT_CARD = 1;
     }
 
     /**
@@ -58,5 +61,18 @@
         private CreditCardProperties() {}
     }
 
+    /**
+     * Properties defined here reflect the visible state of the header in the TouchToFill sheet for
+     * payments.
+     */
+    static class HeaderProperties {
+        static final PropertyModel.ReadableIntPropertyKey IMAGE_DRAWABLE_ID =
+                new PropertyModel.ReadableIntPropertyKey("image_drawable_id");
+
+        static final PropertyKey[] ALL_KEYS = {IMAGE_DRAWABLE_ID};
+
+        private HeaderProperties() {}
+    }
+
     private TouchToFillCreditCardProperties() {}
 }
diff --git a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardView.java b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardView.java
index b56c670..763395d7 100644
--- a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardView.java
+++ b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardView.java
@@ -7,16 +7,13 @@
 import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.ImageView;
 import android.widget.RelativeLayout;
 
 import androidx.annotation.Nullable;
-import androidx.core.content.res.ResourcesCompat;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
 import org.chromium.base.Callback;
-import org.chromium.base.ContextUtils;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetObserver;
@@ -34,9 +31,6 @@
     private final RecyclerView mSheetItemListView;
     private Callback<Integer> mDismissHandler;
     private Runnable mScanCreditCardHandler;
-    // TODO(): show gpay logo if there is at least one card coming from GPay,
-    // if there are only local cards show chrome logo
-    private boolean mOnlyLocalCards;
 
     // TODO(crbug.com/1247698): Reuse this logic between different sheets.
     private final BottomSheetObserver mBottomSheetObserver = new EmptyBottomSheetObserver() {
@@ -72,11 +66,6 @@
         int layoutDirection = LocalizationUtils.isLayoutRtl() ? View.LAYOUT_DIRECTION_RTL
                                                               : View.LAYOUT_DIRECTION_LTR;
         mContentView.setLayoutDirection(layoutDirection);
-
-        ImageView brandingIcon = mContentView.findViewById(R.id.branding_icon);
-        brandingIcon.setImageDrawable(ResourcesCompat.getDrawable(mContentView.getResources(),
-                mOnlyLocalCards ? R.drawable.fre_product_logo : R.drawable.google_pay,
-                ContextUtils.getApplicationContext().getTheme()));
         mSheetItemListView = mContentView.findViewById(R.id.sheet_item_list);
         mSheetItemListView.setLayoutManager(
                 new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
diff --git a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardViewBinder.java b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardViewBinder.java
index e4b6c7c..e870aa3 100644
--- a/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardViewBinder.java
+++ b/chrome/browser/touch_to_fill/payments/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/payments/TouchToFillCreditCardViewBinder.java
@@ -10,6 +10,7 @@
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.CreditCardProperties.CARD_NUMBER;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.CreditCardProperties.ON_CLICK_ACTION;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.DISMISS_HANDLER;
+import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.HeaderProperties.IMAGE_DRAWABLE_ID;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SCAN_CREDIT_CARD_CALLBACK;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SHEET_ITEMS;
 import static org.chromium.chrome.browser.touch_to_fill.payments.TouchToFillCreditCardProperties.SHOULD_SHOW_SCAN_CREDIT_CARD;
@@ -65,7 +66,7 @@
     private TouchToFillCreditCardViewBinder() {}
 
     /**
-     * Factory used to create a new View inside the ListView inside the TouchToFillCreditCardView.
+     * Factory used to create a card item inside the ListView inside the TouchToFillCreditCardView.
      * @param parent The parent {@link ViewGroup} of the new item.
      */
     static View createCardItemView(ViewGroup parent) {
@@ -75,19 +76,52 @@
 
     /** Binds the item view to the model properties. */
     static void bindCardItemView(PropertyModel model, View view, PropertyKey propertyKey) {
-        ImageView icon = view.findViewById(R.id.favicon);
-        int iconId = model.get(CARD_ICON_ID);
-        // Generally the resource id for the icon can only be zero in the tests.
-        // For production code a general card icon id is set by default in the CreditCard
-        // constructor if the card issuer is unknown.
-        icon.setImageDrawable(
-                iconId != 0 ? AppCompatResources.getDrawable(view.getContext(), iconId) : null);
-        TextView cardName = view.findViewById(R.id.card_name);
-        cardName.setText(model.get(CARD_NAME));
-        TextView cardNumber = view.findViewById(R.id.card_number);
-        cardNumber.setText(model.get(CARD_NUMBER));
-        TextView expirationDate = view.findViewById(R.id.expiration_date);
-        expirationDate.setText(model.get(CARD_EXPIRATION));
-        view.setOnClickListener(unusedView -> model.get(ON_CLICK_ACTION).run());
+        if (propertyKey == CARD_ICON_ID) {
+            ImageView icon = view.findViewById(R.id.favicon);
+            int iconId = model.get(CARD_ICON_ID);
+            // Generally the resource id for the icon can only be zero in the tests.
+            // For production code a general card icon id is set by default in the CreditCard
+            // constructor if the card issuer is unknown.
+            icon.setImageDrawable(
+                    iconId != 0 ? AppCompatResources.getDrawable(view.getContext(), iconId) : null);
+        } else if (propertyKey == CARD_NAME) {
+            TextView cardName = view.findViewById(R.id.card_name);
+            cardName.setText(model.get(CARD_NAME));
+        } else if (propertyKey == CARD_NUMBER) {
+            TextView cardNumber = view.findViewById(R.id.card_number);
+            cardNumber.setText(model.get(CARD_NUMBER));
+        } else if (propertyKey == CARD_EXPIRATION) {
+            TextView expirationDate = view.findViewById(R.id.expiration_date);
+            expirationDate.setText(model.get(CARD_EXPIRATION));
+        } else if (propertyKey == ON_CLICK_ACTION) {
+            view.setOnClickListener(unusedView -> model.get(ON_CLICK_ACTION).run());
+        } else {
+            assert false : "Unhandled update to property:" + propertyKey;
+        }
+    }
+
+    /**
+     * Factory used to create a new header inside the ListView inside the TouchToFillCreditCardView.
+     * @param parent The parent {@link ViewGroup} of the new item.
+     */
+    static View createHeaderItemView(ViewGroup parent) {
+        return LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.touch_to_fill_credit_card_header_item, parent, false);
+    }
+
+    /**
+     * Called whenever a property in the given model changes. It updates the given view accordingly.
+     * @param model The observed {@link PropertyModel}. Its data need to be reflected in the view.
+     * @param view The {@link View} of the header to update.
+     * @param key The {@link PropertyKey} which changed.
+     */
+    static void bindHeaderView(PropertyModel model, View view, PropertyKey propertyKey) {
+        if (propertyKey == IMAGE_DRAWABLE_ID) {
+            ImageView sheetHeaderImage = view.findViewById(R.id.branding_icon);
+            sheetHeaderImage.setImageDrawable(AppCompatResources.getDrawable(
+                    view.getContext(), model.get(IMAGE_DRAWABLE_ID)));
+        } else {
+            assert false : "Unhandled update to property:" + propertyKey;
+        }
     }
 }
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index fcc8bac..97d6b46 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -1273,18 +1273,27 @@
       <message name="IDS_AD_PRIVACY_PAGE_TOPICS_LINK_ROW_SUB_LABEL_ENABLED" translateable="false" desc="Description for topics preference when enabled.">
         Enabled Nulla eros tortor, placerat blandit dictum a, interdum id metus
       </message>
+      <message name="IDS_AD_PRIVACY_PAGE_TOPICS_LINK_ROW_SUB_LABEL_DISABLED" translateable="false" desc="Description for topics preference when enabled.">
+        Disabled Nulla eros tortor, placerat blandit dictum a, interdum id metus
+      </message>
       <message name="IDS_AD_PRIVACY_PAGE_FLEDGE_LINK_ROW_LABEL" translateable="false" desc="Title for the fledge preference.">
         Aenean erat leo
       </message>
       <message name="IDS_AD_PRIVACY_PAGE_FLEDGE_LINK_ROW_SUB_LABEL_ENABLED" translateable="false" desc="Description for the fledge preference when enabled.">
         Enabled Duis scelerisque a mi eget ultricies
       </message>
+      <message name="IDS_AD_PRIVACY_PAGE_FLEDGE_LINK_ROW_SUB_LABEL_DISABLED" translateable="false" desc="Description for the fledge preference when enabled.">
+        Disabled Duis scelerisque a mi eget ultricies
+      </message>
       <message name="IDS_AD_PRIVACY_PAGE_AD_MEASUREMENT_LINK_ROW_LABEL" translateable="false" desc="Title for the ad measurement preference.">
         Vestibulum augue erat
       </message>
       <message name="IDS_AD_PRIVACY_PAGE_AD_MEASUREMENT_LINK_ROW_SUB_LABEL_ENABLED" translateable="false" desc="Description for the ad measurement preference when enabled.">
         Enabled Vivamus id lacus et lacus porttitor vulputate. Sed semper egestas orci vel maximus.
       </message>
+      <message name="IDS_AD_PRIVACY_PAGE_AD_MEASUREMENT_LINK_ROW_SUB_LABEL_DISABLED" translateable="false" desc="Description for the ad measurement preference when enabled.">
+        Disabled Vivamus id lacus et lacus porttitor vulputate. Sed semper egestas orci vel maximus.
+      </message>
 
       <!-- Privacy Sandbox v4 - Topics Page -->
       <message name="IDS_SETTINGS_TOPICS_PAGE_TITLE" translateable="false" desc="Title for the Topics preferences page.">
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc
index fd9bdb8..165d017f 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.cc
@@ -5,8 +5,6 @@
 #include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h"
 
 #include "ash/public/cpp/notification_utils.h"
-#include "base/check.h"
-#include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -19,12 +17,12 @@
 namespace ash::cloud_upload {
 namespace {
 
-// The minimum amount of time for which the "in progress" state should be
+// Minimum amount of time, in seconds, for which the notification should be
 // displayed.
-const base::TimeDelta kMinInProgressTime = base::Seconds(5);
+const base::TimeDelta kMinNotificationTime = base::Seconds(5);
 
-// Time for which the "Complete" notification should display.
-const base::TimeDelta kCompleteNotificationTime = base::Seconds(5);
+// Time, in seconds, for which the "Complete" notification should display.
+const base::TimeDelta kCompleteNotificationTimeout = base::Seconds(2);
 
 // If no other class instance holds a reference to the notification manager, the
 // notification manager goes out of scope.
@@ -139,19 +137,19 @@
                                            *notification,
                                            /*metadata=*/nullptr);
 
-  // Make sure we display the "in progress" state for a minimum amount of time.
-  if (state_ == State::kUninitialized) {
-    state_ = State::kInProgress;
-    in_progress_timer_.Start(
-        FROM_HERE, kMinInProgressTime,
+  // Start the "min time" notification timer when the first progress
+  // notification is shown.
+  if (!first_notification_shown) {
+    first_notification_shown = true;
+    notification_timer_.Start(
+        FROM_HERE, kMinNotificationTime,
         base::BindOnce(
-            &CloudUploadNotificationManager::OnMinInProgressTimeReached,
+            &CloudUploadNotificationManager::OnMinNotificationTimeReached,
             weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
-void CloudUploadNotificationManager::ShowCompleteNotification() {
-  DCHECK_EQ(state_, State::kComplete);
+void CloudUploadNotificationManager::ShowUploadComplete() {
   std::unique_ptr<message_center::Notification> notification =
       CreateUploadCompleteNotification();
   notification->set_never_timeout(true);
@@ -159,28 +157,26 @@
                                            *notification,
                                            /*metadata=*/nullptr);
 
+  // If the complete notification is shown before any progress notifications,
+  // start the `kMinNotificationTime` timer.
+  if (!first_notification_shown) {
+    first_notification_shown = true;
+    notification_timer_.Start(
+        FROM_HERE, kMinNotificationTime,
+        base::BindOnce(
+            &CloudUploadNotificationManager::OnMinNotificationTimeReached,
+            weak_ptr_factory_.GetWeakPtr()));
+  }
+
   // Start the timer to automatically dismiss the "Complete" notification.
   complete_notification_timer_.Start(
-      FROM_HERE, kCompleteNotificationTime,
-      base::BindOnce(&CloudUploadNotificationManager::CloseNotification,
-                     weak_ptr_factory_.GetWeakPtr()));
+      FROM_HERE, kCompleteNotificationTimeout,
+      base::BindOnce(
+          &CloudUploadNotificationManager::OnCompleteNotificationTimeout,
+          weak_ptr_factory_.GetWeakPtr()));
 }
 
-void CloudUploadNotificationManager::MarkUploadComplete() {
-  // Check if the "in progress" timeout has happened yet or not.
-  if (state_ == State::kInProgress) {
-    state_ = State::kWaitingForInProgressTimeout;
-  } else if (state_ == State::kUninitialized ||
-             state_ == State::kInProgressTimedOut) {
-    // If the complete notification is shown before any progress notifications,
-    // we don't run the kMinInProgressTime timeout.
-    state_ = State::kComplete;
-    ShowCompleteNotification();
-  }
-}
-
-void CloudUploadNotificationManager::ShowUploadError(
-    const std::string& message) {
+void CloudUploadNotificationManager::ShowUploadError(std::string message) {
   std::unique_ptr<message_center::Notification> notification =
       CreateUploadErrorNotification(message);
   notification->set_never_timeout(true);
@@ -189,19 +185,28 @@
                                            /*metadata=*/nullptr);
 }
 
-void CloudUploadNotificationManager::OnMinInProgressTimeReached() {
-  if (state_ == State::kInProgress) {
-    state_ = State::kInProgressTimedOut;
-  } else if (state_ == State::kWaitingForInProgressTimeout) {
-    state_ = State::kComplete;
-    ShowCompleteNotification();
+void CloudUploadNotificationManager::OnMinNotificationTimeReached() {
+  // Close the notification only if the "Complete notification" has timed out.
+  // Error notifications can only be dismissed by users.
+  if (completed_) {
+    CloseNotification();
+  }
+}
+
+void CloudUploadNotificationManager::OnCompleteNotificationTimeout() {
+  completed_ = true;
+
+  // If `kMinNotificationTime` hasn't been reached yet, do not close the
+  // notification.
+  if (!notification_timer_.IsRunning()) {
+    CloseNotification();
   }
 }
 
 void CloudUploadNotificationManager::CloseNotification() {
   GetNotificationDisplayService()->Close(NotificationHandler::Type::TRANSIENT,
                                          notification_id_);
-  in_progress_timer_.Stop();
+  notification_timer_.Stop();
   complete_notification_timer_.Stop();
   if (callback_) {
     std::move(callback_).Run();
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h
index d162523..8c4ab919 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h
+++ b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h
@@ -5,6 +5,7 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_NOTIFICATION_MANAGER_H_
 #define CHROME_BROWSER_UI_WEBUI_ASH_CLOUD_UPLOAD_CLOUD_UPLOAD_NOTIFICATION_MANAGER_H_
 
+#include "base/files/file_path.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
@@ -16,10 +17,7 @@
 
 namespace ash::cloud_upload {
 
-// Creates, updates and deletes cloud upload system notifications. Ensures that
-// notifications stay in the "in progress" state for a minimum of 5 seconds, and
-// a minimum of 5 seconds for the 'complete' state. For the error state,
-// notifications stay open until the user closes them.
+// Manages creation/deletion and update of cloud upload system notifications.
 class CloudUploadNotificationManager
     : public base::RefCounted<CloudUploadNotificationManager> {
  public:
@@ -28,20 +26,14 @@
                                  const std::string& cloud_provider_name,
                                  const std::string& target_app_name);
 
-  // Creates the notification with "in progress" state if it doesn't exist, or
-  // updates the progress bar if it does. |progress| is within the 0-100 range.
-  // The notification will stay in the "in progress" state for a minimum of 5
-  // seconds, even at 100% progress.
+  // Shows the upload progress notification. |progress| within the 0-100 range.
   void ShowUploadProgress(int progress);
 
-  // Shows the upload complete notification for 5 seconds, but only once the
-  // minimum 5 seconds from the "in progress" state has finished.
-  void MarkUploadComplete();
+  // Shows the upload complete notification for 2s if the upload was successful.
+  void ShowUploadComplete();
 
-  // Shows the error state for the notification indefinitely, until closed by
-  // the user. Does not wait for the progress notification to show for a minimum
-  // time.
-  void ShowUploadError(const std::string& message);
+  // Shows the upload error notification.
+  void ShowUploadError(std::string message);
 
  private:
   friend base::RefCounted<CloudUploadNotificationManager>;
@@ -62,29 +54,17 @@
   std::unique_ptr<message_center::Notification> CreateUploadErrorNotification(
       std::string message);
 
-  // Called when the minimum amount of time to display the "in progress"
-  // notification is reached.
-  void OnMinInProgressTimeReached();
+  // Called when the minimum amount of time to display the notification is
+  // reached.
+  void OnMinNotificationTimeReached();
 
-  // Updates the notification immediately to show the complete state.
-  void ShowCompleteNotification();
+  // Called when the "Complete" notification times out.
+  void OnCompleteNotificationTimeout();
 
   // Called when the upload flow is complete: Ensures that notifications are
   // closed, timers are interrupted and the completion callback has been called.
   void CloseNotification();
 
-  // A state machine and the possible transitions. The state of showing the
-  // error notification is not explicit because it is never used to determine
-  // later logic.
-  enum class State {
-    kUninitialized,  // --> kInProgress, kComplete
-    kInProgress,     // --> kInProgressTimedOut, kWaitingForInProgressTimeout,
-                     // (error)
-    kInProgressTimedOut,           // --> kComplete, (error)
-    kWaitingForInProgressTimeout,  // --> kComplete
-    kComplete
-  };
-
   // Counts the total number of notification manager instances. This counter is
   // never decremented.
   static inline int notification_manager_counter_ = 0;
@@ -96,9 +76,10 @@
   std::string notification_id_;
   std::string target_app_name_;
   base::OnceClosure callback_;
-  base::OneShotTimer in_progress_timer_;
+  base::OneShotTimer notification_timer_;
   base::OneShotTimer complete_notification_timer_;
-  State state_ = State::kUninitialized;
+  bool first_notification_shown = false;
+  bool completed_ = false;
   base::WeakPtrFactory<CloudUploadNotificationManager> weak_ptr_factory_{this};
 };
 
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager_unittest.cc b/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager_unittest.cc
deleted file mode 100644
index 06b7ef2e..0000000
--- a/chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager_unittest.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_notification_manager.h"
-
-#include "base/time/time.h"
-#include "chrome/browser/notifications/notification_display_service_factory.h"
-#include "chrome/browser/notifications/stub_notification_display_service.h"
-#include "chrome/test/base/testing_profile.h"
-#include "content/public/test/browser_task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace ash::cloud_upload {
-
-class CloudUploadNotificationManagerTest : public testing::Test {
- public:
-  CloudUploadNotificationManagerTest() = default;
-
-  CloudUploadNotificationManagerTest(
-      const CloudUploadNotificationManagerTest&) = delete;
-  CloudUploadNotificationManagerTest& operator=(
-      const CloudUploadNotificationManagerTest&) = delete;
-
-  // testing::Test:
-  void SetUp() override {
-    profile_ = std::make_unique<TestingProfile>();
-
-    display_service_ = static_cast<StubNotificationDisplayService*>(
-        NotificationDisplayServiceFactory::GetInstance()
-            ->SetTestingFactoryAndUse(
-                profile_.get(),
-                base::BindRepeating(
-                    &StubNotificationDisplayService::FactoryForTests)));
-  }
-
- protected:
-  Profile* profile() { return profile_.get(); }
-
-  absl::optional<message_center::Notification> notification() {
-    auto notifications = display_service_->GetDisplayedNotificationsForType(
-        NotificationHandler::Type::TRANSIENT);
-    if (notifications.size()) {
-      return notifications[0];
-    }
-    return absl::nullopt;
-  }
-
-  bool HaveProgressNotification() {
-    return notification().has_value() &&
-           notification()->type() ==
-               message_center::NotificationType::NOTIFICATION_TYPE_PROGRESS;
-  }
-
-  bool HaveCompleteNotification() {
-    return notification().has_value() &&
-           notification()->type() ==
-               message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE &&
-           notification()->title().starts_with(u"Move completed");
-  }
-
-  bool HaveErrorNotification() {
-    return notification().has_value() &&
-           notification()->type() ==
-               message_center::NotificationType::NOTIFICATION_TYPE_SIMPLE &&
-           notification()->title().starts_with(u"Failed");
-  }
-
-  content::BrowserTaskEnvironment task_environment_{
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-  std::unique_ptr<TestingProfile> profile_;
-  StubNotificationDisplayService* display_service_;
-};
-
-TEST_F(CloudUploadNotificationManagerTest,
-       ShowUploadProgressCreatesNotification) {
-  scoped_refptr<CloudUploadNotificationManager> manager =
-      base::MakeRefCounted<CloudUploadNotificationManager>(
-          profile(), "foo.docx", "Google Drive", "Google Docs");
-
-  ASSERT_EQ(absl::nullopt, notification());
-  manager->ShowUploadProgress(1);
-  ASSERT_TRUE(HaveProgressNotification());
-}
-
-TEST_F(CloudUploadNotificationManagerTest, MinimumTiming) {
-  scoped_refptr<CloudUploadNotificationManager> manager =
-      base::MakeRefCounted<CloudUploadNotificationManager>(
-          profile(), "foo.docx", "Google Drive", "Google Docs");
-
-  manager->ShowUploadProgress(1);
-  manager->ShowUploadProgress(100);
-  manager->MarkUploadComplete();
-  ASSERT_TRUE(HaveProgressNotification());
-
-  // The progress notification should still be showing.
-  task_environment_.FastForwardBy(base::Milliseconds(4900));
-  ASSERT_TRUE(HaveProgressNotification());
-
-  // Now we see the Complete nofication after 5s.
-  task_environment_.FastForwardBy(base::Milliseconds(500));
-  ASSERT_TRUE(HaveCompleteNotification());
-
-  // Now we're at 9900 ms total - we still expect the Complete notification.
-  task_environment_.FastForwardBy(base::Milliseconds(4500));
-  ASSERT_TRUE(HaveCompleteNotification());
-
-  // After > 10s total, the notification should be closed.
-  task_environment_.FastForwardBy(base::Milliseconds(500));
-  ASSERT_EQ(absl::nullopt, notification());
-}
-
-TEST_F(CloudUploadNotificationManagerTest, CompleteWithoutProgress) {
-  scoped_refptr<CloudUploadNotificationManager> manager =
-      base::MakeRefCounted<CloudUploadNotificationManager>(
-          profile(), "foo.docx", "Google Drive", "Google Docs");
-
-  manager->MarkUploadComplete();
-  ASSERT_TRUE(HaveCompleteNotification());
-
-  // The complete notification should still be showing.
-  task_environment_.FastForwardBy(base::Milliseconds(4900));
-  ASSERT_TRUE(HaveCompleteNotification());
-
-  // After > 5s total, the notification should be closed.
-  task_environment_.FastForwardBy(base::Milliseconds(500));
-  ASSERT_EQ(absl::nullopt, notification());
-}
-
-TEST_F(CloudUploadNotificationManagerTest, ErrorStaysOpen) {
-  scoped_refptr<CloudUploadNotificationManager> manager =
-      base::MakeRefCounted<CloudUploadNotificationManager>(
-          profile(), "foo.docx", "Google Drive", "Google Docs");
-
-  manager->ShowUploadProgress(1);
-  manager->ShowUploadProgress(100);
-  manager->ShowUploadError("error");
-  // The error is shown straight away.
-  ASSERT_TRUE(HaveErrorNotification());
-
-  // The error notification should still be showing.
-  task_environment_.FastForwardBy(base::Seconds(60));
-  ASSERT_TRUE(HaveErrorNotification());
-}
-
-TEST_F(CloudUploadNotificationManagerTest, ManagerLifetime) {
-  {
-    scoped_refptr<CloudUploadNotificationManager> manager =
-        base::MakeRefCounted<CloudUploadNotificationManager>(
-            profile(), "foo.docx", "Google Drive", "Google Docs");
-
-    manager->ShowUploadProgress(1);
-    manager->ShowUploadError("error");
-    ASSERT_TRUE(HaveErrorNotification());
-  }
-  // We still have a ref to manager until the notification is dismissed.
-  ASSERT_TRUE(HaveErrorNotification());
-
-  notification()->delegate()->Click(absl::nullopt, absl::nullopt);
-  ASSERT_EQ(absl::nullopt, notification());
-}
-
-}  // namespace ash::cloud_upload
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
index dccbf26..1957cec 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/drive_upload_handler.cc
@@ -164,7 +164,7 @@
   // Resolve notifications.
   if (notification_manager_) {
     if (hosted_url.is_valid()) {
-      notification_manager_->MarkUploadComplete();
+      notification_manager_->ShowUploadComplete();
     } else if (!error_message.empty()) {
       LOG(ERROR) << "Cloud upload: " << error_message;
       notification_manager_->ShowUploadError(error_message);
diff --git a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
index b27e1a9d..24ca79b 100644
--- a/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
+++ b/chrome/browser/ui/webui/ash/cloud_upload/one_drive_upload_handler.cc
@@ -131,7 +131,7 @@
   // Resolve notifications.
   if (notification_manager_) {
     if (uploaded_file_url.is_valid()) {
-      notification_manager_->MarkUploadComplete();
+      notification_manager_->ShowUploadComplete();
     } else if (!error_message.empty()) {
       LOG(ERROR) << "Upload to OneDrive: " << error_message;
       notification_manager_->ShowUploadError(error_message);
diff --git a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
index 0978a501..de2cb71 100644
--- a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
+++ b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
@@ -73,6 +73,7 @@
       {"deletePassword", IDS_DELETE},
       {"editPassword", IDS_EDIT},
       {"emptyNote", IDS_PASSWORD_MANAGER_UI_NO_NOTE_SAVED},
+      {"exportingPasswordsTitle", IDS_PASSWORD_MANAGER_UI_EXPORTING_TITLE},
       {"exportPasswords", IDS_PASSWORD_MANAGER_UI_EXPORT_TITLE},
       {"exportPasswordsDescription",
        IDS_PASSWORD_MANAGER_UI_EXPORT_BANNER_DESCRIPTION},
diff --git a/chrome/browser/ui/webui/settings/ash/crostini_handler.cc b/chrome/browser/ui/webui/settings/ash/crostini_handler.cc
index 40f69ab2..d0a18cd 100644
--- a/chrome/browser/ui/webui/settings/ash/crostini_handler.cc
+++ b/chrome/browser/ui/webui/settings/ash/crostini_handler.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ash/crostini/crostini_installer.h"
 #include "chrome/browser/ash/crostini/crostini_port_forwarder.h"
 #include "chrome/browser/ash/crostini/crostini_pref_names.h"
+#include "chrome/browser/ash/crostini/crostini_shared_devices.h"
 #include "chrome/browser/ash/crostini/crostini_types.mojom.h"
 #include "chrome/browser/ash/crostini/crostini_util.h"
 #include "chrome/browser/ash/file_manager/path_util.h"
@@ -29,6 +30,8 @@
 #include "components/prefs/pref_service.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/user_manager/user_manager.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/web_ui.h"
 #include "ui/display/screen.h"
@@ -179,6 +182,18 @@
         "openContainerFileSelector",
         base::BindRepeating(&CrostiniHandler::HandleOpenContainerFileSelector,
                             handler_weak_ptr_factory_.GetWeakPtr()));
+    web_ui()->RegisterMessageCallback(
+        "requestSharedVmDevices",
+        base::BindRepeating(&CrostiniHandler::HandleRequestSharedVmDevices,
+                            handler_weak_ptr_factory_.GetWeakPtr()));
+    web_ui()->RegisterMessageCallback(
+        "isVmDeviceShared",
+        base::BindRepeating(&CrostiniHandler::HandleIsVmDeviceShared,
+                            handler_weak_ptr_factory_.GetWeakPtr()));
+    web_ui()->RegisterMessageCallback(
+        "setVmDeviceShared",
+        base::BindRepeating(&CrostiniHandler::HandleSetVmDeviceShared,
+                            handler_weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
@@ -661,14 +676,26 @@
 
 void CrostiniHandler::OnContainerStarted(
     const guest_os::GuestId& container_id) {
-  FireWebUIListener("crostini-status-changed", base::Value(true));
-  HandleRequestContainerInfo(base::Value::List());
+  if (container_id == crostini::DefaultContainerId()) {
+    FireWebUIListener("crostini-status-changed", base::Value(true));
+  }
+  // After other observers have run, we can send container info.
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&CrostiniHandler::HandleRequestContainerInfo,
+                                handler_weak_ptr_factory_.GetWeakPtr(),
+                                base::Value::List()));
 }
 
 void CrostiniHandler::OnContainerShutdown(
     const guest_os::GuestId& container_id) {
-  FireWebUIListener("crostini-status-changed", base::Value(false));
-  HandleRequestContainerInfo(base::Value::List());
+  if (container_id == crostini::DefaultContainerId()) {
+    FireWebUIListener("crostini-status-changed", base::Value(false));
+  }
+  // After other observers have run, we can send container info.
+  content::GetUIThreadTaskRunner({})->PostTask(
+      FROM_HERE, base::BindOnce(&CrostiniHandler::HandleRequestContainerInfo,
+                                handler_weak_ptr_factory_.GetWeakPtr(),
+                                base::Value::List()));
 }
 
 void CrostiniHandler::HandleShutdownCrostini(const base::Value::List& args) {
@@ -850,7 +877,7 @@
   file_selector_ = std::make_unique<crostini::CrostiniFileSelector>(web_ui());
   file_selector_->SelectFile(
       base::BindOnce(&CrostiniHandler::OnContainerFileSelected,
-                     handler_weak_ptr_factory_.GetWeakPtr(), callback_id),
+                     callback_weak_ptr_factory_.GetWeakPtr(), callback_id),
       base::DoNothing());
 }
 
@@ -860,4 +887,64 @@
   ResolveJavascriptCallback(base::Value(callback_id), filePath);
 }
 
+void CrostiniHandler::HandleRequestSharedVmDevices(
+    const base::Value::List& args) {
+  constexpr char kIdKey[] = "id";
+  constexpr char kVmDevicesKey[] = "vmDevices";
+  constexpr char kMicrophone[] = "microphone";
+
+  auto* crostini_shared_devices =
+      crostini::CrostiniSharedDevices::GetForProfile(profile_);
+
+  base::Value::List shared_vmdevices;
+  for (const auto& container_id :
+       guest_os::GetContainers(profile_, guest_os::VmType::TERMINA)) {
+    base::Value::Dict container_shared_devices;
+    container_shared_devices.Set(kIdKey, container_id.ToDictValue());
+
+    base::Value::Dict device_dict;
+    device_dict.Set(kMicrophone, crostini_shared_devices->IsVmDeviceShared(
+                                     container_id, kMicrophone));
+
+    container_shared_devices.Set(kVmDevicesKey, std::move(device_dict));
+
+    shared_vmdevices.Append(std::move(container_shared_devices));
+  }
+
+  FireWebUIListener("crostini-shared-vmdevices", shared_vmdevices);
+}
+
+void CrostiniHandler::HandleIsVmDeviceShared(const base::Value::List& args) {
+  CHECK_EQ(3U, args.size());
+
+  const std::string& callback_id = args[0].GetString();
+  guest_os::GuestId container_id(args[1]);
+  const std::string& vm_device = args[2].GetString();
+
+  ResolveJavascriptCallback(
+      base::Value(callback_id),
+      crostini::CrostiniSharedDevices::GetForProfile(profile_)
+          ->IsVmDeviceShared(container_id, vm_device));
+}
+
+void CrostiniHandler::HandleSetVmDeviceShared(const base::Value::List& args) {
+  CHECK_EQ(4U, args.size());
+  const std::string& callback_id = args[0].GetString();
+  guest_os::GuestId container_id(args[1]);
+  const std::string& vm_device = args[2].GetString();
+  bool shared = args[3].GetBool();
+
+  crostini::CrostiniSharedDevices::GetForProfile(profile_)->SetVmDeviceShared(
+      container_id, vm_device, shared,
+      base::BindOnce(
+          [](base::WeakPtr<CrostiniHandler> weak_this,
+             const std::string callback_id, bool was_applied) {
+            if (weak_this) {
+              weak_this->ResolveJavascriptCallback(base::Value(callback_id),
+                                                   was_applied);
+            }
+          },
+          callback_weak_ptr_factory_.GetWeakPtr(), callback_id));
+}
+
 }  // namespace ash::settings
diff --git a/chrome/browser/ui/webui/settings/ash/crostini_handler.h b/chrome/browser/ui/webui/settings/ash/crostini_handler.h
index 14854df..0a374c5 100644
--- a/chrome/browser/ui/webui/settings/ash/crostini_handler.h
+++ b/chrome/browser/ui/webui/settings/ash/crostini_handler.h
@@ -154,6 +154,13 @@
   void OnContainerFileSelected(const std::string& callback_id,
                                const base::FilePath& path);
 
+  // Handle a request for the shared vmdevice info of all known containers
+  void HandleRequestSharedVmDevices(const base::Value::List& args);
+  // Handle a request to query the sharing status of a VmDevice
+  void HandleIsVmDeviceShared(const base::Value::List& args);
+  // Handle a request to set the sharing status of a VmDevice
+  void HandleSetVmDeviceShared(const base::Value::List& args);
+
   Profile* profile_;
   base::CallbackListSubscription adb_sideloading_device_policy_subscription_;
   PrefChangeRegistrar pref_change_registrar_;
diff --git a/chrome/browser/ui/webui/settings/ash/crostini_section.cc b/chrome/browser/ui/webui/settings/ash/crostini_section.cc
index cf0364d..5ce2b97 100644
--- a/chrome/browser/ui/webui/settings/ash/crostini_section.cc
+++ b/chrome/browser/ui/webui/settings/ash/crostini_section.cc
@@ -388,6 +388,10 @@
        IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CONTAINER_NAME_LABEL},
       {"crostiniExtraContainersContainerIpLabel",
        IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CONTAINER_IP_LABEL},
+      {"crostiniExtraContainersShareMicrophone",
+       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_SHARE_MICROPHONE},
+      {"crostiniExtraContainersAppBadgeColor",
+       IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_APP_BADGE_COLOR},
       {"crostiniExtraContainersCreateDialogTitle",
        IDS_SETTINGS_CROSTINI_EXTRA_CONTAINERS_CREATE_DIALOG_TITLE},
       {"crostiniExtraContainersCreateDialogContainerExistsError",
diff --git a/chrome/browser/ui/webui/settings/ash/os_settings_recovery_browsertest.cc b/chrome/browser/ui/webui/settings/ash/os_settings_recovery_browsertest.cc
index b39306f..06c0e955 100644
--- a/chrome/browser/ui/webui/settings/ash/os_settings_recovery_browsertest.cc
+++ b/chrome/browser/ui/webui/settings/ash/os_settings_recovery_browsertest.cc
@@ -156,4 +156,24 @@
   EXPECT_FALSE(cryptohome_.HasRecoveryFactor(GetAccountId()));
 }
 
+// Check that trying to change recovery with an invalidated auth session shows
+// the password prompt again.
+IN_PROC_BROWSER_TEST_F(OSSettingsRecoveryTestWithFeature, DestroyedSession) {
+  mojom::LockScreenSettingsAsyncWaiter lock_screen_settings =
+      OpenLockScreenSettings();
+
+  // Try to change recovery setting, but with an invalid auth session. This
+  // should throw us back to the password prompt.
+  cryptohome_.DestroySessions();
+  lock_screen_settings.TryEnableRecoveryConfiguration();
+  lock_screen_settings.AssertAuthenticated(false);
+
+  // Check that it's still possible to authenticate and change recovery
+  // settings.
+  EXPECT_FALSE(cryptohome_.HasRecoveryFactor(GetAccountId()));
+  lock_screen_settings.Authenticate(kPassword);
+  lock_screen_settings.EnableRecoveryConfiguration();
+  EXPECT_TRUE(cryptohome_.HasRecoveryFactor(GetAccountId()));
+}
+
 }  // namespace ash::settings
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
index 043354f..9811b9d3 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -1991,6 +1991,14 @@
       {"topicsPageTitle", IDS_SETTINGS_TOPICS_PAGE_TITLE},
       {"topicsPageToggleLabel", IDS_SETTINGS_TOPICS_PAGE_TOGGLE_LABEL},
       {"topicsPageToggleSubLabel", IDS_SETTINGS_TOPICS_PAGE_TOGGLE_SUB_LABEL},
+      {"topicsPageCurrentTopicsHeading",
+       IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_HEADING},
+      {"topicsPageCurrentTopicsDescription",
+       IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION},
+      {"topicsPageCurrentTopicsDescriptionDisabled",
+       IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION_DISABLED},
+      {"topicsPageCurrentTopicsDescriptionEmpty",
+       IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION_EMPTY},
       {"fledgePageTitle", IDS_SETTINGS_FLEDGE_PAGE_TITLE},
       {"adMeasurementPageTitle", IDS_SETTINGS_AD_MEASUREMENT_PAGE_TITLE},
       {"adMeasurementPageToggleLabel",
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 8319194b..e330323 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -72,6 +72,7 @@
 #include "components/account_manager_core/account_manager_facade.h"
 #include "components/commerce/core/commerce_feature_list.h"
 #include "components/commerce/core/shopping_service.h"
+#include "components/content_settings/core/common/features.h"
 #include "components/favicon_base/favicon_url_parser.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/performance_manager/public/features.h"
@@ -438,9 +439,10 @@
   html_source->AddBoolean("safetyCheckNotificationPermissionsEnabled",
                           base::FeatureList::IsEnabled(
                               features::kSafetyCheckNotificationPermissions));
-  html_source->AddBoolean("safetyCheckUnusedSitePermissionsEnabled",
-                          base::FeatureList::IsEnabled(
-                              features::kSafetyCheckUnusedSitePermissions));
+  html_source->AddBoolean(
+      "safetyCheckUnusedSitePermissionsEnabled",
+      base::FeatureList::IsEnabled(
+          content_settings::features::kSafetyCheckUnusedSitePermissions));
 
   // Performance
   AddSettingsPageUIHandler(std::make_unique<PerformanceHandler>());
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
index ef6789f..1733c816 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
@@ -37,14 +37,11 @@
       ntp_background_service_(
           NtpBackgroundServiceFactory::GetForProfile(profile_)),
       theme_service_(ThemeServiceFactory::GetForProfile(profile_)),
-      chrome_colors_service_(
-          chrome_colors::ChromeColorsFactory::GetForProfile(profile_)),
       page_(std::move(pending_page)),
       receiver_(this, std::move(pending_page_handler)) {
   CHECK(ntp_custom_background_service_);
   CHECK(theme_service_);
   CHECK(ntp_background_service_);
-  CHECK(chrome_colors_service_);
   ntp_background_service_->AddObserver(this);
   native_theme_observation_.Observe(ui::NativeTheme::GetInstanceForNativeUi());
   theme_service_observation_.Observe(theme_service_);
@@ -126,20 +123,11 @@
 }
 
 void CustomizeChromePageHandler::SetDefaultColor() {
-  auto* chrome_colors_service =
-      chrome_colors::ChromeColorsFactory::GetForProfile(profile_);
-  chrome_colors_service->ApplyDefaultTheme(web_contents_);
-  // Side panel settings are final.
-  chrome_colors_service->ConfirmThemeChanges();
+  theme_service_->UseDefaultTheme();
 }
 
 void CustomizeChromePageHandler::SetForegroundColor(SkColor foreground_color) {
-  auto* chrome_colors_service =
-      chrome_colors::ChromeColorsFactory::GetForProfile(profile_);
-  chrome_colors_service->ApplyAutogeneratedTheme(foreground_color,
-                                                 web_contents_);
-  // Side panel settings are final.
-  chrome_colors_service->ConfirmThemeChanges();
+  theme_service_->BuildAutogeneratedThemeFromColor(foreground_color);
 }
 
 void CustomizeChromePageHandler::SetClassicChromeDefaultTheme() {
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
index 9598531d..1a5b1d78 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
@@ -19,10 +19,6 @@
 #include "ui/native_theme/native_theme.h"
 #include "ui/native_theme/native_theme_observer.h"
 
-namespace chrome_colors {
-class ChromeColorsService;
-}  // namespace chrome_colors
-
 namespace content {
 class WebContents;
 }  // namespace content
@@ -82,7 +78,6 @@
   GetBackgroundCollectionsCallback background_collections_callback_;
   base::TimeTicks background_collections_request_start_time_;
   raw_ptr<ThemeService> theme_service_;
-  raw_ptr<chrome_colors::ChromeColorsService> chrome_colors_service_;
 
   base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
       native_theme_observation_{this};
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
index 50cf382..7a2b33e 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
@@ -90,20 +90,12 @@
   MOCK_CONST_METHOD0(UsingDefaultTheme, bool());
   MOCK_CONST_METHOD0(UsingSystemTheme, bool());
   MOCK_METHOD0(UseDefaultTheme, void());
+  MOCK_METHOD1(BuildAutogeneratedThemeFromColor, void(SkColor));
 
  private:
   ThemeHelper theme_helper_;
 };
 
-class MockChromeColorsService : public chrome_colors::ChromeColorsService {
- public:
-  explicit MockChromeColorsService(Profile* profile)
-      : chrome_colors::ChromeColorsService(profile) {}
-  MOCK_METHOD1(ApplyDefaultTheme, void(content::WebContents*));
-  MOCK_METHOD2(ApplyAutogeneratedTheme, void(SkColor, content::WebContents*));
-  MOCK_METHOD0(ConfirmThemeChanges, void());
-};
-
 std::unique_ptr<TestingProfile> MakeTestingProfile(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
   TestingProfile::Builder profile_builder;
@@ -124,13 +116,6 @@
                               -> std::unique_ptr<KeyedService> {
         return std::make_unique<testing::NiceMock<MockThemeService>>();
       }));
-  profile_builder.AddTestingFactory(
-      chrome_colors::ChromeColorsFactory::GetInstance(),
-      base::BindRepeating([](content::BrowserContext* context)
-                              -> std::unique_ptr<KeyedService> {
-        return std::make_unique<testing::NiceMock<MockChromeColorsService>>(
-            Profile::FromBrowserContext(context));
-      }));
   profile_builder.SetSharedURLLoaderFactory(url_loader_factory);
   auto profile = profile_builder.Build();
   return profile;
@@ -149,10 +134,7 @@
             NtpBackgroundServiceFactory::GetForProfile(profile_.get()))),
         web_contents_(web_contents_factory_.CreateWebContents(profile_.get())),
         mock_theme_service_(static_cast<MockThemeService*>(
-            ThemeServiceFactory::GetForProfile(profile_.get()))),
-        mock_chrome_colors_service_(static_cast<MockChromeColorsService*>(
-            chrome_colors::ChromeColorsFactory::GetForProfile(
-                profile_.get()))) {}
+            ThemeServiceFactory::GetForProfile(profile_.get()))) {}
 
   void SetUp() override {
     EXPECT_CALL(mock_ntp_background_service(), AddObserver)
@@ -176,9 +158,6 @@
     return *ntp_background_service_observer_;
   }
   MockThemeService& mock_theme_service() { return *mock_theme_service_; }
-  MockChromeColorsService& mock_chrome_colors_service() {
-    return *mock_chrome_colors_service_;
-  }
 
  protected:
   // NOTE: The initialization order of these members matters.
@@ -193,7 +172,6 @@
   testing::NiceMock<MockPage> mock_page_;
   NtpBackgroundServiceObserver* ntp_background_service_observer_;
   MockThemeService* mock_theme_service_;
-  MockChromeColorsService* mock_chrome_colors_service_;
   std::unique_ptr<CustomizeChromePageHandler> handler_;
 };
 
@@ -354,18 +332,16 @@
 }
 
 TEST_F(CustomizeChromePageHandlerTest, SetDefaultColor) {
-  EXPECT_CALL(mock_chrome_colors_service(), ApplyDefaultTheme).Times(1);
-  EXPECT_CALL(mock_chrome_colors_service(), ConfirmThemeChanges).Times(1);
+  EXPECT_CALL(mock_theme_service(), UseDefaultTheme).Times(1);
 
   handler().SetDefaultColor();
 }
 
 TEST_F(CustomizeChromePageHandlerTest, SetForegroundColor) {
   SkColor color = SK_ColorWHITE;
-  EXPECT_CALL(mock_chrome_colors_service(), ApplyAutogeneratedTheme)
+  EXPECT_CALL(mock_theme_service(), BuildAutogeneratedThemeFromColor)
       .Times(1)
       .WillOnce(testing::SaveArg<0>(&color));
-  EXPECT_CALL(mock_chrome_colors_service(), ConfirmThemeChanges).Times(1);
 
   handler().SetForegroundColor(SK_ColorBLUE);
 
diff --git a/chrome/browser/web_applications/web_app_icon_manager.cc b/chrome/browser/web_applications/web_app_icon_manager.cc
index 50d2afa..264e3840 100644
--- a/chrome/browser/web_applications/web_app_icon_manager.cc
+++ b/chrome/browser/web_applications/web_app_icon_manager.cc
@@ -4,42 +4,63 @@
 
 #include "chrome/browser/web_applications/web_app_icon_manager.h"
 
-#include <string>
+#include <array>
+#include <cstdint>
+#include <functional>
+#include <initializer_list>
+#include <ostream>
 #include <utility>
 
-#include "base/bind.h"
-#include "base/callback.h"
+#include "base/check.h"
+#include "base/check_op.h"
 #include "base/containers/adapters.h"
+#include "base/containers/flat_tree.h"
 #include "base/feature_list.h"
+#include "base/files/file.h"
 #include "base/files/scoped_temp_dir.h"
+#include "base/functional/bind.h"
+#include "base/functional/identity.h"
+#include "base/hash/hash.h"
+#include "base/location.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
-#include "base/memory/scoped_refptr.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece_forward.h"
 #include "base/strings/stringprintf.h"
+#include "base/task/sequenced_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
+#include "build/build_config.h"
 #include "chrome/browser/web_applications/file_utils_wrapper.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_icon_generator.h"
-#include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chrome/browser/web_applications/web_app_registrar.h"
 #include "chrome/browser/web_applications/web_app_utils.h"
 #include "chrome/common/chrome_features.h"
 #include "content/public/browser/browser_thread.h"
 #include "skia/ext/image_operations.h"
-#include "third_party/abseil-cpp/absl/types/variant.h"
+#include "third_party/blink/public/mojom/manifest/manifest.mojom-shared.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkColorType.h"
 #include "ui/base/layout.h"
+#include "ui/base/resource/resource_scale_factor.h"
 #include "ui/gfx/codec/png_codec.h"
 #include "ui/gfx/favicon_size.h"
-#include "ui/gfx/image/image_skia_rep.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/image/image_skia_rep_default.h"
+#include "url/gurl.h"
 
 namespace web_app {
 
 namespace {
 
+using ReadCompressedIconCallback =
+    base::OnceCallback<void(std::vector<uint8_t> data)>;
+
 // This utility struct is to carry error logs between threads via return values.
 // If we weren't generating multithreaded errors we would just append the errors
 // to WebAppIconManager::error_log() directly.
@@ -436,13 +457,6 @@
   return result;
 }
 
-void WrapReadCompressedIconWithPurposeCallback(
-    WebAppIconManager::ReadCompressedIconWithPurposeCallback callback,
-    IconPurpose purpose,
-    std::vector<uint8_t> data) {
-  std::move(callback).Run(purpose, std::move(data));
-}
-
 gfx::ImageSkia ConvertUiScaleFactorsBitmapsToImageSkia(
     const std::map<SquareSizePx, SkBitmap>& icon_bitmaps,
     SquareSizeDip size_in_dip) {
@@ -482,13 +496,6 @@
   std::move(callback).Run(std::move(bitmap));
 }
 
-void WrapReadCompressedIconCallback(
-    WebAppIconManager::ReadCompressedIconCallback callback,
-    IconPurpose ignored,
-    std::vector<uint8_t> data) {
-  std::move(callback).Run(std::move(data));
-}
-
 // A utility that manages writing icons to disk for a single app. Should only be
 // used on an I/O thread.
 class WriteIconsJob {
@@ -978,8 +985,7 @@
   DCHECK(best_icon.has_value());
   IconId icon_id(app_id, best_icon->purpose, best_icon->size_px);
   ReadCompressedIconCallback wrapped =
-      base::BindOnce(WrapReadCompressedIconWithPurposeCallback,
-                     std::move(callback), best_icon->purpose);
+      base::BindOnce(std::move(callback), best_icon->purpose);
 
   icon_task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE,
@@ -998,16 +1004,6 @@
                    std::move(wrapped));
 }
 
-void WebAppIconManager::ReadSmallestCompressedIconAny(
-    const AppId& app_id,
-    SquareSizePx min_icon_size,
-    ReadCompressedIconCallback callback) {
-  ReadCompressedIconWithPurposeCallback wrapped =
-      base::BindOnce(WrapReadCompressedIconCallback, std::move(callback));
-  ReadSmallestCompressedIcon(app_id, {IconPurpose::ANY}, min_icon_size,
-                             std::move(wrapped));
-}
-
 SkBitmap WebAppIconManager::GetFavicon(const AppId& app_id) const {
   auto iter = favicon_cache_.find(app_id);
   if (iter == favicon_cache_.end())
diff --git a/chrome/browser/web_applications/web_app_icon_manager.h b/chrome/browser/web_applications/web_app_icon_manager.h
index 639d7bd..1019115 100644
--- a/chrome/browser/web_applications/web_app_icon_manager.h
+++ b/chrome/browser/web_applications/web_app_icon_manager.h
@@ -5,31 +5,39 @@
 #ifndef CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_ICON_MANAGER_H_
 #define CHROME_BROWSER_WEB_APPLICATIONS_WEB_APP_ICON_MANAGER_H_
 
+#include <stddef.h>
+#include <stdint.h>
 #include <map>
 #include <memory>
+#include <string>
 #include <vector>
 
-#include "base/callback.h"
+#include "base/containers/flat_map.h"
 #include "base/files/file_path.h"
+#include "base/functional/callback.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
-#include "base/task/sequenced_task_runner.h"
-#include "base/time/time.h"
-#include "chrome/browser/web_applications/app_registrar_observer.h"
+#include "chrome/browser/web_applications/web_app_id.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
 #include "chrome/browser/web_applications/web_app_install_manager.h"
 #include "chrome/browser/web_applications/web_app_install_manager_observer.h"
-#include "chrome/browser/web_applications/web_app_registrar.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/image/image_skia.h"
 
 class Profile;
 
+namespace base {
+class SequencedTaskRunner;
+class Time;
+}  // namespace base
+
 namespace web_app {
 
 class FileUtilsWrapper;
+class WebAppRegistrar;
 
 using SquareSizeDip = int;
 
@@ -142,14 +150,6 @@
                            SquareSizePx min_icon_size,
                            ReadIconCallback callback);
 
-  using ReadCompressedIconCallback =
-      base::OnceCallback<void(std::vector<uint8_t> data)>;
-  // Convenience method for |ReadSmallestCompressedIcon| with IconPurpose::ANY
-  // only.
-  void ReadSmallestCompressedIconAny(const AppId& app_id,
-                                     SquareSizePx min_icon_size,
-                                     ReadCompressedIconCallback callback);
-
   // Returns a square icon of gfx::kFaviconSize px, or an empty bitmap if not
   // found.
   SkBitmap GetFavicon(const AppId& app_id) const;
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index ead0e0f..16f09f4 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1670889566-1fec7648febd165c2cc50850d889d9e2c11c2e87.profdata
+chrome-linux-main-1670911113-e1aa66f34a84d00f6e1a8efd50e96dcf2551b3cf.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 50a6a4be..cda0d4f 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1670867915-f9acec514e2db21e8cb162d5f7eff165da735b7b.profdata
+chrome-mac-arm-main-1670911113-5a8126d13e16d816344b0b6f9f75fa9dfab0386e.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index cef09c3..25da7b3d 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1670889566-42ea1512f5afbd78bcbcc3aa2936c8335d141ebf.profdata
+chrome-mac-main-1670932793-9ed8f62fbaebb0a80dec16554a5b5fe50857243f.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 47f9412..b9fcfb86 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1670878747-2b16e54bce8674b59280de7680dd81eeafe1f39a.profdata
+chrome-win32-main-1670911113-3705680ffb97b516000d2aaf1c32d8521b347e7f.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 7598acd..45210cdb 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1670889566-c90f50899c0d4f6ffe2d2ba211447e26103d0a1d.profdata
+chrome-win64-main-1670921383-cc708cef991f2e311fc9a845be6cf88ae485cde4.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 24d4f8d..eb2762e4 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -992,11 +992,6 @@
     kSafetyCheckNotificationPermissionsLowEnagementLimit{
         &kSafetyCheckNotificationPermissions,
         "low-engagement-notification-count", 4};
-
-// Enables unused site permission module in Safety Check.
-BASE_FEATURE(kSafetyCheckUnusedSitePermissions,
-             "SafetyCheckUnusedSitePermissions",
-             base::FEATURE_DISABLED_BY_DEFAULT);
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 3fd65d4..a01e8dd 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -566,9 +566,6 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::FeatureParam<int>
     kSafetyCheckNotificationPermissionsLowEnagementLimit;
-
-COMPONENT_EXPORT(CHROME_FEATURES)
-BASE_DECLARE_FEATURE(kSafetyCheckUnusedSitePermissions);
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/common/chromeos/extensions/api/diagnostics.idl b/chrome/common/chromeos/extensions/api/diagnostics.idl
index f78b88c3..d10f1be 100644
--- a/chrome/common/chromeos/extensions/api/diagnostics.idl
+++ b/chrome/common/chromeos/extensions/api/diagnostics.idl
@@ -30,7 +30,8 @@
     sensitive_sensor,
     nvme_self_test,
     fingerprint_alive,
-    smartctl_check_with_percentage_used
+    smartctl_check_with_percentage_used,
+    emmc_lifetime
   };
 
   enum RoutineStatus {
@@ -186,6 +187,8 @@
 
     [supportsPromises] static void runDnsResolverPresentRoutine(RunRoutineCallback callback);
 
+    [supportsPromises] static void runEmmcLifetimeRoutine(RunRoutineCallback callback);
+
     [supportsPromises] static void runFingerprintAliveRoutine(RunRoutineCallback callback);
 
     [supportsPromises] static void runGatewayCanBePingedRoutine(RunRoutineCallback callback);
diff --git a/chrome/install_static/user_data_dir_win_unittest.cc b/chrome/install_static/user_data_dir_win_unittest.cc
index cf2a33ee..262d938 100644
--- a/chrome/install_static/user_data_dir_win_unittest.cc
+++ b/chrome/install_static/user_data_dir_win_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/test/test_reg_util_win.h"
 #include "build/branding_buildflags.h"
+#include "chrome/browser/chrome_for_testing/buildflags.h"
 #include "chrome/chrome_elf/nt_registry/nt_registry.h"
 #include "chrome/install_static/install_details.h"
 #include "chrome/install_static/user_data_dir.h"
@@ -23,6 +24,11 @@
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
 const wchar_t kPolicyRegistryKey[] = L"SOFTWARE\\Policies\\Google\\Chrome";
 const wchar_t kUserDataDirNameSuffix[] = L"\\Google\\Chrome\\User Data";
+#elif BUILDFLAG(GOOGLE_CHROME_FOR_TESTING_BRANDING)
+const wchar_t kPolicyRegistryKey[] =
+    L"SOFTWARE\\Policies\\Google\\Chrome for Testing";
+const wchar_t kUserDataDirNameSuffix[] =
+    L"\\Google\\Chrome for Testing\\User Data";
 #else
 const wchar_t kPolicyRegistryKey[] = L"SOFTWARE\\Policies\\Chromium";
 const wchar_t kUserDataDirNameSuffix[] = L"\\Chromium\\User Data";
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 2f1a1e2..e6a2bd6 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1969,7 +1969,6 @@
       "../browser/optimization_guide/page_content_annotations_service_browsertest.cc",
       "../browser/optimization_guide/page_text_observer_browsertest.cc",
       "../browser/optimization_guide/prediction/prediction_manager_browsertest.cc",
-      "../browser/optimization_guide/prediction/prediction_model_store_browsertest.cc",
       "../browser/origin_trials/origin_trials_browsertest.cc",
       "../browser/page_load_metrics/observers/ad_metrics/ad_density_intervention_browsertest.cc",
       "../browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc",
@@ -3599,6 +3598,7 @@
         "../browser/supervised_user/permission_request_creator_mock.cc",
         "../browser/supervised_user/permission_request_creator_mock.h",
         "../browser/supervised_user/supervised_user_navigation_throttle_browsertest.cc",
+        "../browser/supervised_user/supervised_user_regional_url_filter_browsertest.cc",
         "../browser/supervised_user/supervised_user_service_browsertest.cc",
         "../browser/supervised_user/supervised_user_url_filter_browsertest.cc",
       ]
@@ -3611,6 +3611,7 @@
       }
       deps += [
         "//chrome/browser/supervised_user:test_support",
+        "//chrome/browser/supervised_user/kids_chrome_management:proto",
         "//chrome/browser/supervised_user/supervised_user_features",
       ]
     }
diff --git a/chrome/test/data/webui/password_manager/passwords_export_dialog_test.ts b/chrome/test/data/webui/password_manager/passwords_export_dialog_test.ts
index 2684366..da21609 100644
--- a/chrome/test/data/webui/password_manager/passwords_export_dialog_test.ts
+++ b/chrome/test/data/webui/password_manager/passwords_export_dialog_test.ts
@@ -10,6 +10,7 @@
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {CrDialogElement, PasswordsExportDialogElement, PasswordManagerImpl} from 'chrome://password-manager/password_manager.js';
 import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {MockTimer} from 'chrome://webui-test/mock_timer.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestPasswordManagerProxy} from './test_password_manager_proxy.js';
@@ -26,6 +27,43 @@
   return dialog;
 }
 
+function assertDialogIsShown(
+    exportDialogElement: PasswordsExportDialogElement, dialogId: string) {
+  const dialog =
+      exportDialogElement.shadowRoot!.querySelector<CrDialogElement>(dialogId);
+  assertTrue(!!dialog);
+  assertTrue(dialog.open);
+}
+
+function clickExportPasswordsButton(
+    exportDialog: PasswordsExportDialogElement) {
+  assertDialogIsShown(exportDialog, '#dialogStart');
+  const exportButton = exportDialog.shadowRoot!.querySelector<HTMLElement>(
+      '#exportPasswordsButton');
+  assertTrue(!!exportButton);
+  exportButton.click();
+  flush();
+  assertDialogIsShown(exportDialog, '#dialogStart');
+}
+
+function checkThatNoDialogIsShown(exportDialog: PasswordsExportDialogElement) {
+  assertFalse(!!exportDialog.shadowRoot!.querySelector<CrDialogElement>(
+      '#dialogStart'));
+  assertFalse(!!exportDialog.shadowRoot!.querySelector<CrDialogElement>(
+      '#dialogProgress'));
+  // TODO(crbug.com/1394416) Check that the error dialog is also not shown when
+  // it's implemented.
+}
+
+function updateExportStatus(
+    passwordManager: TestPasswordManagerProxy,
+    status: chrome.passwordsPrivate.ExportProgressStatus) {
+  const progressCallback =
+      passwordManager.listeners.passwordsFileExportProgressListener;
+  assertTrue(!!progressCallback);
+  progressCallback({status});
+}
+
 suite('PasswordsExportDialog', function() {
   let passwordManager: TestPasswordManagerProxy;
 
@@ -40,10 +78,7 @@
   test('exportDismissable', async function() {
     const exportDialog = createExportPasswordsDialog();
     await passwordManager.whenCalled('requestExportProgressStatus');
-    const dialogStart =
-        exportDialog.shadowRoot!.querySelector<CrDialogElement>('#dialogStart');
-    assertTrue(!!dialogStart);
-    assertTrue(dialogStart.open);
+    assertDialogIsShown(exportDialog, '#dialogStart');
 
     const cancelButton =
         exportDialog.shadowRoot!.querySelector<HTMLElement>('#cancelButton');
@@ -68,17 +103,111 @@
   // fires close event on completion.
   test('Export starts and finishes', async function() {
     const exportDialog = createExportPasswordsDialog();
-    const exportButton = exportDialog.shadowRoot!.querySelector<HTMLElement>(
-        '#exportPasswordsButton');
-    assertTrue(!!exportButton);
-    exportButton.click();
-    await passwordManager.whenCalled('exportPasswords');
+    clickExportPasswordsButton(exportDialog);
 
-    const progressCallback =
-        passwordManager.listeners.passwordsFileExportProgressListener;
-    assertTrue(!!progressCallback);
-    progressCallback(
-        {status: chrome.passwordsPrivate.ExportProgressStatus.SUCCEEDED});
+    updateExportStatus(
+        passwordManager,
+        chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS);
+    updateExportStatus(
+        passwordManager,
+        chrome.passwordsPrivate.ExportProgressStatus.SUCCEEDED);
     await eventToPromise('passwords-export-dialog-close', exportDialog);
   });
+
+  // Test the export flow. If exporting is fast, we should skip the
+  // in-progress view altogether.
+  test('exportFlowFast', function() {
+    const exportDialog = createExportPasswordsDialog();
+
+    // Use this to freeze the delayed progress bar and avoid flakiness.
+    const mockTimer = new MockTimer();
+    mockTimer.install();
+
+    clickExportPasswordsButton(exportDialog);
+    updateExportStatus(
+        passwordManager,
+        chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS);
+    updateExportStatus(
+        passwordManager,
+        chrome.passwordsPrivate.ExportProgressStatus.SUCCEEDED);
+
+    flush();
+    // When we are done, the export dialog closes completely.
+    checkThatNoDialogIsShown(exportDialog);
+  });
+
+  // Test the export flow. If exporting is slow, Chrome should show the
+  // in-progress dialog for at least 1000ms.
+  test('exportFlowSlow', function() {
+    const exportDialog = createExportPasswordsDialog();
+
+    const mockTimer = new MockTimer();
+    mockTimer.install();
+
+    // The initial dialog remains open for 100ms after export enters the
+    // in-progress state.
+    clickExportPasswordsButton(exportDialog);
+    updateExportStatus(
+        passwordManager,
+        chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS);
+    flush();
+    assertDialogIsShown(exportDialog, '#dialogStart');
+
+    // After 100ms of not having completed, the dialog switches to the
+    // progress bar. Chrome will continue to show the progress bar for 1000ms,
+    // despite a completion event.
+    mockTimer.tick(99);
+    flush();
+    assertDialogIsShown(exportDialog, '#dialogStart');
+
+    mockTimer.tick(1);
+    flush();
+    assertDialogIsShown(exportDialog, '#dialogProgress');
+    updateExportStatus(
+        passwordManager,
+        chrome.passwordsPrivate.ExportProgressStatus.SUCCEEDED);
+    flush();
+    assertDialogIsShown(exportDialog, '#dialogProgress');
+
+    // Until 1000ms expire, progress dialog is shown, even though
+    // export has already finished.
+    mockTimer.tick(999);
+    flush();
+    assertDialogIsShown(exportDialog, '#dialogProgress');
+    mockTimer.tick(1);
+    flush();
+    // On SUCCEEDED the dialog closes completely.
+    checkThatNoDialogIsShown(exportDialog);
+  });
+
+  // Test that canceling the dialog while exporting will also cancel the
+  // export on the browser.
+  test('cancelExport', async function() {
+    const exportDialog = createExportPasswordsDialog();
+
+    const mockTimer = new MockTimer();
+    mockTimer.install();
+
+    // The initial dialog remains open for 100ms after export enters the
+    // in-progress state.
+    clickExportPasswordsButton(exportDialog);
+    updateExportStatus(
+        passwordManager,
+        chrome.passwordsPrivate.ExportProgressStatus.IN_PROGRESS);
+    // The progress bar only appears after 100ms.
+    mockTimer.tick(100);
+    flush();
+    assertDialogIsShown(exportDialog, '#dialogProgress');
+
+    const cancelProgressButton =
+        exportDialog.shadowRoot!.querySelector<HTMLElement>(
+            '#cancelProgressButton');
+    assertTrue(!!cancelProgressButton);
+    cancelProgressButton.click();
+    await passwordManager.whenCalled('cancelExportPasswords');
+
+    flush();
+    // The dialog should be dismissed entirely.
+    checkThatNoDialogIsShown(exportDialog);
+  });
 });
\ No newline at end of file
diff --git a/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts b/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts
index 82235478..981fd3b 100644
--- a/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts
+++ b/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts
@@ -36,6 +36,7 @@
 
   constructor() {
     super([
+      'cancelExportPasswords',
       'exportPasswords',
       'getBlockedSitesList',
       'getCredentialGroups',
@@ -190,4 +191,8 @@
       _listener: PasswordsFileExportProgressListener) {
     this.listeners.passwordsFileExportProgressListener = null;
   }
+
+  cancelExportPasswords() {
+    this.methodCalled('cancelExportPasswords');
+  }
 }
diff --git a/chrome/test/data/webui/settings/chromeos/crostini_extra_containers_subpage_test.js b/chrome/test/data/webui/settings/chromeos/crostini_extra_containers_subpage_test.js
index 588369f..3f40291 100644
--- a/chrome/test/data/webui/settings/chromeos/crostini_extra_containers_subpage_test.js
+++ b/chrome/test/data/webui/settings/chromeos/crostini_extra_containers_subpage_test.js
@@ -20,7 +20,7 @@
   /** @type {?TestCrostiniBrowserProxy} */
   let crostiniBrowserProxy = null;
 
-  /** @type {?SettingsCrostiniSubPageElement} */
+  /** @type {?SettingsCrostinuExtraContainersElement} */
   let subpage;
 
   setup(async function() {
@@ -43,13 +43,33 @@
       },
     ];
 
+    const sharedVmDevices_ = [
+      {
+        id: allContainers_[0].id,
+        vmDevices: {microphone: true},
+      },
+      {
+        id: allContainers_[1].id,
+        vmDevices: {microphone: false},
+      },
+      {
+        id: allContainers_[2].id,
+        vmDevices: {microphone: true},
+      },
+    ];
+
     crostiniBrowserProxy.containerInfo = allContainers_;
+    crostiniBrowserProxy.sharedVmDevices = sharedVmDevices_;
     crostiniPage.prefs = {
       crostini: {
         enabled: {value: true},
       },
     };
     flush();
+    assertEquals(0, crostiniBrowserProxy.getCallCount('requestContainerInfo'));
+    assertEquals(
+        0, crostiniBrowserProxy.getCallCount('requestSharedVmDevices'));
+
     Router.getInstance().navigateTo(
         routes.CROSTINI_EXTRA_CONTAINERS);
 
@@ -57,6 +77,9 @@
     subpage = crostiniPage.shadowRoot.querySelector(
         'settings-crostini-extra-containers');
     assertTrue(!!subpage);
+    assertEquals(1, crostiniBrowserProxy.getCallCount('requestContainerInfo'));
+    assertEquals(
+        1, crostiniBrowserProxy.getCallCount('requestSharedVmDevices'));
   });
 
   teardown(function() {
@@ -317,4 +340,61 @@
                           .disabled);
         });
   });
+
+  suite('ContainerDetails', function() {
+    test('ExpandButton', async function() {
+      const expandButton =
+          subpage.shadowRoot.querySelector('#expand-button-termina-penguin');
+      assertTrue(!!expandButton);
+
+      // The collapse element should open/close on clicking |expandButton|.
+      const collapse =
+          subpage.shadowRoot.querySelector('#collapse-termina-penguin');
+      assertTrue(!!collapse);
+
+      assertFalse(collapse.opened);
+      expandButton.click();
+      await flushTasks();
+      assertTrue(collapse.opened);
+
+      expandButton.click();
+      await flushTasks();
+      assertFalse(collapse.opened);
+    });
+
+    test('ToggleMicrophoneOff', async function() {
+      // The toggle is inside an iron-collapse, but we can still click it
+      // via the testing apis.
+      const toggle =
+          subpage.shadowRoot.querySelector('#microphone-termina-penguin');
+
+      assertTrue(!!toggle);
+      assertTrue(toggle.checked);
+
+      toggle.click();
+      await crostiniBrowserProxy.resolvePromises('setVmDeviceShared', true);
+      await crostiniBrowserProxy.resolvePromises('isVmDeviceShared', false);
+
+      assertFalse(toggle.checked);
+
+      assertEquals(1, crostiniBrowserProxy.getCallCount('setVmDeviceShared'));
+      const args1 = crostiniBrowserProxy.getArgs('setVmDeviceShared')[0];
+      assertArrayEquals(
+          [
+            {vm_name: 'termina', container_name: 'penguin'},
+            'microphone',
+            false,
+          ],
+          args1);
+
+      assertEquals(1, crostiniBrowserProxy.getCallCount('isVmDeviceShared'));
+      const args2 = crostiniBrowserProxy.getArgs('isVmDeviceShared')[0];
+      assertArrayEquals(
+          [
+            {vm_name: 'termina', container_name: 'penguin'},
+            'microphone',
+          ],
+          args2);
+    });
+  });
 });
diff --git a/chrome/test/data/webui/settings/chromeos/test_api.test-mojom b/chrome/test/data/webui/settings/chromeos/test_api.test-mojom
index a18ca32..ef78355 100644
--- a/chrome/test/data/webui/settings/chromeos/test_api.test-mojom
+++ b/chrome/test/data/webui/settings/chromeos/test_api.test-mojom
@@ -39,6 +39,10 @@
   // the recovery is still disabled.
   EnableRecoveryConfiguration() => ();
 
+  // Try to toggle whether recovery is configured or not. Does not assume that
+  // the action is successful.
+  TryEnableRecoveryConfiguration() => ();
+
   // The following function expects the cryptohome recovery toggle is on.
   // Clicks on the recovery toggle, expecting the recovery dialog to show up.
   // if the param is CancelDialog:
diff --git a/chrome/test/data/webui/settings/chromeos/test_api.ts b/chrome/test/data/webui/settings/chromeos/test_api.ts
index 25df8803..dd5b0c9 100644
--- a/chrome/test/data/webui/settings/chromeos/test_api.ts
+++ b/chrome/test/data/webui/settings/chromeos/test_api.ts
@@ -249,6 +249,12 @@
     await assertAsync(() => toggle.checked);
   }
 
+  async tryEnableRecoveryConfiguration(): Promise<void> {
+    const toggle = await retryUntilSome(() => this.recoveryToggle());
+    assertTrue(!toggle.checked);
+    toggle.click();
+  }
+
   async disableRecoveryConfiguration(dialogAction: RecoveryDialogAction):
       Promise<void> {
     assertTrue(this.recoveryDisableDialog() === null);
diff --git a/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js b/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
index 811b465..0813392 100644
--- a/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
+++ b/chrome/test/data/webui/settings/chromeos/test_crostini_browser_proxy.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import {webUIListenerCallback} from 'chrome://resources/js/cr.m.js';
-
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
 /** @implements {CrostiniBrowserProxy} */
@@ -38,12 +37,18 @@
       'setContainerBadgeColor',
       'stopContainer',
       'requestCrostiniExportImportOperationStatus',
+      'openContainerFileSelector',
+      'requestSharedVmDevices',
+      'isVmDeviceShared',
+      'setVmDeviceShared',
     ]);
     this.crostiniMicSharingEnabled = false;
     this.crostiniIsRunning = true;
     this.methodCalls_ = {};
     this.portOperationSuccess = true;
     this.containerInfo = [];
+    this.selectedContainerFileName = '';
+    this.sharedVmDevices = [];
   }
 
   getNewPromiseFor(name) {
@@ -233,4 +238,28 @@
   stopContainer(containerId) {
     this.methodCalled('stopContainer');
   }
+
+  /** @override */
+  openContainerFileSelector() {
+    this.methodCalled('openContainerFileSelector');
+    return Promise.resolve(this.selectedContainerFileName);
+  }
+
+  /** @override */
+  requestSharedVmDevices() {
+    this.methodCalled('requestSharedVmDevices');
+    webUIListenerCallback('crostini-shared-vmdevices', this.sharedVmDevices);
+  }
+
+  /** @override */
+  isVmDeviceShared(id, device) {
+    this.methodCalled('isVmDeviceShared', id, device);
+    return this.getNewPromiseFor('isVmDeviceShared');
+  }
+
+  /** @override */
+  setVmDeviceShared(id, device, shared) {
+    this.methodCalled('setVmDeviceShared', id, device, shared);
+    return this.getNewPromiseFor('setVmDeviceShared');
+  }
 }
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 8e051636..37a487a 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -12,6 +12,7 @@
 GEN('#include "build/chromeos_buildflags.h"');
 GEN('#include "chrome/browser/ui/ui_features.h"');
 GEN('#include "chrome/common/chrome_features.h"');
+GEN('#include "components/content_settings/core/common/features.h"');
 GEN('#include "components/performance_manager/public/features.h"');
 GEN('#include "components/privacy_sandbox/privacy_sandbox_features.h"');
 GEN('#include "components/autofill/core/common/autofill_features.h"');
@@ -341,7 +342,7 @@
   get featureListInternal() {
     return {
       enabled: [
-        'features::kSafetyCheckUnusedSitePermissions',
+        'content_settings::features::kSafetyCheckUnusedSitePermissions',
         'features::kSafetyCheckNotificationPermissions',
       ],
     };
@@ -830,7 +831,7 @@
   get featureList() {
     return {
       enabled: [
-        'features::kSafetyCheckUnusedSitePermissions',
+        'content_settings::features::kSafetyCheckUnusedSitePermissions',
       ],
     };
   }
diff --git a/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts b/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
index b0a7dbb..6e68ed2 100644
--- a/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
+++ b/chrome/test/data/webui/settings/privacy_sandbox_page_test.ts
@@ -9,7 +9,7 @@
 import {CrSettingsPrefs, Router, routes, SettingsPrefsElement} from 'chrome://settings/settings.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {isVisible} from 'chrome://webui-test/test_util.js';
+import {isChildVisible, isVisible} from 'chrome://webui-test/test_util.js';
 
 suite('PrivacySandboxPageTests', function() {
   let page: SettingsPrivacySandboxPageElement;
@@ -145,6 +145,9 @@
     assertEquals(
         loadTimeData.getString('topicsPageToggleSubLabel'),
         page.$.topicsToggle.subLabel);
+    assertFalse(isChildVisible(page, '#currentTopicsDescription'));
+    assertFalse(isChildVisible(page, '#currentTopicsDescriptionEmpty'));
+    assertTrue(isChildVisible(page, '#currentTopicsDescriptionDisabled'));
 
     page.$.topicsToggle.click();
     await flushTasks();
@@ -154,6 +157,12 @@
         loadTimeData.getString('topicsPageToggleSubLabel'),
         page.$.topicsToggle.subLabel);
     assertTrue(!!page.getPref('privacy_sandbox.m1.topics_enabled.value'));
+    // TODO(crbug.com/1378703): Test for `#currentTopicsDescription` and
+    // `#currentTopicsDescriptionEmpty` separately.
+    assertTrue(
+        isChildVisible(page, '#currentTopicsDescription') ||
+        isChildVisible(page, '#currentTopicsDescriptionEmpty'));
+    assertFalse(isChildVisible(page, '#currentTopicsDescriptionDisabled'));
   });
 
   test('disableTopicsToggle', async function() {
@@ -164,6 +173,12 @@
     assertEquals(
         loadTimeData.getString('topicsPageToggleSubLabel'),
         page.$.topicsToggle.subLabel);
+    // TODO(crbug.com/1378703): Test for `#currentTopicsDescription` and
+    // `#currentTopicsDescriptionEmpty` separately.
+    assertTrue(
+        isChildVisible(page, '#currentTopicsDescription') ||
+        isChildVisible(page, '#currentTopicsDescriptionEmpty'));
+    assertFalse(isChildVisible(page, '#currentTopicsDescriptionDisabled'));
 
     page.$.topicsToggle.click();
     await flushTasks();
@@ -173,6 +188,9 @@
         loadTimeData.getString('topicsPageToggleSubLabel'),
         page.$.topicsToggle.subLabel);
     assertFalse(!!page.getPref('privacy_sandbox.m1.topics_enabled.value'));
+    assertFalse(isChildVisible(page, '#currentTopicsDescription'));
+    assertFalse(isChildVisible(page, '#currentTopicsDescriptionEmpty'));
+    assertTrue(isChildVisible(page, '#currentTopicsDescriptionDisabled'));
   });
 });
 
diff --git a/chrome/test/data/webui/settings/test_site_settings_permissions_browser_proxy.ts b/chrome/test/data/webui/settings/test_site_settings_permissions_browser_proxy.ts
index 72f92c4..ef17a80 100644
--- a/chrome/test/data/webui/settings/test_site_settings_permissions_browser_proxy.ts
+++ b/chrome/test/data/webui/settings/test_site_settings_permissions_browser_proxy.ts
@@ -18,10 +18,17 @@
 
   constructor() {
     super([
+      'allowPermissionsAgainForUnusedSite',
       'getRevokedUnusedSitePermissionsList',
     ]);
   }
 
+  allowPermissionsAgainForUnusedSite(unusedSitePermissions:
+                                         UnusedSitePermissions) {
+    this.methodCalled(
+        'allowPermissionsAgainForUnusedSite', [unusedSitePermissions]);
+  }
+
   setUnusedSitePermissions(unusedSitePermissionsList: UnusedSitePermissions[]) {
     this.unusedSitePermissions_ = unusedSitePermissionsList;
   }
diff --git a/chrome/test/data/webui/settings/unused_site_permissions_test.ts b/chrome/test/data/webui/settings/unused_site_permissions_test.ts
index d8030a50..a7ac5e9 100644
--- a/chrome/test/data/webui/settings/unused_site_permissions_test.ts
+++ b/chrome/test/data/webui/settings/unused_site_permissions_test.ts
@@ -4,7 +4,7 @@
 
 // clang-format off
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {ContentSettingsTypes, SettingsUnusedSitePermissionsElement, SiteSettingsPermissionsBrowserProxyImpl} from 'chrome://settings/lazy_load.js';
 
 import {TestSiteSettingsPermissionsBrowserProxy} from './test_site_settings_permissions_browser_proxy.js';
@@ -28,6 +28,25 @@
                                       permissions: permissions.slice(0, i),
                                     }));
 
+  /* Asserts for each row whether or not it is animating. */
+  function assertAnimation(expectedAnimation: boolean[]) {
+    const rows = getSiteList();
+
+    assertEquals(
+        rows.length, expectedAnimation.length,
+        'Provided ' + expectedAnimation.length +
+            ' expectations but there are ' + rows.length + ' rows');
+    for (const [i, row] of rows.entries()) {
+      assertEquals(
+          expectedAnimation[i]!, row!.classList.contains('removed'),
+          'Expectation not met for row #' + i);
+    }
+  }
+
+  function getSiteList() {
+    return testElement.shadowRoot!.querySelectorAll('.site-list .site-entry');
+  }
+
   setup(async function() {
     browserProxy = new TestSiteSettingsPermissionsBrowserProxy();
     browserProxy.setUnusedSitePermissions(mockData);
@@ -35,44 +54,48 @@
 
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
     testElement = document.createElement('settings-unused-site-permissions');
+    testElement.setModelUpdateDelayMsForTesting(0);
     document.body.appendChild(testElement);
     await browserProxy.whenCalled('getRevokedUnusedSitePermissionsList');
     flush();
   });
 
   test('Unused Site Permission strings', function() {
-    const entries =
-        testElement.shadowRoot!.querySelectorAll('.site-list .site-entry');
-    assertEquals(4, entries.length);
+    const siteList = getSiteList();
+    assertEquals(4, siteList.length);
 
     // Check that the text describing the permissions is correct.
     assertEquals(
         mockData[0]!.origin,
-        entries[0]!.querySelector('.site-representation')!.textContent!.trim());
+        siteList[0]!.querySelector(
+                        '.site-representation')!.textContent!.trim());
     assertEquals(
         'Removed location',
-        entries[0]!.querySelector('.secondary')!.textContent!.trim());
+        siteList[0]!.querySelector('.secondary')!.textContent!.trim());
 
     assertEquals(
         mockData[1]!.origin,
-        entries[1]!.querySelector('.site-representation')!.textContent!.trim());
+        siteList[1]!.querySelector(
+                        '.site-representation')!.textContent!.trim());
     assertEquals(
         'Removed location, microphone',
-        entries[1]!.querySelector('.secondary')!.textContent!.trim());
+        siteList[1]!.querySelector('.secondary')!.textContent!.trim());
 
     assertEquals(
         mockData[2]!.origin,
-        entries[2]!.querySelector('.site-representation')!.textContent!.trim());
+        siteList[2]!.querySelector(
+                        '.site-representation')!.textContent!.trim());
     assertEquals(
         'Removed location, microphone, camera',
-        entries[2]!.querySelector('.secondary')!.textContent!.trim());
+        siteList[2]!.querySelector('.secondary')!.textContent!.trim());
 
     assertEquals(
         mockData[3]!.origin,
-        entries[3]!.querySelector('.site-representation')!.textContent!.trim());
+        siteList[3]!.querySelector(
+                        '.site-representation')!.textContent!.trim());
     assertEquals(
         'Removed location, microphone, and 2 more',
-        entries[3]!.querySelector('.secondary')!.textContent!.trim());
+        siteList[3]!.querySelector('.secondary')!.textContent!.trim());
   });
 
   test('Collapsible List', function() {
@@ -104,4 +127,24 @@
     assertTrue(expandButton.expanded);
     assertTrue(unusedSitePermissionList.opened);
   });
+
+  test('Allow Again Click', async function() {
+    const siteList = getSiteList();
+
+    assertEquals(siteList.length, 4);
+    assertAnimation([false, false, false, false]);
+
+    const element = siteList[0]!.querySelector('cr-icon-button')!;
+    element.click();
+
+    assertAnimation([true, false, false, false]);
+    // Ensure the browser proxy call is done.
+    const expectedOrigin =
+        siteList[0]!.querySelector('.site-representation')!.textContent!.trim();
+    const [unusedSitePermissions] =
+        await browserProxy.whenCalled('allowPermissionsAgainForUnusedSite');
+    assertEquals(unusedSitePermissions.origin, expectedOrigin);
+    assertDeepEquals(
+        unusedSitePermissions.permissions, mockData[0]!.permissions);
+  });
 });
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
index f750581..b70fbecd 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/categories_test.ts
@@ -87,4 +87,14 @@
     const event = await eventPromise;
     assertTrue(!!event);
   });
+
+  test('clicking classic chrome sets theme and sends event', async () => {
+    await setInitialSettings(0);
+
+    const eventPromise = eventToPromise('theme-select', categoriesElement);
+    categoriesElement.$.classicChromeTile.click();
+    const event = await eventPromise;
+    assertTrue(!!event);
+    assertEquals(1, handler.getCallCount('setClassicChromeDefaultTheme'));
+  });
 });
diff --git a/chromecast/media/audio/cast_audio_mixer_unittest.cc b/chromecast/media/audio/cast_audio_mixer_unittest.cc
index e25aed0a0..51668d9a7 100644
--- a/chromecast/media/audio/cast_audio_mixer_unittest.cc
+++ b/chromecast/media/audio/cast_audio_mixer_unittest.cc
@@ -22,6 +22,7 @@
 #include "chromecast/media/audio/mock_cast_audio_manager_helper_delegate.h"
 #include "media/audio/audio_io.h"
 #include "media/audio/test_audio_thread.h"
+#include "media/base/audio_glitch_info.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromecast/media/audio/cast_audio_output_device.cc b/chromecast/media/audio/cast_audio_output_device.cc
index d7ee795..8013b5a 100644
--- a/chromecast/media/audio/cast_audio_output_device.cc
+++ b/chromecast/media/audio/cast_audio_output_device.cc
@@ -20,6 +20,7 @@
 #include "chromecast/media/audio/audio_output_service/output_stream_connection.h"
 #include "chromecast/media/base/default_monotonic_clock.h"
 #include "media/base/audio_bus.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/bind_to_current_loop.h"
@@ -364,7 +365,7 @@
     return 0;
   }
   return active_render_callback_->Render(delay, base::TimeTicks(),
-                                         /*frames_skipped=*/0, audio_bus);
+                                         /*glitch_info=*/{}, audio_bus);
 }
 
 }  // namespace media
diff --git a/chromeos/ash/components/network/network_state.cc b/chromeos/ash/components/network/network_state.cc
index a02d0f31..b69f724 100644
--- a/chromeos/ash/components/network/network_state.cc
+++ b/chromeos/ash/components/network/network_state.cc
@@ -41,6 +41,22 @@
   return stringp ? *stringp : std::string();
 }
 
+bool IsValidConnectionState(const std::string& connection_state) {
+  return connection_state == shill::kStateIdle ||
+         connection_state == shill::kStateAssociation ||
+         connection_state == shill::kStateConfiguration ||
+         connection_state == shill::kStateReady ||
+         connection_state == shill::kStateNoConnectivity ||
+         connection_state == shill::kStateRedirectFound ||
+         connection_state == shill::kStatePortalSuspected ||
+         connection_state == shill::kStateOnline ||
+         connection_state == shill::kStateFailure ||
+         connection_state == shill::kStateDisconnect ||
+         // TODO(b/260792466): Empty should not be a valid state,
+         // but e.g. new tether NetworkStates and unit tests use it currently.
+         connection_state.empty();
+}
+
 }  // namespace
 
 namespace ash {
@@ -385,33 +401,22 @@
 std::string NetworkState::connection_state() const {
   if (!visible())
     return shill::kStateIdle;
-  DCHECK(connection_state_ == shill::kStateIdle ||
-         connection_state_ == shill::kStateAssociation ||
-         connection_state_ == shill::kStateConfiguration ||
-         connection_state_ == shill::kStateReady ||
-         connection_state_ == shill::kStateNoConnectivity ||
-         connection_state_ == shill::kStateRedirectFound ||
-         connection_state_ == shill::kStatePortalSuspected ||
-         connection_state_ == shill::kStateOnline ||
-         connection_state_ == shill::kStateFailure ||
-         connection_state_ == shill::kStateDisconnect ||
-         // TODO(https://crbug.com/552190): Empty should not be a valid state,
-         // but e.g. new tether NetworkStates and unit tests use it currently.
-         connection_state_.empty());
 
   return connection_state_;
 }
 
 void NetworkState::SetConnectionState(const std::string& connection_state) {
+  DCHECK(IsValidConnectionState(connection_state)) << connection_state;
+
   if (connection_state == connection_state_)
     return;
-  last_connection_state_ = connection_state_;
+  const std::string prev_connection_state = connection_state_;
   connection_state_ = connection_state;
   if (StateIsConnected(connection_state_) ||
-      StateIsConnecting(last_connection_state_)) {
+      StateIsConnecting(prev_connection_state)) {
     // If connected or previously connecting, clear |connect_requested_|.
     connect_requested_ = false;
-  } else if (StateIsConnected(last_connection_state_) &&
+  } else if (StateIsConnected(prev_connection_state) &&
              StateIsConnecting(connection_state_)) {
     // If transitioning from a connected state to a connecting state, set
     // |connect_requested_| so that the UI knows the connecting state is
diff --git a/chromeos/ash/components/network/network_state.h b/chromeos/ash/components/network/network_state.h
index 55e6322..4d29fc3 100644
--- a/chromeos/ash/components/network/network_state.h
+++ b/chromeos/ash/components/network/network_state.h
@@ -321,7 +321,6 @@
   std::string guid_;
   std::string tether_guid_;  // Used to double link a Tether and Wi-Fi network.
   std::string connection_state_;
-  std::string last_connection_state_;
   std::string profile_path_;
   GURL probe_url_;
   std::vector<uint8_t> raw_ssid_;  // Unknown encoding. Not necessarily UTF-8.
diff --git a/chromeos/ash/services/auth_factor_config/recovery_factor_editor.cc b/chromeos/ash/services/auth_factor_config/recovery_factor_editor.cc
index 73f190a..5cc1688 100644
--- a/chromeos/ash/services/auth_factor_config/recovery_factor_editor.cc
+++ b/chromeos/ash/services/auth_factor_config/recovery_factor_editor.cc
@@ -68,6 +68,13 @@
     std::unique_ptr<UserContext> context,
     absl::optional<AuthenticationError> error) {
   if (error.has_value()) {
+    // Handle expired auth session gracefully.
+    if (error->get_cryptohome_code() ==
+        user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN) {
+      std::move(callback).Run(ConfigureResult::kInvalidTokenError);
+      return;
+    }
+
     LOG(ERROR) << "Configuring recovery factor failed, code "
                << error->get_cryptohome_code();
     std::move(callback).Run(ConfigureResult::kClientError);
diff --git a/chromeos/ash/services/libassistant/audio/audio_device_owner.cc b/chromeos/ash/services/libassistant/audio/audio_device_owner.cc
index 67aa927..1d75ac8 100644
--- a/chromeos/ash/services/libassistant/audio/audio_device_owner.cc
+++ b/chromeos/ash/services/libassistant/audio/audio_device_owner.cc
@@ -163,7 +163,7 @@
 // Runs on audio renderer thread (started internally in |output_device_|).
 int AudioDeviceOwner::Render(base::TimeDelta delay,
                              base::TimeTicks delay_timestamp,
-                             int prior_frames_skipped,
+                             const media::AudioGlitchInfo& glitch_info,
                              media::AudioBus* dest) {
   base::AutoLock lock(lock_);
 
diff --git a/chromeos/ash/services/libassistant/audio/audio_device_owner.h b/chromeos/ash/services/libassistant/audio/audio_device_owner.h
index 38a0321..1e3ab4a 100644
--- a/chromeos/ash/services/libassistant/audio/audio_device_owner.h
+++ b/chromeos/ash/services/libassistant/audio/audio_device_owner.h
@@ -62,7 +62,7 @@
   // media::AudioRenderSink::RenderCallback overrides:
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const media::AudioGlitchInfo& glitch_info,
              media::AudioBus* dest) override;
 
   void OnRenderError() override;
diff --git a/chromeos/ash/services/libassistant/audio/audio_output_provider_impl_unittest.cc b/chromeos/ash/services/libassistant/audio/audio_output_provider_impl_unittest.cc
index 592fafd..5fce6d7 100644
--- a/chromeos/ash/services/libassistant/audio/audio_output_provider_impl_unittest.cc
+++ b/chromeos/ash/services/libassistant/audio/audio_output_provider_impl_unittest.cc
@@ -19,6 +19,7 @@
 #include "chromeos/ash/services/libassistant/test_support/fake_platform_delegate.h"
 #include "chromeos/assistant/internal/libassistant/shared_headers.h"
 #include "media/base/audio_bus.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/base/bind_to_current_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -333,7 +334,7 @@
   audio_output_delegate.Reset();
   audio_bus->Zero();
   // On first render, it will push the data to |audio_bus|.
-  owner->Render(base::Microseconds(0), base::TimeTicks::Now(), 0,
+  owner->Render(base::Microseconds(0), base::TimeTicks::Now(), {},
                 audio_bus.get());
   audio_output_delegate.Wait();
   EXPECT_FALSE(audio_bus->AreFramesZero());
@@ -341,7 +342,7 @@
 
   // The subsequent Render call will detect no data available and notify
   // delegate for OnEndOfStream().
-  owner->Render(base::Microseconds(0), base::TimeTicks::Now(), 0,
+  owner->Render(base::Microseconds(0), base::TimeTicks::Now(), {},
                 audio_bus.get());
   EXPECT_TRUE(audio_output_delegate.end_of_stream());
 }
diff --git a/chromeos/crosapi/mojom/diagnostics_service.mojom b/chromeos/crosapi/mojom/diagnostics_service.mojom
index f16cff7..6ef7c146 100644
--- a/chromeos/crosapi/mojom/diagnostics_service.mojom
+++ b/chromeos/crosapi/mojom/diagnostics_service.mojom
@@ -19,7 +19,7 @@
 // Interface for exposing diagnostics service. Implemented in ash-chrome.
 //
 // Next version: 2
-// Next ID: 23
+// Next ID: 24
 [Stable, Uuid="14bc6194-c059-4048-9bea-ca6823eeda82",
 RenamedFrom="ash.health.mojom.DiagnosticsService"]
 interface DiagnosticsService {
@@ -372,11 +372,25 @@
   //                routine.
   RunFingerprintAliveRoutine@22()
       => (DiagnosticsRunRoutineResponse response);
+
+  // Requests that the EmmcLifetime routine is created and started on the
+  // platform. This routine checks the lifetime of the eMMC drive. The routine
+  // will pass if PRE_EOL_INFO is 0x01 (normal). In addition, the value of
+  // DEVICE_LIFE_TIME_EST_TYP_A and DEVICE_LIFE_TIME_EST_TYP_B will be
+  // included in the output.
+  // The availability of this routine can be determined by checking that
+  // kEmmcLifetime is returned by GetAvailableRoutines.
+  //
+  // The response:
+  // * |response| - contains a unique identifier and status for the created
+  //                routine.
+  RunEmmcLifetimeRoutine@23()
+      => (DiagnosticsRunRoutineResponse response);
 };
 
 // Enumeration of each of the diagnostics routines the platform may support.
 //
-// Next ID: 23
+// Next ID: 24
 [Stable, Extensible, RenamedFrom="ash.health.mojom.DiagnosticRoutineEnum"]
 enum DiagnosticsRoutineEnum {
   [Default] kUnknown = 15,
@@ -402,6 +416,7 @@
   kSensitiveSensor = 20,
   kFingerprintAlive = 21,
   kSmartctlCheckWithPercentageUsed = 22,
+  kEmmcLifetime = 23,
 };
 
 // Enumeration of each of the possible statuses for a diagnostics routine.
diff --git a/chromeos/services/tts/tts_player.cc b/chromeos/services/tts/tts_player.cc
index a6c1efca..4a9650d 100644
--- a/chromeos/services/tts/tts_player.cc
+++ b/chromeos/services/tts/tts_player.cc
@@ -55,7 +55,7 @@
 
 int TtsPlayer::Render(base::TimeDelta delay,
                       base::TimeTicks delay_timestamp,
-                      int prior_frames_skipped,
+                      const media::AudioGlitchInfo& glitch_info,
                       media::AudioBus* dest) {
   size_t frames_in_buf = 0;
   {
diff --git a/chromeos/services/tts/tts_player.h b/chromeos/services/tts/tts_player.h
index 38ed860b..6bd7e19 100644
--- a/chromeos/services/tts/tts_player.h
+++ b/chromeos/services/tts/tts_player.h
@@ -55,7 +55,7 @@
   // media::AudioRendererSink::RenderCallback:
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const media::AudioGlitchInfo& glitch_info,
              media::AudioBus* dest) override;
   void OnRenderError() override;
 
diff --git a/chromeos/services/tts/tts_service_unittest.cc b/chromeos/services/tts/tts_service_unittest.cc
index 204ffc7..f1b201f 100644
--- a/chromeos/services/tts/tts_service_unittest.cc
+++ b/chromeos/services/tts/tts_service_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/task_environment.h"
 #include "chromeos/services/tts/public/mojom/tts_service.mojom.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/mojo/mojom/audio_data_pipe.mojom.h"
 #include "media/mojo/mojom/audio_stream_factory.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -165,7 +166,7 @@
 
   auto bus = media::AudioBus::Create(1 /* channels */, 512 /* frames */);
   service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
-      base::Seconds(0), base::TimeTicks::Now(), 0 /* prior frames skipped */,
+      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
       bus.get());
   observer.FlushForTesting();
 
@@ -178,7 +179,7 @@
       std::vector<float>(), 100 /* char_index */, false /* last buffer */);
   playback_tts_stream.FlushForTesting();
   service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
-      base::Seconds(0), base::TimeTicks::Now(), 0 /* prior frames skipped */,
+      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
       bus.get());
   observer.FlushForTesting();
   EXPECT_EQ(1, backing_observer.start_count);
@@ -192,7 +193,7 @@
       std::vector<float>(), 9999 /* char_index */, true /* last buffer */);
   playback_tts_stream.FlushForTesting();
   service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
-      base::Seconds(0), base::TimeTicks::Now(), 0 /* prior frames skipped */,
+      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
       bus.get());
   observer.FlushForTesting();
   EXPECT_EQ(1, backing_observer.start_count);
@@ -216,7 +217,7 @@
 
   auto bus = media::AudioBus::Create(1 /* channels */, 512 /* frames */);
   service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
-      base::Seconds(0), base::TimeTicks::Now(), 0 /* prior frames skipped */,
+      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
       bus.get());
   observer.FlushForTesting();
 
@@ -229,7 +230,7 @@
       std::vector<float>(), -1 /* char_index */, false /* last buffer */);
   playback_tts_stream.FlushForTesting();
   service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
-      base::Seconds(0), base::TimeTicks::Now(), 0 /* prior frames skipped */,
+      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
       bus.get());
   observer.FlushForTesting();
   EXPECT_EQ(1, backing_observer.start_count);
@@ -249,7 +250,7 @@
       ->AddExplicitTimepoint(300, base::Seconds(0));
   playback_tts_stream.FlushForTesting();
   service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
-      base::Seconds(0), base::TimeTicks::Now(), 0 /* prior frames skipped */,
+      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
       bus.get());
   observer.FlushForTesting();
   EXPECT_EQ(1, backing_observer.start_count);
@@ -263,7 +264,7 @@
       std::vector<float>(), 9999 /* char_index */, true /* last buffer */);
   playback_tts_stream.FlushForTesting();
   service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
-      base::Seconds(0), base::TimeTicks::Now(), 0 /* prior frames skipped */,
+      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
       bus.get());
   observer.FlushForTesting();
   EXPECT_EQ(1, backing_observer.start_count);
diff --git a/chromeos/tast_control.gni b/chromeos/tast_control.gni
index 67dbdba..203f2b5 100644
--- a/chromeos/tast_control.gni
+++ b/chromeos/tast_control.gni
@@ -26,6 +26,9 @@
   "graphics.KmsvncConnect",
   "graphics.Smoke.chrome",
 
+  # https://crbug.com/1400636
+  "graphics.ScreenshotCLI",
+
   # b/258714568
   "inputs.InputMethodManagement.guest",
   "inputs.PhysicalKeyboardEmojiSuggestion.guest",
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index 01d488be..1de22c1 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -776,6 +776,9 @@
     <message name="IDS_AUTOFILL_BOTTOM_SHEET_MANAGE_PAYMENT_METHODS" desc="An option in the autofill bottom sheet that triggers navigation to the payment settings." formatter_data="android_java">
       Manage payment methods
     </message>
+    <message name="IDS_AUTOFILL_CREDIT_CARD_BOTTOM_SHEET_TITLE" desc="The title of the touch to fill bottom sheet for payments that appears under the GPay icon." formatter_data="android_java">
+      Autofill payment info
+    </message>
   </if>
 
 </grit-part>
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_CREDIT_CARD_BOTTOM_SHEET_TITLE.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_CREDIT_CARD_BOTTOM_SHEET_TITLE.png.sha1
new file mode 100644
index 0000000..ff7a3fa
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_CREDIT_CARD_BOTTOM_SHEET_TITLE.png.sha1
@@ -0,0 +1 @@
+e432bc3ce9207b674ce286a90b0b23a162de351f
\ No newline at end of file
diff --git a/components/browser_ui/site_settings/android/BUILD.gn b/components/browser_ui/site_settings/android/BUILD.gn
index 2f6beee..77448e8 100644
--- a/components/browser_ui/site_settings/android/BUILD.gn
+++ b/components/browser_ui/site_settings/android/BUILD.gn
@@ -224,6 +224,7 @@
     "java/res/xml/grouped_websites_preferences.xml",
     "java/res/xml/single_website_preferences.xml",
     "java/res/xml/site_settings_preferences.xml",
+    "java/res/xml/site_settings_preferences_with_categories.xml",
     "java/res/xml/website_preferences.xml",
   ]
 
diff --git a/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml b/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml
index f771368..b2a404b 100644
--- a/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml
+++ b/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml
@@ -5,7 +5,11 @@
 found in the LICENSE file.
 -->
 
-<!-- The order of the following items is from: http://crbug.com/610358. -->
+<!--
+The order of the following items is from: http://crbug.com/610358.
+Used when PrivacySandboxSettingsV4 is disabled.
+Also add new settings to site_settings_preferences_with_categories.xml!
+-->
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
     <!-- All sites -->
@@ -15,20 +19,10 @@
         android:title="@string/all_sites"
         android:icon="@drawable/settings_all_sites"
         app:iconTint="@macro/default_icon_color" />
-    <!-- Cookies (PrivacySandboxSettings4 disabled) -->
+    <!-- Cookies -->
     <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="cookies"
         android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings" />
-    <!-- Cookies (PrivacySandboxSettings4 enabled) -->
-    <org.chromium.components.browser_ui.settings.ChromeBasePreference
-        android:key="third_party_cookies"
-        android:title="@string/third_party_cookies_page_title"
-        android:icon="@drawable/permission_cookie"
-        app:iconTint="@macro/default_icon_color"
-        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings" />
-    <org.chromium.components.browser_ui.settings.ChromeBasePreference
-        android:key="site_data"
-        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings" />
     <!-- Location -->
     <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
@@ -78,7 +72,8 @@
         android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
         android:key="sound" />
     <!-- Storage -->
-    <!-- TODO(finnur): Move this over to the new Usage screen, once it exists. -->
+    <!-- TODO(finnur): Move this over to the new Usage screen, once it exists.
+                       Also update site_settings_preferences_with_categories.xml. -->
     <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:fragment="org.chromium.components.browser_ui.site_settings.AllSiteSettings"
         android:key="use_storage"
@@ -126,7 +121,8 @@
         android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
         android:key="auto_dark_web_content" />
     <!-- Request Desktop Site -->
-    <!-- TODO(crbug.com/1243758): Update the location of this setting once approved. -->
+    <!-- TODO(crbug.com/1243758): Update the location of this setting once approved.
+                                  Also update site_settings_preferences_with_categories.xml. -->
     <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
         android:key="request_desktop_site" />
diff --git a/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences_with_categories.xml b/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences_with_categories.xml
new file mode 100644
index 0000000..9fa3886
--- /dev/null
+++ b/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences_with_categories.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+Copyright 2022 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+
+<!--
+The order of the following items is from: http://crbug.com/610358.
+Used when PrivacySandboxSettingsV4 is enabled.
+Also add new settings to site_settings_preferences.xml!
+-->
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <!-- All sites -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.AllSiteSettings"
+        android:key="all_sites"
+        android:title="@string/all_sites"
+        android:icon="@drawable/settings_all_sites"
+        app:iconTint="@macro/default_icon_color" />
+
+    <PreferenceCategory android:title="@string/site_settings_permission_category"/>
+    <!-- Location -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="device_location" />
+    <!-- Camera -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="camera" />
+    <!-- Microphone -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="microphone" />
+    <!-- Sensors -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="sensors" />
+    <!-- Notifications -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="notifications" />
+    <!-- NFC -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="nfc" />
+    <!-- USB -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="usb" />
+    <!-- Clipboard API -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="clipboard" />
+    <!-- Bluetooth -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="bluetooth" />
+    <!-- Bluetooth Scanning -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="bluetooth_scanning" />
+    <!-- VR -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="virtual_reality" />
+    <!-- AR -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="augmented_reality" />
+    <!-- Idle Detection -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="idle_detection" />
+    <!-- Federated Identity API -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="federated_identity_api" />
+    <!-- Auto-dark Web Content -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="auto_dark_web_content" />
+    <!-- Request Desktop Site -->
+    <!-- TODO(crbug.com/1243758): Update the location of this setting once approved.
+                                  Also update site_settings_preferences.xml. -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="request_desktop_site" />
+
+    <PreferenceCategory android:title="@string/site_settings_content_category"/>
+    <!-- Third party cookies -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:key="third_party_cookies"
+        android:title="@string/third_party_cookies_page_title"
+        android:icon="@drawable/permission_cookie"
+        app:iconTint="@macro/default_icon_color"
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings" />
+    <!-- Site data -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:key="site_data"
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings" />
+    <!-- JavaScript -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="javascript" />
+    <!-- Popups -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="popups" />
+    <!-- Ads -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="ads" />
+    <!-- Background sync -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="background_sync" />
+    <!-- Automatic Downloads -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="automatic_downloads" />
+    <!-- Protected content -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="protected_content" />
+    <!-- Sound -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.SingleCategorySettings"
+        android:key="sound" />
+    <!-- Storage -->
+    <!-- TODO(finnur): Move this over to the new Usage screen, once it exists.
+                       Also update site_settings_preferences.xml. -->
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
+        android:fragment="org.chromium.components.browser_ui.site_settings.AllSiteSettings"
+        android:key="use_storage"
+        android:title="@string/website_settings_storage"
+        android:icon="@drawable/ic_storage"
+        app:iconTint="@macro/default_icon_color" />
+</PreferenceScreen>
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java
index 5a5616e..9db7953 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java
@@ -30,7 +30,10 @@
 
     @Override
     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
-        SettingsUtils.addPreferencesFromResource(this, R.xml.site_settings_preferences);
+        SettingsUtils.addPreferencesFromResource(this,
+                getSiteSettingsDelegate().isPrivacySandboxSettings4Enabled()
+                        ? R.xml.site_settings_preferences_with_categories
+                        : R.xml.site_settings_preferences);
         getActivity().setTitle(getContext().getString(R.string.prefs_site_settings));
 
         configurePreferences();
diff --git a/components/browser_ui/strings/android/site_settings.grdp b/components/browser_ui/strings/android/site_settings.grdp
index b981f132..cf597187 100644
--- a/components/browser_ui/strings/android/site_settings.grdp
+++ b/components/browser_ui/strings/android/site_settings.grdp
@@ -5,6 +5,13 @@
     Site settings
   </message>
 
+  <message name="IDS_SITE_SETTINGS_PERMISSION_CATEGORY" desc="Title permissions section in site settings. [CHAR_LIMIT=32]">
+    Permissions
+  </message>
+  <message name="IDS_SITE_SETTINGS_CONTENT_CATEGORY" desc="Title content settings section in site settings. [CHAR_LIMIT=32]">
+    Content
+  </message>
+
   <!-- Site settings categories -->
   <message name="IDS_ALL_SITES" desc='Title of the "All sites" settings screen that allows the user to see permissions for all websites. [CHAR_LIMIT=32]'>
     All sites
diff --git a/components/browser_ui/strings/android/site_settings_grdp/IDS_SITE_SETTINGS_CONTENT_CATEGORY.png.sha1 b/components/browser_ui/strings/android/site_settings_grdp/IDS_SITE_SETTINGS_CONTENT_CATEGORY.png.sha1
new file mode 100644
index 0000000..25de86af
--- /dev/null
+++ b/components/browser_ui/strings/android/site_settings_grdp/IDS_SITE_SETTINGS_CONTENT_CATEGORY.png.sha1
@@ -0,0 +1 @@
+0502b55f7f7167adaf690dcdd0a681730dc2ddb5
\ No newline at end of file
diff --git a/components/browser_ui/strings/android/site_settings_grdp/IDS_SITE_SETTINGS_PERMISSION_CATEGORY.png.sha1 b/components/browser_ui/strings/android/site_settings_grdp/IDS_SITE_SETTINGS_PERMISSION_CATEGORY.png.sha1
new file mode 100644
index 0000000..38f48a77
--- /dev/null
+++ b/components/browser_ui/strings/android/site_settings_grdp/IDS_SITE_SETTINGS_PERMISSION_CATEGORY.png.sha1
@@ -0,0 +1 @@
+8fdebe5b6a5e148466ee059a047cc9f17371cab3
\ No newline at end of file
diff --git a/components/content_settings/core/browser/content_settings_pref_provider.cc b/components/content_settings/core/browser/content_settings_pref_provider.cc
index c616ba9..4e01a898 100644
--- a/components/content_settings/core/browser/content_settings_pref_provider.cc
+++ b/components/content_settings/core/browser/content_settings_pref_provider.cc
@@ -227,7 +227,7 @@
          !primary_pattern.GetHost().empty());
 
   base::Time last_visited = constraints.track_last_visit_for_autoexpiration
-                                ? GetCoarseTime(clock_->Now())
+                                ? GetCoarseVisitedTime(clock_->Now())
                                 : base::Time();
 
   // If SessionModel is OneTime, we know for sure that a one time permission
@@ -265,7 +265,7 @@
       DCHECK(rule.metadata.last_visited != base::Time());
       // Reset iterator to release lock before updating setting.
       it.reset();
-      rule.metadata.last_visited = GetCoarseTime(clock_->Now());
+      rule.metadata.last_visited = GetCoarseVisitedTime(clock_->Now());
       GetPref(content_type)
           ->SetWebsiteSetting(rule.primary_pattern, rule.secondary_pattern,
                               std::move(rule.value), std::move(rule.metadata));
diff --git a/components/content_settings/core/browser/content_settings_utils.cc b/components/content_settings/core/browser/content_settings_utils.cc
index 41f7ef88..040837d 100644
--- a/components/content_settings/core/browser/content_settings_utils.cc
+++ b/components/content_settings/core/browser/content_settings_utils.cc
@@ -15,6 +15,7 @@
 #include "components/content_settings/core/browser/content_settings_registry.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
+#include "components/content_settings/core/common/features.h"
 
 namespace {
 
@@ -195,13 +196,15 @@
   return info && info->GetInitialDefaultSetting() == CONTENT_SETTING_ASK;
 }
 
-base::Time GetCoarseTime(base::Time time) {
+base::Time GetCoarseVisitedTime(base::Time time) {
   return base::Time::FromDeltaSinceWindowsEpoch(
       time.ToDeltaSinceWindowsEpoch().FloorToMultiple(
-          GetCoarseTimePrecision()));
+          GetCoarseVisitedTimePrecision()));
 }
 
-base::TimeDelta GetCoarseTimePrecision() {
+base::TimeDelta GetCoarseVisitedTimePrecision() {
+  if (features::kSafetyCheckUnusedSitePermissionsNoDelay.Get())
+    return base::Days(0);
   return base::Days(7);
 }
 
diff --git a/components/content_settings/core/browser/content_settings_utils.h b/components/content_settings/core/browser/content_settings_utils.h
index c26d0dc..b4e5fe8d 100644
--- a/components/content_settings/core/browser/content_settings_utils.h
+++ b/components/content_settings/core/browser/content_settings_utils.h
@@ -77,10 +77,10 @@
 bool CanTrackLastVisit(ContentSettingsType type);
 
 // Get a timestamp with week-precision.
-base::Time GetCoarseTime(base::Time time);
+base::Time GetCoarseVisitedTime(base::Time time);
 
 // Returns a TimeDelta representing a week.
-base::TimeDelta GetCoarseTimePrecision();
+base::TimeDelta GetCoarseVisitedTimePrecision();
 
 }  // namespace content_settings
 
diff --git a/components/content_settings/core/browser/content_settings_utils_unittest.cc b/components/content_settings/core/browser/content_settings_utils_unittest.cc
index 711b762c..fcd0d52 100644
--- a/components/content_settings/core/browser/content_settings_utils_unittest.cc
+++ b/components/content_settings/core/browser/content_settings_utils_unittest.cc
@@ -8,7 +8,10 @@
 
 #include <string>
 
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "components/content_settings/core/common/features.h"
 #include "components/content_settings/core/test/content_settings_test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -131,13 +134,44 @@
   }
 }
 
-TEST(ContentSettingsUtilsTest, GetCoarseTime) {
+class ContentSettingsUtilsFlagTest : public testing::TestWithParam<bool> {
+ public:
+  ContentSettingsUtilsFlagTest() {
+    if (IsNoDelayForTestingEnabled()) {
+      features_.InitWithFeaturesAndParameters(
+          {{content_settings::features::kSafetyCheckUnusedSitePermissions,
+            {{"unused-site-permissions-no-delay-for-testing", "true"}}}},
+          {});
+    }
+  }
+
+  bool IsNoDelayForTestingEnabled() const { return GetParam(); }
+
+ protected:
+  base::test::SingleThreadTaskEnvironment task_environment_;
+
+ private:
+  base::test::ScopedFeatureList features_;
+};
+
+TEST_P(ContentSettingsUtilsFlagTest, GetCoarseVisitedTime) {
   base::Time now = base::Time::Now();
   for (int i = 0; i < 20; i++) {
     base::Time time = now + base::Days(i);
-    EXPECT_LE(GetCoarseTime(time), time);
-    EXPECT_GE(GetCoarseTime(time), time - GetCoarseTimePrecision());
+    if (IsNoDelayForTestingEnabled()) {
+      EXPECT_EQ(GetCoarseVisitedTime(time), time);
+      EXPECT_EQ(GetCoarseVisitedTime(time),
+                time - GetCoarseVisitedTimePrecision());
+    } else {
+      EXPECT_LE(GetCoarseVisitedTime(time), time);
+      EXPECT_GE(GetCoarseVisitedTime(time),
+                time - GetCoarseVisitedTimePrecision());
+    }
   }
 }
 
+INSTANTIATE_TEST_SUITE_P(/* no prefix */,
+                         ContentSettingsUtilsFlagTest,
+                         testing::Bool());
+
 }  // namespace content_settings
diff --git a/components/content_settings/core/common/features.cc b/components/content_settings/core/common/features.cc
index 56a32c8..6ba4648 100644
--- a/components/content_settings/core/common/features.cc
+++ b/components/content_settings/core/common/features.cc
@@ -25,4 +25,21 @@
     &kDarkenWebsitesCheckboxInThemesSetting, "opt_out", true};
 #endif  // BUILDFLAG(IS_ANDROID)
 
+namespace features {
+
+// Enables unused site permission module in Safety Check.
+BASE_FEATURE(kSafetyCheckUnusedSitePermissions,
+             "SafetyCheckUnusedSitePermissions",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+const base::FeatureParam<base::TimeDelta>
+    kSafetyCheckUnusedSitePermissionsRepeatedUpdateInterval{
+        &kSafetyCheckUnusedSitePermissions,
+        "unused-site-repeated-update-interval", base::Days(1)};
+
+const base::FeatureParam<bool> kSafetyCheckUnusedSitePermissionsNoDelay{
+    &kSafetyCheckUnusedSitePermissions,
+    "unused-site-permissions-no-delay-for-testing", false};
+
+}  // namespace features
 }  // namespace content_settings
diff --git a/components/content_settings/core/common/features.h b/components/content_settings/core/common/features.h
index 7a00e67..1f46a3d 100644
--- a/components/content_settings/core/common/features.h
+++ b/components/content_settings/core/common/features.h
@@ -26,6 +26,24 @@
 extern const base::FeatureParam<bool> kDarkenWebsitesCheckboxOptOut;
 #endif
 
+namespace features {
+
+// Feature to enable the unused site permissions module of Safety Check.
+COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
+BASE_DECLARE_FEATURE(kSafetyCheckUnusedSitePermissions);
+
+// Determines the frequency at which permissions of sites are checked whether
+// they are unused.
+COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
+extern const base::FeatureParam<base::TimeDelta>
+    kSafetyCheckUnusedSitePermissionsRepeatedUpdateInterval;
+
+// When enabled, site permissions will be considered as unused immediately in
+// order to facilitate testing.
+COMPONENT_EXPORT(CONTENT_SETTINGS_FEATURES)
+extern const base::FeatureParam<bool> kSafetyCheckUnusedSitePermissionsNoDelay;
+
+}  // namespace features
 }  // namespace content_settings
 
 #endif  // COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_FEATURES_H_
diff --git a/components/optimization_guide/core/optimization_guide_enums.h b/components/optimization_guide/core/optimization_guide_enums.h
index 6cd237b..5bbbc39 100644
--- a/components/optimization_guide/core/optimization_guide_enums.h
+++ b/components/optimization_guide/core/optimization_guide_enums.h
@@ -110,11 +110,9 @@
   // The new directory to persist this model version's files could not be
   // created.
   kCouldNotCreateDirectory = 12,
-  // The model info was not saved to model store file.
-  kFailedModelInfoSaving = 13,
 
   // Add new values above this line.
-  kMaxValue = kFailedModelInfoSaving,
+  kMaxValue = kCouldNotCreateDirectory,
 };
 
 // The status for the page content annotations being stored.
diff --git a/components/optimization_guide/core/prediction_manager.cc b/components/optimization_guide/core/prediction_manager.cc
index c365071..deefa109 100644
--- a/components/optimization_guide/core/prediction_manager.cc
+++ b/components/optimization_guide/core/prediction_manager.cc
@@ -39,7 +39,6 @@
 #include "components/optimization_guide/core/prediction_model_download_manager.h"
 #include "components/optimization_guide/core/prediction_model_fetcher_impl.h"
 #include "components/optimization_guide/core/prediction_model_override.h"
-#include "components/optimization_guide/core/prediction_model_store.h"
 #include "components/optimization_guide/core/store_update_data.h"
 #include "components/optimization_guide/optimization_guide_internals/webui/optimization_guide_internals.mojom.h"
 #include "components/optimization_guide/proto/models.pb.h"
@@ -58,12 +57,6 @@
                     features::PredictionModelFetchRandomMaxDelaySecs()));
 }
 
-proto::ModelCacheKey GetModelCacheKey(const std::string& locale) {
-  proto::ModelCacheKey model_cache_key;
-  model_cache_key.set_locale(locale);
-  return model_cache_key;
-}
-
 // Util class for recording the state of a prediction model. The result is
 // recorded when it goes out of scope and its destructor is called.
 class ScopedPredictionManagerModelStatusRecorder {
@@ -187,7 +180,6 @@
 
 PredictionManager::PredictionManager(
     base::WeakPtr<OptimizationGuideStore> model_and_features_store,
-    PredictionModelStore* prediction_model_store,
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     PrefService* pref_service,
     bool off_the_record,
@@ -198,7 +190,6 @@
     ComponentUpdatesEnabledProvider component_updates_enabled_provider)
     : prediction_model_download_manager_(nullptr),
       model_and_features_store_(model_and_features_store),
-      prediction_model_store_(prediction_model_store),
       url_loader_factory_(url_loader_factory),
       optimization_guide_logger_(optimization_guide_logger),
       pref_service_(pref_service),
@@ -206,11 +197,7 @@
       clock_(base::DefaultClock::GetInstance()),
       off_the_record_(off_the_record),
       application_locale_(application_locale),
-      model_cache_key_(GetModelCacheKey(application_locale_)),
       models_dir_path_(models_dir_path) {
-  DCHECK(!features::IsInstallWideModelStoreEnabled() ||
-         prediction_model_store_);
-  DCHECK(!model_and_features_store_ || !prediction_model_store_);
   Initialize(std::move(background_download_service_provider));
 }
 
@@ -221,13 +208,7 @@
 
 void PredictionManager::Initialize(
     BackgroundDownloadServiceProvider background_download_service_provider) {
-  if (features::IsInstallWideModelStoreEnabled()) {
-    store_is_ready_ = true;
-    init_time_ = base::TimeTicks::Now();
-    LoadPredictionModels(GetRegisteredOptimizationTargets());
-    LOCAL_HISTOGRAM_BOOLEAN(
-        "OptimizationGuide.PredictionManager.StoreInitialized", true);
-  } else if (model_and_features_store_) {
+  if (model_and_features_store_) {
     model_and_features_store_->Initialize(
         switches::ShouldPurgeModelAndFeaturesStoreOnStartup(),
         base::BindOnce(&PredictionManager::OnStoreInitialized,
@@ -490,8 +471,7 @@
         prediction_models) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  // Check whether the model store from the original profile still exists.
-  if (!features::IsInstallWideModelStoreEnabled() && !model_and_features_store_)
+  if (!model_and_features_store_)
     return;
 
   std::unique_ptr<StoreUpdateData> prediction_model_update_data =
@@ -499,11 +479,7 @@
           clock_->Now() + features::StoredModelsValidDuration());
   bool has_models_to_update = false;
   for (const auto& model : prediction_models) {
-    if (!model.has_model()) {
-      // We already have this updated model, so don't update in store.
-      continue;
-    }
-    if (!model.model().download_url().empty()) {
+    if (model.has_model() && !model.model().download_url().empty()) {
       // We should only be updating the store for on-the-record profiles and
       // after the store has been initialized.
       DCHECK(prediction_model_download_manager_);
@@ -535,6 +511,10 @@
       // once the download has completed successfully.
       continue;
     }
+    if (!model.has_model()) {
+      // We already have this updated model, so don't update in store.
+      continue;
+    }
 
     has_models_to_update = true;
     // Storing the model regardless of whether the model is valid or not. Model
@@ -544,12 +524,7 @@
     OnLoadPredictionModel(model.model_info().optimization_target(),
                           /*record_availability_metrics=*/false,
                           std::make_unique<proto::PredictionModel>(model));
-    if (features::IsInstallWideModelStoreEnabled()) {
-      // Update the metadata in the store.
-      prediction_model_store_->UpdateMetadataForExistingModel(
-          model.model_info().optimization_target(), model_cache_key_,
-          model.model_info());
-    }
+
     if (optimization_guide_logger_->ShouldEnableDebugLogs()) {
       OPTIMIZATION_GUIDE_LOGGER(
           optimization_guide_common::mojom::LogSource::MODEL_MANAGEMENT,
@@ -560,7 +535,7 @@
     }
   }
 
-  if (has_models_to_update && model_and_features_store_) {
+  if (has_models_to_update) {
     model_and_features_store_->UpdatePredictionModels(
         std::move(prediction_model_update_data),
         base::BindOnce(&PredictionManager::OnPredictionModelsStored,
@@ -568,12 +543,11 @@
   }
 }
 
-void PredictionManager::OnModelReady(const base::FilePath& base_model_dir,
-                                     const proto::PredictionModel& model) {
+void PredictionManager::OnModelReady(const proto::PredictionModel& model) {
   if (switches::IsModelOverridePresent())
     return;
 
-  if (!features::IsInstallWideModelStoreEnabled() && !model_and_features_store_)
+  if (!model_and_features_store_)
     return;
 
   DCHECK(model.model_info().has_version() &&
@@ -593,22 +567,14 @@
   }
 
   // Store the received model in the store.
-  if (features::IsInstallWideModelStoreEnabled()) {
-    prediction_model_store_->UpdateModel(
-        model.model_info().optimization_target(), model_cache_key_,
-        model.model_info(), base_model_dir,
-        base::BindOnce(&PredictionManager::OnPredictionModelsStored,
-                       ui_weak_ptr_factory_.GetWeakPtr()));
-  } else {
-    std::unique_ptr<StoreUpdateData> prediction_model_update_data =
-        StoreUpdateData::CreatePredictionModelStoreUpdateData(
-            clock_->Now() + features::StoredModelsValidDuration());
-    prediction_model_update_data->CopyPredictionModelIntoUpdateData(model);
-    model_and_features_store_->UpdatePredictionModels(
-        std::move(prediction_model_update_data),
-        base::BindOnce(&PredictionManager::OnPredictionModelsStored,
-                       ui_weak_ptr_factory_.GetWeakPtr()));
-  }
+  std::unique_ptr<StoreUpdateData> prediction_model_update_data =
+      StoreUpdateData::CreatePredictionModelStoreUpdateData(
+          clock_->Now() + features::StoredModelsValidDuration());
+  prediction_model_update_data->CopyPredictionModelIntoUpdateData(model);
+  model_and_features_store_->UpdatePredictionModels(
+      std::move(prediction_model_update_data),
+      base::BindOnce(&PredictionManager::OnPredictionModelsStored,
+                     ui_weak_ptr_factory_.GetWeakPtr()));
 
   if (registered_optimization_targets_and_metadata_.contains(
           model.model_info().optimization_target())) {
@@ -677,45 +643,31 @@
       "OptimizationGuide.PredictionManager.PredictionModelsStored", true);
 }
 
-void PredictionManager::MaybeInitializeModelDownloads(
-    download::BackgroundDownloadService* background_download_service) {
+void PredictionManager::OnStoreInitialized(
+    BackgroundDownloadServiceProvider background_download_service_provider) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  store_is_ready_ = true;
+  init_time_ = base::TimeTicks::Now();
+  LOCAL_HISTOGRAM_BOOLEAN(
+      "OptimizationGuide.PredictionManager.StoreInitialized", true);
 
   // Create the download manager here if we are allowed to.
   if (features::IsModelDownloadingEnabled() && !off_the_record_ &&
       !prediction_model_download_manager_) {
     prediction_model_download_manager_ =
         std::make_unique<PredictionModelDownloadManager>(
-            background_download_service, models_dir_path_,
-            prediction_model_store_, model_cache_key_,
+            background_download_service_provider
+                ? std::move(background_download_service_provider).Run()
+                : nullptr,
+            models_dir_path_,
             base::ThreadPool::CreateSequencedTaskRunner(
                 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
                  base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}));
     prediction_model_download_manager_->AddObserver(this);
   }
 
-  // Only load models if there are optimization targets registered.
-  if (!registered_optimization_targets_and_metadata_.empty())
-    MaybeScheduleFirstModelFetch();
-}
-
-void PredictionManager::OnStoreInitialized(
-    BackgroundDownloadServiceProvider background_download_service_provider) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  store_is_ready_ = true;
-  init_time_ = base::TimeTicks::Now();
-  LOCAL_HISTOGRAM_BOOLEAN(
-      "OptimizationGuide.PredictionManager.StoreInitialized", true);
-
   // Purge any inactive models from the store.
-  if (model_and_features_store_)
-    model_and_features_store_->PurgeInactiveModels();
-
-  MaybeInitializeModelDownloads(
-      background_download_service_provider && !off_the_record_
-          ? std::move(background_download_service_provider).Run()
-          : nullptr);
+  model_and_features_store_->PurgeInactiveModels();
 
   // Only load models if there are optimization targets registered.
   if (registered_optimization_targets_and_metadata_.empty())
@@ -724,6 +676,8 @@
   // The store is ready so start loading models for the registered optimization
   // targets.
   LoadPredictionModels(GetRegisteredOptimizationTargets());
+
+  MaybeScheduleFirstModelFetch();
 }
 
 void PredictionManager::OnPredictionModelOverrideLoaded(
@@ -751,23 +705,6 @@
     return;
   }
 
-  if (features::IsInstallWideModelStoreEnabled()) {
-    for (const auto optimization_target : optimization_targets) {
-      if (!prediction_model_store_->HasModel(optimization_target,
-                                             model_cache_key_)) {
-        RecordModelAvailableAtRegistration(optimization_target, false);
-        continue;
-      }
-      prediction_model_store_->LoadModel(
-          optimization_target, model_cache_key_,
-          base::BindOnce(&PredictionManager::OnLoadPredictionModel,
-                         ui_weak_ptr_factory_.GetWeakPtr(), optimization_target,
-                         /*record_availability_metrics=*/true));
-    }
-    return;
-  }
-
-  DCHECK(!features::IsInstallWideModelStoreEnabled());
   if (!model_and_features_store_)
     return;
 
diff --git a/components/optimization_guide/core/prediction_manager.h b/components/optimization_guide/core/prediction_manager.h
index 1a61645..0858c2a 100644
--- a/components/optimization_guide/core/prediction_manager.h
+++ b/components/optimization_guide/core/prediction_manager.h
@@ -45,7 +45,6 @@
 class OptimizationTargetModelObserver;
 class PredictionModelDownloadManager;
 class PredictionModelFetcher;
-class PredictionModelStore;
 class ModelInfo;
 
 // A PredictionManager supported by the optimization guide that makes an
@@ -64,7 +63,6 @@
 
   PredictionManager(
       base::WeakPtr<OptimizationGuideStore> model_and_features_store,
-      PredictionModelStore* prediction_model_store,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       PrefService* pref_service,
       bool off_the_record,
@@ -133,8 +131,7 @@
       std::unique_ptr<ModelInfo> model_info);
 
   // PredictionModelDownloadObserver:
-  void OnModelReady(const base::FilePath& base_model_dir,
-                    const proto::PredictionModel& model) override;
+  void OnModelReady(const proto::PredictionModel& model) override;
   void OnModelDownloadStarted(
       proto::OptimizationTarget optimization_target) override;
   void OnModelDownloadFailed(
@@ -143,10 +140,6 @@
   std::vector<optimization_guide_internals::mojom::DownloadedModelInfoPtr>
   GetDownloadedModelsInfoForWebUI() const;
 
-  // Initialize the model metadata fetching and downloads.
-  void MaybeInitializeModelDownloads(
-      download::BackgroundDownloadService* background_download_service);
-
  protected:
   // Process |prediction_models| to be stored in the in memory optimization
   // target prediction model map for immediate use and asynchronously write the
@@ -157,7 +150,6 @@
 
  private:
   friend class PredictionManagerTestBase;
-  friend class PredictionModelStoreBrowserTest;
 
   // Called on construction to initialize the prediction model.
   // |background_dowload_service_provider| can provide the
@@ -256,10 +248,6 @@
   void NotifyObserversOfNewModel(proto::OptimizationTarget optimization_target,
                                  const ModelInfo& model_info);
 
-  void SetModelCacheKeyForTesting(const proto::ModelCacheKey& model_cache_key) {
-    model_cache_key_ = model_cache_key;
-  }
-
   // A map of optimization target to the model file containing the model for the
   // target.
   base::flat_map<proto::OptimizationTarget, std::unique_ptr<ModelInfo>>
@@ -285,16 +273,12 @@
   std::unique_ptr<PredictionModelDownloadManager>
       prediction_model_download_manager_;
 
-  // TODO(crbug/1358568): Remove this old model store once the new model store
-  // is launched.
+  // TODO(crbug/1183507): Remove host model features store and all relevant
+  // code, and deprecate the proto field too.
   // The optimization guide store that contains prediction models and host
   // model features from the remote Optimization Guide Service.
   base::WeakPtr<OptimizationGuideStore> model_and_features_store_;
 
-  // The new optimization guide model store. Will be null when the feature is
-  // not enabled. Not owned and outlives |this| since its an install-wide store.
-  raw_ptr<PredictionModelStore> prediction_model_store_;
-
   // A stored response from a model and host model features fetch used to hold
   // models to be stored once host model features are processed and stored.
   std::unique_ptr<proto::GetModelsResponse> get_models_response_data_to_store_;
@@ -316,8 +300,6 @@
   ComponentUpdatesEnabledProvider component_updates_enabled_provider_;
 
   // Time the prediction manager got initialized.
-  // TODO(crbug/1358568): Remove this old model store once the new model store
-  // is launched.
   base::TimeTicks init_time_;
 
   // The timer used to schedule fetching prediction models and host model
@@ -329,19 +311,18 @@
   raw_ptr<const base::Clock> clock_;
 
   // Whether the |model_and_features_store_| is initialized and ready for use.
-  // TODO(crbug/1358568): Remove this old model store once the new model store
-  // is launched.
   bool store_is_ready_ = false;
 
+  // Whether host model features have been loaded from the store and are ready
+  // for use.
+  bool host_model_features_loaded_ = false;
+
   // Whether the profile for this PredictionManager is off the record.
   bool off_the_record_ = false;
 
   // The locale of the application.
   std::string application_locale_;
 
-  // Model cache key for the profile.
-  proto::ModelCacheKey model_cache_key_;
-
   // The path to the directory containing the models.
   base::FilePath models_dir_path_;
 
diff --git a/components/optimization_guide/core/prediction_manager_unittest.cc b/components/optimization_guide/core/prediction_manager_unittest.cc
index a3a503c..7a11b37 100644
--- a/components/optimization_guide/core/prediction_manager_unittest.cc
+++ b/components/optimization_guide/core/prediction_manager_unittest.cc
@@ -11,7 +11,6 @@
 
 #include "base/base64.h"
 #include "base/command_line.h"
-#include "base/files/file_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/gtest_util.h"
@@ -34,7 +33,6 @@
 #include "components/optimization_guide/core/prediction_model_download_manager.h"
 #include "components/optimization_guide/core/prediction_model_fetcher.h"
 #include "components/optimization_guide/core/prediction_model_fetcher_impl.h"
-#include "components/optimization_guide/core/prediction_model_store.h"
 #include "components/optimization_guide/core/proto_database_provider_test_base.h"
 #include "components/optimization_guide/proto/hint_cache.pb.h"
 #include "components/optimization_guide/proto/models.pb.h"
@@ -50,15 +48,12 @@
 using leveldb_proto::test::FakeDB;
 
 namespace {
-
 // Retry delay is 2 minutes to allow for fetch retry delay + some random delay
 // to pass.
 constexpr int kTestFetchRetryDelaySecs = 60 * 2 + 62;
 // 24 hours + random fetch delay.
 constexpr int kUpdateFetchModelAndFeaturesTimeSecs = 24 * 60 * 60 + 62;
 
-constexpr char kTestLocale[] = "en-US";
-
 }  // namespace
 
 namespace optimization_guide {
@@ -88,12 +83,6 @@
   return get_models_response;
 }
 
-proto::ModelCacheKey GetTestModelCacheKey() {
-  proto::ModelCacheKey model_cache_key;
-  model_cache_key.set_locale(kTestLocale);
-  return model_cache_key;
-}
-
 class FakeOptimizationTargetModelObserver
     : public OptimizationTargetModelObserver {
  public:
@@ -125,8 +114,6 @@
       scoped_refptr<base::SequencedTaskRunner> task_runner)
       : PredictionModelDownloadManager(/*download_service=*/nullptr,
                                        models_dir_path,
-                                       /*prediction_model_store=*/nullptr,
-                                       proto::ModelCacheKey(),
                                        task_runner) {}
   ~FakePredictionModelDownloadManager() override = default;
 
@@ -310,8 +297,7 @@
                        bool have_models_in_store = true) {
     load_models_ = load_models;
     have_models_in_store_ = have_models_in_store;
-    if (init_callback_)
-      std::move(init_callback_).Run();
+    std::move(init_callback_).Run();
   }
 
   void LoadPredictionModel(const EntryKey& prediction_model_entry_key,
@@ -360,7 +346,6 @@
  public:
   TestPredictionManager(
       base::WeakPtr<OptimizationGuideStore> model_and_features_store,
-      PredictionModelStore* prediction_model_store,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       PrefService* pref_service,
       ComponentUpdatesEnabledProvider component_updates_enabled_provider,
@@ -369,7 +354,6 @@
       const base::FilePath& models_dir_path)
       : PredictionManager(
             model_and_features_store,
-            prediction_model_store,
             url_loader_factory,
             pref_service,
             off_the_record,
@@ -419,14 +403,12 @@
 
     model_and_features_store_ = CreateModelAndHostModelFeaturesStore();
     prediction_manager_ = std::make_unique<TestPredictionManager>(
-        !features::IsInstallWideModelStoreEnabled()
-            ? model_and_features_store_->AsWeakPtr()
-            : nullptr,
-        prediction_model_store_.get(), url_loader_factory_, pref_service_.get(),
+        model_and_features_store_->AsWeakPtr(), url_loader_factory_,
+        pref_service_.get(),
         base::BindRepeating(
             &PredictionManagerTestBase::AreComponentUpdatesEnabled,
             base::Unretained(this)),
-        false, kTestLocale, temp_dir());
+        false, "en-US", temp_dir());
     prediction_manager_->SetClockForTesting(task_environment_.GetMockClock());
   }
 
@@ -455,13 +437,8 @@
 
   void SetStoreInitialized(bool load_models = true,
                            bool have_models_in_store = true) {
-    if (features::IsInstallWideModelStoreEnabled()) {
-      prediction_manager_->MaybeInitializeModelDownloads(
-          /*background_download_service=*/nullptr);
-    } else {
-      models_and_features_store()->RunInitCallback(load_models,
-                                                   have_models_in_store);
-    }
+    models_and_features_store()->RunInitCallback(load_models,
+                                                 have_models_in_store);
     RunUntilIdle();
     // Move clock forward for any short delays added for the fetcher, until the
     // startup fetch could start.
@@ -487,7 +464,7 @@
   TestOptimizationGuideStore* models_and_features_store() const {
     base::WeakPtr<OptimizationGuideStore> store =
         prediction_manager()->model_and_features_store();
-    DCHECK(features::IsInstallWideModelStoreEnabled() || store);
+    DCHECK(store);
     return static_cast<TestOptimizationGuideStore*>(store.get());
   }
 
@@ -513,7 +490,6 @@
   // tsan flakes caused by other tasks running while |feature_list_| is
   // destroyed.
   base::test::ScopedFeatureList feature_list_;
-  std::unique_ptr<PredictionModelStore> prediction_model_store_;
 
  private:
   base::test::TaskEnvironment task_environment_{
@@ -582,63 +558,24 @@
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
 }
 
-class PredictionManagerTest : public testing::WithParamInterface<bool>,
-                              public PredictionManagerTestBase {
+class PredictionManagerTest : public PredictionManagerTestBase {
  public:
   PredictionManagerTest() {
     // This needs to be done before any tasks are run that might check if a
     // feature is enabled, to avoid tsan errors.
 
-    std::vector<base::test::FeatureRef> enabled_features = {
-        features::kRemoteOptimizationGuideFetching,
-        features::kOptimizationGuideModelDownloading,
-    };
-    if (ShouldEnableInstallWideModelStore()) {
-      local_state_prefs_ = std::make_unique<TestingPrefServiceSimple>();
-      prefs::RegisterLocalStatePrefs(local_state_prefs_->registry());
-      enabled_features.emplace_back(
-          features::kOptimizationGuideInstallWideModelStore);
-    }
-    feature_list_.InitWithFeatures(enabled_features, {});
+    feature_list_.InitWithFeatures(
+        {features::kRemoteOptimizationGuideFetching,
+         features::kOptimizationGuideModelDownloading},
+        {});
   }
 
-  void SetUp() override {
-    PredictionManagerTestBase::SetUp();
-    if (ShouldEnableInstallWideModelStore()) {
-      prediction_model_store_ =
-          PredictionModelStore::CreatePredictionModelStoreForTesting(
-              local_state_prefs_.get(), temp_dir());
-    }
-  }
-
-  void CreateTestModelFiles(const proto::ModelInfo model_info,
-                            const base::FilePath& base_model_dir) {
-    CreateDirectory(base_model_dir);
-    WriteFile(base_model_dir.Append(GetBaseFileNameForModels()), "");
-    std::string model_info_str;
-    ASSERT_TRUE(model_info.SerializeToString(&model_info_str));
-    WriteFile(base_model_dir.Append(GetBaseFileNameForModelInfo()),
-              model_info_str);
-    RunUntilIdle();
-  }
-
-  PredictionModelStore* prediction_model_store() {
-    return prediction_model_store_.get();
-  }
-
-  bool ShouldEnableInstallWideModelStore() const { return GetParam(); }
-
  private:
   variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
       variations::VariationsIdsProvider::Mode::kUseSignedInState};
-  std::unique_ptr<TestingPrefServiceSimple> local_state_prefs_;
 };
 
-INSTANTIATE_TEST_SUITE_P(All,
-                         PredictionManagerTest,
-                         /*ShouldEnableInstallWideModelStore=*/testing::Bool());
-
-TEST_P(PredictionManagerTest, RemoteFetchingPrefDisabled) {
+TEST_F(PredictionManagerTest, RemoteFetchingPrefDisabled) {
   SetComponentUpdatesPrefEnabled(false);
   CreatePredictionManager();
 
@@ -654,7 +591,7 @@
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
 }
 
-TEST_P(PredictionManagerTest, AddObserverForOptimizationTargetModel) {
+TEST_F(PredictionManagerTest, AddObserverForOptimizationTargetModel) {
   base::HistogramTester histogram_tester;
 
   CreatePredictionManager();
@@ -706,7 +643,7 @@
                    .has_value());
 
   base::FilePath additional_file_path =
-      temp_dir().AppendASCII("foo").AppendASCII("additional_file.txt");
+      temp_dir().AppendASCII("whatever").AppendASCII("additional_file.txt");
   proto::ModelInfo model_info;
   model_info.set_optimization_target(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
@@ -723,17 +660,17 @@
 
     proto::PredictionModel model1;
     *model1.mutable_model_info() = model_info;
-    model1.mutable_model()->set_download_url(FilePathToString(
-        temp_dir().AppendASCII("foo").Append(GetBaseFileNameForModels())));
-    prediction_manager()->OnModelReady(temp_dir().AppendASCII("foo"), model1);
+    model1.mutable_model()->set_download_url(
+        FilePathToString(temp_dir().AppendASCII("whatever")));
+    prediction_manager()->OnModelReady(model1);
     RunUntilIdle();
 
     absl::optional<ModelInfo> received_model =
         observer.last_received_model_for_target(
             proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
     EXPECT_EQ(received_model->GetModelMetadata()->type_url(), "sometypeurl");
-    EXPECT_EQ(temp_dir().AppendASCII("foo").Append(GetBaseFileNameForModels()),
-              received_model->GetModelFilePath());
+    EXPECT_EQ(received_model->GetModelFilePath().BaseName().value(),
+              FILE_PATH_LITERAL("whatever"));
     EXPECT_EQ(received_model->GetAdditionalFiles(),
               base::flat_set<base::FilePath>{additional_file_path});
 
@@ -768,12 +705,11 @@
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, &observer);
   observer.Reset();
   proto::PredictionModel model2;
-  model_info.clear_additional_files();
   *model2.mutable_model_info() = model_info;
   model2.mutable_model_info()->set_version(2);
-  model2.mutable_model()->set_download_url(FilePathToString(
-      temp_dir().AppendASCII("bar").AppendASCII("bar_model.tflite")));
-  prediction_manager()->OnModelReady(temp_dir().AppendASCII("bar"), model2);
+  model2.mutable_model()->set_download_url(
+      FilePathToString(temp_dir().AppendASCII("whatever2")));
+  prediction_manager()->OnModelReady(model2);
   RunUntilIdle();
 
   // Last received path should not have been updated since the observer was
@@ -784,7 +720,7 @@
                    .has_value());
 }
 
-TEST_P(PredictionManagerTest,
+TEST_F(PredictionManagerTest,
        AddObserverForOptimizationTargetModelAddAnotherObserverForSameTarget) {
   // Fails under "threadsafe" mode.
   testing::GTEST_FLAG(death_test_style) = "fast";
@@ -806,16 +742,18 @@
   // Ensure observer is hooked up.
   proto::PredictionModel model1;
   *model1.mutable_model_info() = model_info;
-  model1.mutable_model()->set_download_url(FilePathToString(
-      temp_dir().AppendASCII("foo").Append(GetBaseFileNameForModels())));
-  prediction_manager()->OnModelReady(temp_dir().AppendASCII("foo"), model1);
+  model1.mutable_model()->set_download_url(
+      FilePathToString(temp_dir().AppendASCII("whatever")));
+  prediction_manager()->OnModelReady(model1);
   RunUntilIdle();
 
-  EXPECT_EQ(temp_dir().AppendASCII("foo").Append(GetBaseFileNameForModels()),
-            observer1
+  EXPECT_EQ(observer1
                 .last_received_model_for_target(
                     proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD)
-                ->GetModelFilePath());
+                ->GetModelFilePath()
+                .BaseName()
+                .value(),
+            FILE_PATH_LITERAL("whatever"));
 
 #if !BUILDFLAG(IS_WIN)
   // Do not run the DCHECK death test on Windows since there's some weird
@@ -833,7 +771,7 @@
 
 // See crbug/1227996.
 #if !BUILDFLAG(IS_WIN)
-TEST_P(PredictionManagerTest,
+TEST_F(PredictionManagerTest,
        AddObserverForOptimizationTargetModelCommandLineOverride) {
   base::HistogramTester histogram_tester;
 
@@ -898,9 +836,9 @@
   model.mutable_model_info()->set_optimization_target(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
   model.mutable_model_info()->set_version(1);
-  model.mutable_model()->set_download_url(FilePathToString(
-      temp_dir().AppendASCII("foo").Append(GetBaseFileNameForModels())));
-  prediction_manager()->OnModelReady(temp_dir().AppendASCII("foo"), model);
+  model.mutable_model()->set_download_url(
+      FilePathToString(temp_dir().AppendASCII("whatever2")));
+  prediction_manager()->OnModelReady(model);
   RunUntilIdle();
 
   // Last received path should not have been updated since the observer was
@@ -912,7 +850,7 @@
 }
 #endif
 
-TEST_P(PredictionManagerTest,
+TEST_F(PredictionManagerTest,
        NoPredictionModelForRegisteredOptimizationTarget) {
   base::HistogramTester histogram_tester;
 
@@ -928,7 +866,7 @@
       false, 1);
 }
 
-TEST_P(PredictionManagerTest, UpdatePredictionModelsWithInvalidModel) {
+TEST_F(PredictionManagerTest, UpdatePredictionModelsWithInvalidModel) {
   base::HistogramTester histogram_tester;
 
   CreatePredictionManager();
@@ -944,7 +882,7 @@
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
   model.mutable_model_info()->set_version(3);
   model.mutable_model();
-  prediction_manager()->OnModelReady(temp_dir().AppendASCII("foo"), model);
+  prediction_manager()->OnModelReady(model);
   RunUntilIdle();
 
   histogram_tester.ExpectBucketCount("OptimizationGuide.IsPredictionModelValid",
@@ -956,13 +894,11 @@
       "OptimizationGuide.PredictionModelUpdateVersion.PainfulPageLoad", 1);
   histogram_tester.ExpectTotalCount(
       "OptimizationGuide.PredictionModelLoadedVersion.PainfulPageLoad", 0);
-  if (!ShouldEnableInstallWideModelStore()) {
-    histogram_tester.ExpectUniqueSample(
-        "OptimizationGuide.PredictionModelRemoved.PainfulPageLoad", true, 1);
-  }
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.PredictionModelRemoved.PainfulPageLoad", true, 1);
 }
 
-TEST_P(PredictionManagerTest, UpdateModelFileWithSameVersion) {
+TEST_F(PredictionManagerTest, UpdateModelFileWithSameVersion) {
   base::HistogramTester histogram_tester;
 
   CreatePredictionManager();
@@ -976,9 +912,9 @@
   model.mutable_model_info()->set_optimization_target(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
   model.mutable_model_info()->set_version(3);
-  model.mutable_model()->set_download_url(FilePathToString(
-      temp_dir().AppendASCII("foo").Append(GetBaseFileNameForModels())));
-  prediction_manager()->OnModelReady(temp_dir().AppendASCII("foo"), model);
+  model.mutable_model()->set_download_url(
+      FilePathToString(temp_dir().AppendASCII("whatever2")));
+  prediction_manager()->OnModelReady(model);
   RunUntilIdle();
 
   EXPECT_TRUE(observer
@@ -990,7 +926,7 @@
   observer.Reset();
 
   // Send the same model again.
-  prediction_manager()->OnModelReady(temp_dir().AppendASCII("foo"), model);
+  prediction_manager()->OnModelReady(model);
 
   // The observer should not have received an update.
   EXPECT_FALSE(observer
@@ -999,7 +935,7 @@
                    .has_value());
 }
 
-TEST_P(PredictionManagerTest, DownloadManagerUnavailableShouldNotFetch) {
+TEST_F(PredictionManagerTest, DownloadManagerUnavailableShouldNotFetch) {
   base::HistogramTester histogram_tester;
 
   CreatePredictionManager();
@@ -1027,7 +963,7 @@
       ModelDeliveryEvent::kDownloadServiceUnavailable, 1);
 }
 
-TEST_P(PredictionManagerTest, UpdateModelWithDownloadUrl) {
+TEST_F(PredictionManagerTest, UpdateModelWithDownloadUrl) {
   base::HistogramTester histogram_tester;
 
   CreatePredictionManager();
@@ -1063,24 +999,19 @@
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
 }
 
-TEST_P(PredictionManagerTest, UpdateModelForUnregisteredTargetOnModelReady) {
+TEST_F(PredictionManagerTest, UpdateModelForUnregisteredTargetOnModelReady) {
   base::HistogramTester histogram_tester;
   CreatePredictionManager();
 
   SetStoreInitialized();
 
-  auto base_model_dir = temp_dir().AppendASCII("foo");
   proto::PredictionModel model;
   model.mutable_model_info()->set_optimization_target(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
   model.mutable_model_info()->set_version(3);
   model.mutable_model()->set_download_url(
-      FilePathToString(base_model_dir.Append(GetBaseFileNameForModels())));
-  if (ShouldEnableInstallWideModelStore()) {
-    CreateTestModelFiles(model.model_info(), base_model_dir);
-  }
-  prediction_manager()->OnModelReady(base_model_dir, model);
-  RunUntilIdle();
+      FilePathToString(temp_dir().AppendASCII("whatever")));
+  prediction_manager()->OnModelReady(model);
 
   histogram_tester.ExpectTotalCount(
       "OptimizationGuide.PredictionManager.PredictionModelsStored", 1);
@@ -1110,23 +1041,10 @@
       ModelDeliveryEvent::kModelDelivered, 1);
 }
 
-TEST_P(PredictionManagerTest,
+TEST_F(PredictionManagerTest,
        StoreInitializedAfterOptimizationTargetRegistered) {
   base::HistogramTester histogram_tester;
   CreatePredictionManager();
-  if (ShouldEnableInstallWideModelStore()) {
-    proto::ModelInfo model_info;
-    model_info.set_optimization_target(
-        proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-    model_info.set_version(1);
-    auto base_model_dir = temp_dir().AppendASCII("foo");
-    CreateTestModelFiles(model_info, base_model_dir);
-    prediction_model_store()->UpdateModel(
-        proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, GetTestModelCacheKey(),
-        model_info, base_model_dir, base::DoNothing());
-    RunUntilIdle();
-  }
-
   // Ensure that the fetch does not cause any models or features to load.
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
@@ -1134,12 +1052,10 @@
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, absl::nullopt, &observer);
-  if (!ShouldEnableInstallWideModelStore())
-    EXPECT_FALSE(models_and_features_store()->WasModelLoaded());
+  EXPECT_FALSE(models_and_features_store()->WasModelLoaded());
 
   SetStoreInitialized();
-  if (!ShouldEnableInstallWideModelStore())
-    EXPECT_TRUE(models_and_features_store()->WasModelLoaded());
+  EXPECT_TRUE(models_and_features_store()->WasModelLoaded());
 
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
   histogram_tester.ExpectUniqueSample(
@@ -1150,38 +1066,23 @@
       true, 1);
 }
 
-TEST_P(PredictionManagerTest,
+TEST_F(PredictionManagerTest,
        StoreInitializedBeforeOptimizationTargetRegistered) {
   base::HistogramTester histogram_tester;
   CreatePredictionManager();
-  if (ShouldEnableInstallWideModelStore()) {
-    proto::ModelInfo model_info;
-    model_info.set_optimization_target(
-        proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD);
-    model_info.set_version(1);
-    auto base_model_dir = temp_dir().AppendASCII("foo");
-    CreateTestModelFiles(model_info, base_model_dir);
-    prediction_model_store()->UpdateModel(
-        proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, GetTestModelCacheKey(),
-        model_info, base_model_dir, base::DoNothing());
-    RunUntilIdle();
-  }
-
   // Ensure that the fetch does not cause any models or features to load.
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
           PredictionModelFetcherEndState::kFetchFailed));
   SetStoreInitialized();
 
-  if (!ShouldEnableInstallWideModelStore())
-    EXPECT_FALSE(models_and_features_store()->WasModelLoaded());
+  EXPECT_FALSE(models_and_features_store()->WasModelLoaded());
   FakeOptimizationTargetModelObserver observer;
   prediction_manager()->AddObserverForOptimizationTargetModel(
       proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD, absl::nullopt, &observer);
   RunUntilIdle();
 
-  if (!ShouldEnableInstallWideModelStore())
-    EXPECT_TRUE(models_and_features_store()->WasModelLoaded());
+  EXPECT_TRUE(models_and_features_store()->WasModelLoaded());
 
   EXPECT_FALSE(prediction_model_fetcher()->models_fetched());
   histogram_tester.ExpectUniqueSample(
@@ -1192,7 +1093,7 @@
       true, 1);
 }
 
-TEST_P(PredictionManagerTest, ModelFetcherTimerRetryDelay) {
+TEST_F(PredictionManagerTest, ModelFetcherTimerRetryDelay) {
   CreatePredictionManager();
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
@@ -1219,7 +1120,7 @@
   EXPECT_TRUE(prediction_model_fetcher()->models_fetched());
 }
 
-TEST_P(PredictionManagerTest, ModelFetcherTimerFetchSucceeds) {
+TEST_F(PredictionManagerTest, ModelFetcherTimerFetchSucceeds) {
   CreatePredictionManager();
   prediction_manager()->SetPredictionModelFetcherForTesting(
       BuildTestPredictionModelFetcher(
diff --git a/components/optimization_guide/core/prediction_model_download_manager.cc b/components/optimization_guide/core/prediction_model_download_manager.cc
index 201321d..858188ac 100644
--- a/components/optimization_guide/core/prediction_model_download_manager.cc
+++ b/components/optimization_guide/core/prediction_model_download_manager.cc
@@ -25,7 +25,6 @@
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/core/prediction_model_download_observer.h"
-#include "components/optimization_guide/core/prediction_model_store.h"
 #include "components/services/unzip/public/cpp/unzip.h"
 #include "crypto/sha2.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
@@ -85,15 +84,6 @@
       status);
 }
 
-// Writes the |model_info| to |file_path|.
-bool WriteModelInfoProtoToFile(const proto::ModelInfo& model_info,
-                               const base::FilePath& file_path) {
-  std::string model_info_str;
-  if (!model_info.SerializeToString(&model_info_str))
-    return false;
-  return base::WriteFile(file_path, model_info_str);
-}
-
 }  // namespace
 
 const char kPredictionModelOptimizationTargetCustomDataKey[] =
@@ -102,15 +92,11 @@
 PredictionModelDownloadManager::PredictionModelDownloadManager(
     download::BackgroundDownloadService* download_service,
     const base::FilePath& models_dir_path,
-    PredictionModelStore* prediction_model_store,
-    const proto::ModelCacheKey& model_cache_key,
     scoped_refptr<base::SequencedTaskRunner> background_task_runner)
     : download_service_(download_service),
       is_available_for_downloads_(true),
       api_key_(features::GetOptimizationGuideServiceAPIKey()),
       models_dir_path_(models_dir_path),
-      prediction_model_store_(prediction_model_store),
-      model_cache_key_(model_cache_key),
       background_task_runner_(background_task_runner) {}
 
 PredictionModelDownloadManager::~PredictionModelDownloadManager() = default;
@@ -354,26 +340,19 @@
         PredictionModelDownloadStatus::kFailedCrxUnzip);
     return;
   }
-  base::FilePath base_model_dir =
-      features::IsInstallWideModelStoreEnabled()
-          ? prediction_model_store_->GetBaseModelDirForModelCacheKey(
-                *optimization_target, model_cache_key_)
-          : models_dir_path_.AppendASCII(
-                base::GUID::GenerateRandomV4().AsLowercaseString());
 
   background_task_runner_->PostTaskAndReplyWithResult(
       FROM_HERE,
       base::BindOnce(&PredictionModelDownloadManager::ProcessUnzippedContents,
-                     base_model_dir, unzipped_dir_path),
+                     models_dir_path_, unzipped_dir_path),
       base::BindOnce(&PredictionModelDownloadManager::NotifyModelReady,
-                     ui_weak_ptr_factory_.GetWeakPtr(), optimization_target,
-                     base_model_dir));
+                     ui_weak_ptr_factory_.GetWeakPtr(), optimization_target));
 }
 
 // static
 absl::optional<proto::PredictionModel>
 PredictionModelDownloadManager::ProcessUnzippedContents(
-    const base::FilePath& base_model_dir,
+    const base::FilePath& model_dir_path,
     const base::FilePath& unzipped_dir_path) {
   // Clean up temp dir when this function finishes.
   base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
@@ -400,15 +379,17 @@
     return absl::nullopt;
   }
 
-  if (base_model_dir.empty()) {
+  if (model_dir_path.empty()) {
     RecordPredictionModelDownloadStatus(
         PredictionModelDownloadStatus::kOptGuideDirectoryDoesNotExist);
     return absl::nullopt;
   }
 
-  // Move each packaged file away from temp directory into the model
-  // base directory.
-  if (!base::CreateDirectory(base_model_dir)) {
+  // Move each packaged file away from temp directory into a new directory.
+
+  base::FilePath store_dir = model_dir_path.AppendASCII(
+      base::GUID::GenerateRandomV4().AsLowercaseString());
+  if (!base::CreateDirectory(store_dir)) {
     RecordPredictionModelDownloadStatus(
         PredictionModelDownloadStatus::kCouldNotCreateDirectory);
     return absl::nullopt;
@@ -419,7 +400,7 @@
   // Note that the base file name is used for backwards compatibility checking
   // in |OptimizationGuideStore::OnLoadModelsToBeUpdated|.
   base::FilePath store_model_path =
-      base_model_dir.Append(GetBaseFileNameForModels());
+      store_dir.Append(GetBaseFileNameForModels());
 
   proto::PredictionModel model;
   *model.mutable_model_info() = model_info;
@@ -447,7 +428,7 @@
     base::FilePath temp_add_file_path =
         unzipped_dir_path.AppendASCII(additional_file_path);
     base::FilePath store_add_file_path =
-        base_model_dir.AppendASCII(additional_file_path);
+        store_dir.AppendASCII(additional_file_path);
 
     // Make sure the additional file gets moved.
     files_to_move.emplace_back(
@@ -458,15 +439,6 @@
         FilePathToString(store_add_file_path));
   }
 
-  if (features::IsInstallWideModelStoreEnabled() &&
-      !WriteModelInfoProtoToFile(
-          model.model_info(),
-          base_model_dir.Append(GetBaseFileNameForModelInfo()))) {
-    RecordPredictionModelDownloadStatus(
-        PredictionModelDownloadStatus::kFailedModelInfoSaving);
-    return absl::nullopt;
-  }
-
   PredictionModelDownloadStatus status =
       PredictionModelDownloadStatus::kSuccess;
   for (const auto& move_file : files_to_move) {
@@ -498,7 +470,6 @@
 
 void PredictionModelDownloadManager::NotifyModelReady(
     absl::optional<proto::OptimizationTarget> optimization_target,
-    const base::FilePath& base_model_dir,
     const absl::optional<proto::PredictionModel>& model) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -510,7 +481,7 @@
   }
 
   for (PredictionModelDownloadObserver& observer : observers_)
-    observer.OnModelReady(base_model_dir, *model);
+    observer.OnModelReady(*model);
 }
 
 void PredictionModelDownloadManager::NotifyModelDownloadFailed(
diff --git a/components/optimization_guide/core/prediction_model_download_manager.h b/components/optimization_guide/core/prediction_model_download_manager.h
index db39a511..1247cc9 100644
--- a/components/optimization_guide/core/prediction_model_download_manager.h
+++ b/components/optimization_guide/core/prediction_model_download_manager.h
@@ -14,7 +14,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "components/download/public/background_service/download_params.h"
-#include "components/optimization_guide/core/prediction_model_store.h"
 #include "components/optimization_guide/proto/models.pb.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
@@ -26,7 +25,6 @@
 
 class PredictionModelDownloadClient;
 class PredictionModelDownloadObserver;
-class PredictionModelStore;
 
 namespace proto {
 class PredictionModel;
@@ -53,8 +51,6 @@
   PredictionModelDownloadManager(
       download::BackgroundDownloadService* download_service,
       const base::FilePath& models_dir_path,
-      PredictionModelStore* prediction_model_store,
-      const proto::ModelCacheKey& model_cache_key,
       scoped_refptr<base::SequencedTaskRunner> background_task_runner);
   virtual ~PredictionModelDownloadManager();
   PredictionModelDownloadManager(const PredictionModelDownloadManager&) =
@@ -153,7 +149,6 @@
   // Must be invoked on the UI thread.
   void NotifyModelReady(
       absl::optional<proto::OptimizationTarget> optimization_target,
-      const base::FilePath& base_model_dir,
       const absl::optional<proto::PredictionModel>& model);
 
   // Notifies |observers_| that a model download failed for
@@ -183,12 +178,6 @@
   // The path to the dir containing models.
   base::FilePath models_dir_path_;
 
-  // The optimization guide model store. Not owned. Should outlive |this|.
-  raw_ptr<PredictionModelStore> prediction_model_store_;
-
-  // The ModelCacheKey that the user profile for |this| is associated with.
-  const proto::ModelCacheKey model_cache_key_;
-
   // Background thread where download file processing should be performed.
   scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
 
diff --git a/components/optimization_guide/core/prediction_model_download_manager_unittest.cc b/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
index c60b2a3e..1eb39fa3 100644
--- a/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
+++ b/components/optimization_guide/core/prediction_model_download_manager_unittest.cc
@@ -42,8 +42,7 @@
   TestPredictionModelDownloadObserver() = default;
   ~TestPredictionModelDownloadObserver() override = default;
 
-  void OnModelReady(const base::FilePath& base_model_dir,
-                    const proto::PredictionModel& model) override {
+  void OnModelReady(const proto::PredictionModel& model) override {
     last_ready_model_ = model;
   }
 
@@ -82,7 +81,6 @@
         std::make_unique<download::test::MockDownloadService>();
     download_manager_ = std::make_unique<PredictionModelDownloadManager>(
         mock_download_service_.get(), temp_models_dir_.GetPath(),
-        /*prediction_model_store=*/nullptr, proto::ModelCacheKey(),
         task_environment_.GetMainThreadTaskRunner());
 
 #if !BUILDFLAG(IS_IOS)
diff --git a/components/optimization_guide/core/prediction_model_download_observer.h b/components/optimization_guide/core/prediction_model_download_observer.h
index 0c16e78..4c6468d1 100644
--- a/components/optimization_guide/core/prediction_model_download_observer.h
+++ b/components/optimization_guide/core/prediction_model_download_observer.h
@@ -14,10 +14,8 @@
 // completed.
 class PredictionModelDownloadObserver : public base::CheckedObserver {
  public:
-  // Invoked when a model has been downloaded and verified. |base_model_dir| is
-  // the base dir where model files are available.
-  virtual void OnModelReady(const base::FilePath& base_model_dir,
-                            const proto::PredictionModel& model) = 0;
+  // Invoked when a model has been downloaded and verified.
+  virtual void OnModelReady(const proto::PredictionModel& model) = 0;
 
   // Invoked when a model download started.
   virtual void OnModelDownloadStarted(
diff --git a/components/optimization_guide/core/prediction_model_store.cc b/components/optimization_guide/core/prediction_model_store.cc
index 69f548a..e2a576fb 100644
--- a/components/optimization_guide/core/prediction_model_store.cc
+++ b/components/optimization_guide/core/prediction_model_store.cc
@@ -6,7 +6,6 @@
 
 #include "base/files/file_util.h"
 #include "base/guid.h"
-#include "base/memory/ptr_util.h"
 #include "base/rand_util.h"
 #include "base/task/thread_pool.h"
 #include "components/optimization_guide/core/model_store_metadata_entry.h"
@@ -14,6 +13,7 @@
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_prefs.h"
 #include "components/prefs/pref_service.h"
+#include "model_store_metadata_entry.h"
 
 namespace optimization_guide {
 
@@ -57,44 +57,19 @@
 
 }  // namespace
 
-// static
-PredictionModelStore* PredictionModelStore::GetInstance() {
-  static base::NoDestructor<PredictionModelStore> model_store;
-  return model_store.get();
-}
-
-PredictionModelStore::PredictionModelStore()
-    : background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
+PredictionModelStore::PredictionModelStore(PrefService* local_state,
+                                           const base::FilePath& base_store_dir)
+    : local_state_(local_state),
+      base_store_dir_(base_store_dir),
+      background_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT})) {
   DCHECK(optimization_guide::features::IsInstallWideModelStoreEnabled());
+  DCHECK(local_state);
+  DCHECK(!base_store_dir.empty());
 }
 
 PredictionModelStore::~PredictionModelStore() = default;
 
-void PredictionModelStore::Initialize(PrefService* local_state,
-                                      const base::FilePath& base_store_dir) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(local_state);
-  DCHECK(!base_store_dir.empty());
-
-  // Should not be initialized already.
-  DCHECK(!local_state_);
-  DCHECK(base_store_dir_.empty());
-
-  local_state_ = local_state;
-  base_store_dir_ = base_store_dir;
-}
-
-// static
-std::unique_ptr<PredictionModelStore>
-PredictionModelStore::CreatePredictionModelStoreForTesting(
-    PrefService* local_state,
-    const base::FilePath& base_store_dir) {
-  auto store = base::WrapUnique(new PredictionModelStore());
-  store->Initialize(local_state, base_store_dir);
-  return store;
-}
-
 bool PredictionModelStore::HasModel(
     proto::OptimizationTarget optimization_target,
     const proto::ModelCacheKey& model_cache_key) const {
@@ -180,36 +155,6 @@
   std::move(callback).Run(std::move(model));
 }
 
-void PredictionModelStore::UpdateMetadataForExistingModel(
-    proto::OptimizationTarget optimization_target,
-    const proto::ModelCacheKey& model_cache_key,
-    const proto::ModelInfo& model_info) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(model_info.has_version());
-  DCHECK_EQ(optimization_target, model_info.optimization_target());
-
-  if (!HasModel(optimization_target, model_cache_key))
-    return;
-
-  ModelStoreMetadataEntryUpdater metadata(local_state_, optimization_target,
-                                          model_cache_key);
-  auto base_model_dir = metadata.GetModelBaseDir();
-  DCHECK(base_store_dir_.IsParent(*base_model_dir));
-  if (model_info.has_valid_duration()) {
-    metadata.SetExpiryTime(
-        base::Time::Now() +
-        base::Seconds(model_info.valid_duration().seconds()));
-  }
-  metadata.SetKeepBeyondValidDuration(model_info.keep_beyond_valid_duration());
-  background_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&CheckAllPathsExist,
-                     GetModelFilePaths(model_info, *base_model_dir)),
-      base::BindOnce(&PredictionModelStore::OnModelUpdateVerified,
-                     weak_ptr_factory_.GetWeakPtr(), optimization_target,
-                     model_cache_key, base::DoNothing()));
-}
-
 void PredictionModelStore::UpdateModel(
     proto::OptimizationTarget optimization_target,
     const proto::ModelCacheKey& model_cache_key,
diff --git a/components/optimization_guide/core/prediction_model_store.h b/components/optimization_guide/core/prediction_model_store.h
index eaf8c8df..60599d63 100644
--- a/components/optimization_guide/core/prediction_model_store.h
+++ b/components/optimization_guide/core/prediction_model_store.h
@@ -8,7 +8,6 @@
 #include "base/files/file_path.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
-#include "base/no_destructor.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/values.h"
@@ -33,17 +32,8 @@
   using PredictionModelLoadedCallback =
       base::OnceCallback<void(std::unique_ptr<proto::PredictionModel>)>;
 
-  // Returns the singleton model store.
-  static PredictionModelStore* GetInstance();
-
-  static std::unique_ptr<PredictionModelStore>
-  CreatePredictionModelStoreForTesting(PrefService* local_state,
-                                       const base::FilePath& base_store_dir);
-
-  // Initializes the model store with |local_state| and the |base_store_dir|.
-  // Model store will be usable only after it is initialized.
-  void Initialize(PrefService* local_state,
-                  const base::FilePath& base_store_dir);
+  PredictionModelStore(PrefService* local_state,
+                       const base::FilePath& base_store_dir);
 
   PredictionModelStore(const PredictionModelStore&) = delete;
   PredictionModelStore& operator=(const PredictionModelStore&) = delete;
@@ -65,13 +55,6 @@
                  const proto::ModelCacheKey& model_cache_key,
                  PredictionModelLoadedCallback callback);
 
-  // Update the model metadata for |model_info| if the model represented by
-  // |optimization_target| and |model_cache_key| exists.
-  void UpdateMetadataForExistingModel(
-      proto::OptimizationTarget optimization_target,
-      const proto::ModelCacheKey& model_cache_key,
-      const proto::ModelInfo& model_info);
-
   // Update the model for |model_info| in the store represented by
   // |optimization_target| and |model_cache_key|. The model files are stored in
   // |base_model_dir|. |callback| is invoked on completion.
@@ -89,9 +72,7 @@
       const proto::ModelCacheKey& model_cache_key);
 
  private:
-  friend base::NoDestructor<PredictionModelStore>;
-
-  PredictionModelStore();
+  friend class PredictionModelStoreTest;
 
   // Loads the model and verifies if the model files exist and returns the
   // model. Otherwise nullptr is returned on any failures.
diff --git a/components/optimization_guide/core/prediction_model_store_unittest.cc b/components/optimization_guide/core/prediction_model_store_unittest.cc
index 227847fe..4d2828e 100644
--- a/components/optimization_guide/core/prediction_model_store_unittest.cc
+++ b/components/optimization_guide/core/prediction_model_store_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "components/optimization_guide/core/prediction_model_store.h"
+#include <memory>
 
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
@@ -58,9 +59,8 @@
     ASSERT_TRUE(temp_models_dir_.CreateUniqueTempDir());
     local_state_prefs_ = std::make_unique<TestingPrefServiceSimple>();
     prefs::RegisterLocalStatePrefs(local_state_prefs_->registry());
-    prediction_model_store_ =
-        PredictionModelStore::CreatePredictionModelStoreForTesting(
-            local_state_prefs_.get(), temp_models_dir_.GetPath());
+    prediction_model_store_ = std::make_unique<PredictionModelStore>(
+        local_state_prefs_.get(), temp_models_dir_.GetPath());
   }
 
   void OnPredictionModelLoaded(
@@ -206,42 +206,4 @@
   EXPECT_FALSE(last_loaded_prediction_model());
 }
 
-TEST_F(PredictionModelStoreTest, UpdateMetadataForExistingModel) {
-  auto model_cache_key = CreateModelCacheKey(kTestLocaleFoo);
-  auto model_detail =
-      CreateTestModelFiles(kTestOptimizationTargetFoo, model_cache_key, {});
-  prediction_model_store_->UpdateModel(
-      kTestOptimizationTargetFoo, model_cache_key, model_detail.model_info,
-      model_detail.base_model_dir, base::DoNothing());
-  RunUntilIdle();
-
-  EXPECT_TRUE(prediction_model_store_->HasModel(kTestOptimizationTargetFoo,
-                                                model_cache_key));
-  prediction_model_store_->LoadModel(
-      kTestOptimizationTargetFoo, model_cache_key,
-      base::BindOnce(&PredictionModelStoreTest::OnPredictionModelLoaded,
-                     base::Unretained(this)));
-  RunUntilIdle();
-  proto::PredictionModel* loaded_model = last_loaded_prediction_model();
-  EXPECT_TRUE(loaded_model);
-  EXPECT_EQ(kTestOptimizationTargetFoo,
-            loaded_model->model_info().optimization_target());
-  EXPECT_FALSE(loaded_model->model_info().keep_beyond_valid_duration());
-
-  proto::ModelInfo model_info;
-  model_info.set_optimization_target(kTestOptimizationTargetFoo);
-  model_info.set_version(1);
-  model_info.mutable_valid_duration()->set_seconds(
-      base::Minutes(100).InSeconds());
-  model_info.set_keep_beyond_valid_duration(true);
-  prediction_model_store_->UpdateMetadataForExistingModel(
-      kTestOptimizationTargetFoo, model_cache_key, model_info);
-  RunUntilIdle();
-  auto metadata_entry = ModelStoreMetadataEntry::GetModelMetadataEntryIfExists(
-      local_state_prefs_.get(), kTestOptimizationTargetFoo, model_cache_key);
-  EXPECT_LE(base::Minutes(99),
-            metadata_entry->GetExpiryTime() - base::Time::Now());
-  EXPECT_TRUE(metadata_entry->GetKeepBeyondValidDuration());
-}
-
 }  // namespace optimization_guide
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn
index 5368c3a9..eb8cfb85 100644
--- a/components/password_manager/core/browser/BUILD.gn
+++ b/components/password_manager/core/browser/BUILD.gn
@@ -39,6 +39,8 @@
     "affiliation/affiliation_service.h",
     "affiliation/affiliation_service_impl.cc",
     "affiliation/affiliation_service_impl.h",
+    "affiliation/affiliations_prefetcher.cc",
+    "affiliation/affiliations_prefetcher.h",
     "affiliation/asset_link_data.cc",
     "affiliation/asset_link_data.h",
     "affiliation/facet_manager.cc",
@@ -633,6 +635,7 @@
     "affiliation/affiliation_fetch_throttler_unittest.cc",
     "affiliation/affiliation_fetcher_factory_impl_unittest.cc",
     "affiliation/affiliation_service_impl_unittest.cc",
+    "affiliation/affiliations_prefetcher_unittest.cc",
     "affiliation/asset_link_data_unittest.cc",
     "affiliation/facet_manager_unittest.cc",
     "affiliation/hash_affiliation_fetcher_unittest.cc",
diff --git a/components/password_manager/core/browser/affiliation/affiliated_match_helper.cc b/components/password_manager/core/browser/affiliation/affiliated_match_helper.cc
index d941368..794b225 100644
--- a/components/password_manager/core/browser/affiliation/affiliated_match_helper.cc
+++ b/components/password_manager/core/browser/affiliation/affiliated_match_helper.cc
@@ -5,49 +5,30 @@
 #include "components/password_manager/core/browser/affiliation/affiliated_match_helper.h"
 
 #include <algorithm>
+#include <memory>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/task/sequenced_task_runner.h"
+#include "components/password_manager/core/browser/affiliation/affiliation_service.h"
+#include "components/password_manager/core/browser/affiliation/affiliations_prefetcher.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 
 namespace password_manager {
 
-namespace {
-
-bool IsFacetValidForAffiliation(const FacetURI& facet) {
-  return facet.IsValidAndroidFacetURI() ||
-         (facet.IsValidWebFacetURI() &&
-          (base::FeatureList::IsEnabled(
-               features::kFillingAcrossAffiliatedWebsites) ||
-           base::FeatureList::IsEnabled(features::kPasswordsGrouping)));
-}
-
-}  // namespace
-
-// static
-constexpr base::TimeDelta AffiliatedMatchHelper::kInitializationDelayOnStartup;
-
 AffiliatedMatchHelper::AffiliatedMatchHelper(
     AffiliationService* affiliation_service)
-    : affiliation_service_(affiliation_service) {}
+    : affiliation_service_(affiliation_service),
+      affiliations_prefetcher_(std::make_unique<AffiliationsPrefetcher>()) {}
 
-AffiliatedMatchHelper::~AffiliatedMatchHelper() {
-  if (password_store_)
-    password_store_->RemoveObserver(this);
-}
+AffiliatedMatchHelper::~AffiliatedMatchHelper() = default;
 
 void AffiliatedMatchHelper::Initialize(PasswordStoreInterface* password_store) {
   DCHECK(password_store);
   DCHECK(affiliation_service_);
-  password_store_ = password_store;
-  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
-      FROM_HERE,
-      base::BindOnce(&AffiliatedMatchHelper::DoDeferredInitialization,
-                     weak_ptr_factory_.GetWeakPtr()),
-      kInitializationDelayOnStartup);
+  affiliations_prefetcher_->Init(affiliation_service_, password_store);
 }
 
 void AffiliatedMatchHelper::GetAffiliatedAndroidAndWebRealms(
@@ -75,13 +56,6 @@
          facet_uri.IsValidWebFacetURI();
 }
 
-void AffiliatedMatchHelper::DoDeferredInitialization() {
-  // Must start observing for changes at the same time as when the snapshot is
-  // taken to avoid inconsistencies due to any changes taking place in-between.
-  password_store_->AddObserver(this);
-  password_store_->GetAllLogins(weak_ptr_factory_.GetWeakPtr());
-}
-
 void AffiliatedMatchHelper::CompleteGetAffiliatedAndroidAndWebRealms(
     const FacetURI& original_facet_uri,
     AffiliatedRealmsCallback result_callback,
@@ -112,67 +86,4 @@
   std::move(result_callback).Run(affiliated_realms);
 }
 
-void AffiliatedMatchHelper::OnLoginsChanged(
-    PasswordStoreInterface* /*store*/,
-    const PasswordStoreChangeList& changes) {
-  std::vector<FacetURI> facet_uris_to_trim;
-  for (const PasswordStoreChange& change : changes) {
-    FacetURI facet_uri =
-        FacetURI::FromPotentiallyInvalidSpec(change.form().signon_realm);
-
-    if (!facet_uri.is_valid())
-      continue;
-    // Require a valid Android Facet if filling across affiliated websites is
-    // disabled.
-    if (!facet_uri.IsValidAndroidFacetURI() &&
-        !base::FeatureList::IsEnabled(
-            features::kFillingAcrossAffiliatedWebsites)) {
-      continue;
-    }
-
-    if (change.type() == PasswordStoreChange::ADD) {
-      affiliation_service_->Prefetch(facet_uri, base::Time::Max());
-    } else if (change.type() == PasswordStoreChange::REMOVE) {
-      // Stop keeping affiliation information fresh for deleted Android logins,
-      // and make a note to potentially remove any unneeded cached data later.
-      facet_uris_to_trim.push_back(facet_uri);
-      affiliation_service_->CancelPrefetch(facet_uri, base::Time::Max());
-    }
-  }
-
-  // When the primary key for a login is updated, |changes| will contain both a
-  // REMOVE and ADD change for that login. Cached affiliation data should not be
-  // deleted in this case. A simple solution is to call TrimCacheForFacetURI()
-  // always after Prefetch() calls -- the trimming logic will detect that there
-  // is an active prefetch and not delete the corresponding data.
-  for (const FacetURI& facet_uri : facet_uris_to_trim)
-    affiliation_service_->TrimCacheForFacetURI(facet_uri);
-}
-
-void AffiliatedMatchHelper::OnLoginsRetained(
-    PasswordStoreInterface* /*store*/,
-    const std::vector<PasswordForm>& retained_passwords) {
-  std::vector<FacetURI> facets;
-  for (const auto& form : retained_passwords) {
-    FacetURI facet_uri =
-        FacetURI::FromPotentiallyInvalidSpec(form.signon_realm);
-    if (IsFacetValidForAffiliation(facet_uri))
-      facets.push_back(std::move(facet_uri));
-  }
-  affiliation_service_->KeepPrefetchForFacets(std::move(facets));
-}
-
-void AffiliatedMatchHelper::OnGetPasswordStoreResults(
-    std::vector<std::unique_ptr<PasswordForm>> results) {
-  std::vector<FacetURI> facets;
-  for (const auto& form : results) {
-    FacetURI facet_uri =
-        FacetURI::FromPotentiallyInvalidSpec(form->signon_realm);
-    if (IsFacetValidForAffiliation(facet_uri))
-      facets.push_back(std::move(facet_uri));
-  }
-  affiliation_service_->KeepPrefetchForFacets(facets);
-  affiliation_service_->TrimUnusedCache(std::move(facets));
-}
-
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/affiliation/affiliated_match_helper.h b/components/password_manager/core/browser/affiliation/affiliated_match_helper.h
index f08d662..f89c132e 100644
--- a/components/password_manager/core/browser/affiliation/affiliated_match_helper.h
+++ b/components/password_manager/core/browser/affiliation/affiliated_match_helper.h
@@ -14,12 +14,13 @@
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "components/password_manager/core/browser/affiliation/affiliation_utils.h"
-#include "components/password_manager/core/browser/password_store_consumer.h"
 #include "components/password_manager/core/browser/password_store_interface.h"
-#include "components/password_manager/core/browser/affiliation/affiliation_service.h"
 
 namespace password_manager {
 
+class AffiliationService;
+class AffiliationsPrefetcher;
+class PasswordStoreInterface;
 struct PasswordForm;
 
 // Interacts with the AffiliationService on behalf of the PasswordStore.
@@ -31,13 +32,7 @@
 // achieved by implementing the "proactive fetching" strategy for interacting
 // with the AffiliationService (see affiliation_service.h for details), with
 // Android applications and web realms playing the role of facet Y.
-//
-// More specifically, this class prefetches affiliation information on start-up
-// for all credentials stored in the PasswordStore. Then, the actual GetLogins()
-// can be restricted to the cache, so that realms of the observed web forms will
-// never be looked up against the Affiliation API.
-class AffiliatedMatchHelper : public PasswordStoreInterface::Observer,
-                              public PasswordStoreConsumer {
+class AffiliatedMatchHelper {
  public:
   // Callback to returns the list of affiliated signon_realms (as per defined in
   // PasswordForm) to the caller.
@@ -49,7 +44,7 @@
   explicit AffiliatedMatchHelper(AffiliationService* affiliation_service);
   AffiliatedMatchHelper(const AffiliatedMatchHelper&) = delete;
   AffiliatedMatchHelper& operator=(const AffiliatedMatchHelper&) = delete;
-  ~AffiliatedMatchHelper() override;
+  virtual ~AffiliatedMatchHelper();
 
   // Schedules deferred initialization.
   void Initialize(PasswordStoreInterface* password_store);
@@ -66,14 +61,6 @@
   // purposes of affiliation-based matching.
   static bool IsValidWebCredential(const PasswordFormDigest& form);
 
-  // I/O heavy initialization on start-up will be delayed by this long.
-  // This should be high enough not to exacerbate start-up I/O contention too
-  // much, but also low enough that the user be able log-in shortly after
-  // browser start-up into web sites using Android credentials.
-  // TODO(engedy): See if we can tie this instead to some meaningful event.
-  static constexpr base::TimeDelta kInitializationDelayOnStartup =
-      base::Seconds(30);
-
   AffiliationService* get_affiliation_service() { return affiliation_service_; }
 
  private:
@@ -90,21 +77,10 @@
       const AffiliatedFacets& results,
       bool success);
 
-  // PasswordStoreInterface::Observer:
-  void OnLoginsChanged(PasswordStoreInterface* store,
-                       const PasswordStoreChangeList& changes) override;
-  void OnLoginsRetained(
-      PasswordStoreInterface* store,
-      const std::vector<PasswordForm>& retained_passwords) override;
-
-  // PasswordStoreConsumer:
-  void OnGetPasswordStoreResults(
-      std::vector<std::unique_ptr<PasswordForm>> results) override;
-
-  raw_ptr<PasswordStoreInterface> password_store_ = nullptr;
-
   raw_ptr<AffiliationService, DanglingUntriaged> affiliation_service_;
 
+  std::unique_ptr<AffiliationsPrefetcher> affiliations_prefetcher_;
+
   base::WeakPtrFactory<AffiliatedMatchHelper> weak_ptr_factory_{this};
 };
 
diff --git a/components/password_manager/core/browser/affiliation/affiliated_match_helper_unittest.cc b/components/password_manager/core/browser/affiliation/affiliated_match_helper_unittest.cc
index 86c410c..8216179 100644
--- a/components/password_manager/core/browser/affiliation/affiliated_match_helper_unittest.cc
+++ b/components/password_manager/core/browser/affiliation/affiliated_match_helper_unittest.cc
@@ -20,9 +20,7 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "components/password_manager/core/browser/affiliation/affiliation_utils.h"
-#include "components/password_manager/core/browser/affiliation/affiliation_service_impl.h"
 #include "components/password_manager/core/browser/affiliation/mock_affiliation_service.h"
-#include "components/password_manager/core/browser/test_password_store.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "services/network/test/test_shared_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -126,11 +124,6 @@
 const char kTestAndroidRealmBeta3[] =
     "android://hash@com.yetanother.beta.android/";
 
-const char kTestAndroidFacetURIGamma[] =
-    "android://hash@com.example.gamma.android";
-const char kTestAndroidRealmGamma[] =
-    "android://hash@com.example.gamma.android";
-
 const char16_t kTestUsername[] = u"JohnDoe";
 const char16_t kTestPassword[] = u"secret";
 
@@ -165,14 +158,6 @@
   return form;
 }
 
-PasswordForm GetTestBlocklistedAndroidCredentials(const char* signon_realm) {
-  PasswordForm form = GetTestAndroidCredentials(signon_realm);
-  form.blocked_by_user = true;
-  form.username_value.clear();
-  form.password_value.clear();
-  return form;
-}
-
 PasswordFormDigest GetTestObservedWebForm(const char* signon_realm,
                                           const char* origin) {
   return {PasswordForm::Scheme::kHtml, signon_realm,
@@ -190,20 +175,6 @@
   }
 
  protected:
-  void RunDeferredInitialization() {
-    mock_time_task_runner_->RunUntilIdle();
-    ASSERT_EQ(AffiliatedMatchHelper::kInitializationDelayOnStartup,
-              mock_time_task_runner_->NextPendingTaskDelay());
-    mock_affiliation_service()->ExpectCallToTrimUnusedCache();
-    mock_time_task_runner_->FastForwardBy(
-        AffiliatedMatchHelper::kInitializationDelayOnStartup);
-  }
-
-  void ExpectNoDeferredTasks() {
-    mock_time_task_runner_->RunUntilIdle();
-    ASSERT_FALSE(mock_time_task_runner_->HasPendingTask());
-  }
-
   void RunUntilIdle() {
     // TODO(gab): Add support for base::RunLoop().RunUntilIdle() in scope of
     // ScopedMockTimeMessageLoopTaskRunner and use it instead of this helper
@@ -211,117 +182,6 @@
     mock_time_task_runner_->RunUntilIdle();
   }
 
-  void AddLoginAndWait(const PasswordForm& form) {
-    password_store_->AddLogin(form);
-    RunUntilIdle();
-  }
-
-  void UpdateLoginWithPrimaryKey(const PasswordForm& new_form,
-                                 const PasswordForm& old_primary_key) {
-    password_store_->UpdateLoginWithPrimaryKey(new_form, old_primary_key);
-    RunUntilIdle();
-  }
-
-  void RemoveLogin(const PasswordForm& form) {
-    password_store_->RemoveLogin(form);
-    RunUntilIdle();
-  }
-
-  void AddAndroidAndNonAndroidTestLogins() {
-    AddLoginAndWait(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
-    AddLoginAndWait(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
-    AddLoginAndWait(
-        GetTestBlocklistedAndroidCredentials(kTestAndroidRealmBeta3));
-    AddLoginAndWait(GetTestAndroidCredentials(kTestAndroidRealmGamma));
-
-    AddLoginAndWait(GetTestAndroidCredentials(kTestWebRealmAlpha1));
-    AddLoginAndWait(GetTestAndroidCredentials(kTestWebRealmAlpha2));
-  }
-
-  void RemoveAndroidAndNonAndroidTestLogins() {
-    RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
-    RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
-    RemoveLogin(GetTestBlocklistedAndroidCredentials(kTestAndroidRealmBeta3));
-    RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma));
-
-    RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1));
-    RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2));
-  }
-
-  void ExpectPrefetchForTestLogins() {
-    mock_affiliation_service()->ExpectCallToPrefetch(
-        kTestAndroidFacetURIAlpha3);
-    mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta2);
-    mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta3);
-    mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIGamma);
-
-    if (base::FeatureList::IsEnabled(
-            features::kFillingAcrossAffiliatedWebsites)) {
-      mock_affiliation_service()->ExpectCallToPrefetch(kTestWebFacetURIAlpha1);
-      mock_affiliation_service()->ExpectCallToPrefetch(kTestWebFacetURIAlpha2);
-    }
-  }
-
-  void ExpectKeepPrefetchForTestLogins() {
-    std::vector<FacetURI> expected_facets;
-    expected_facets.push_back(
-        FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3));
-    expected_facets.push_back(
-        FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2));
-    expected_facets.push_back(
-        FacetURI::FromCanonicalSpec(kTestAndroidFacetURIGamma));
-    expected_facets.push_back(
-        FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3));
-
-    if (base::FeatureList::IsEnabled(
-            features::kFillingAcrossAffiliatedWebsites)) {
-      expected_facets.push_back(
-          FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1));
-      expected_facets.push_back(
-          FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha2));
-    }
-
-    mock_affiliation_service()->ExpectKeepPrefetchForFacets(expected_facets);
-  }
-
-  void ExpectCancelPrefetchForTestLogins() {
-    mock_affiliation_service()->ExpectCallToCancelPrefetch(
-        kTestAndroidFacetURIAlpha3);
-    mock_affiliation_service()->ExpectCallToCancelPrefetch(
-        kTestAndroidFacetURIBeta2);
-    mock_affiliation_service()->ExpectCallToCancelPrefetch(
-        kTestAndroidFacetURIBeta3);
-    mock_affiliation_service()->ExpectCallToCancelPrefetch(
-        kTestAndroidFacetURIGamma);
-
-    if (base::FeatureList::IsEnabled(
-            features::kFillingAcrossAffiliatedWebsites)) {
-      mock_affiliation_service()->ExpectCallToCancelPrefetch(
-          kTestWebFacetURIAlpha1);
-      mock_affiliation_service()->ExpectCallToCancelPrefetch(
-          kTestWebFacetURIAlpha2);
-    }
-  }
-
-  void ExpectTrimCacheForTestLogins() {
-    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
-        kTestAndroidFacetURIAlpha3);
-    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
-        kTestAndroidFacetURIBeta2);
-    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
-        kTestAndroidFacetURIBeta3);
-    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
-        kTestAndroidFacetURIGamma);
-
-    if (base::FeatureList::IsEnabled(
-            features::kFillingAcrossAffiliatedWebsites)) {
-      mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
-          kTestWebFacetURIAlpha1);
-      mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
-          kTestWebFacetURIAlpha2);
-    }
-  }
-
   std::vector<std::string> GetAffiliatedAndroidAndWebRealms(
       const PasswordFormDigest& observed_form) {
     expecting_result_callback_ = true;
@@ -334,18 +194,11 @@
     return last_result_realms_;
   }
 
-  void DestroyPasswordStore() {
-    password_store_->ShutdownOnUIThread();
-    password_store_ = nullptr;
-  }
-
-  TestPasswordStore* password_store() { return password_store_.get(); }
-
   OverloadedMockAffiliationService* mock_affiliation_service() {
     return mock_affiliation_service_.get();
   }
 
-  AffiliatedMatchHelper* match_helper() { return match_helper_; }
+  AffiliatedMatchHelper* match_helper() { return match_helper_.get(); }
 
  private:
   void OnAffiliatedRealmsCallback(
@@ -359,16 +212,11 @@
   void SetUp() override {
     mock_affiliation_service_ = std::make_unique<
         testing::StrictMock<OverloadedMockAffiliationService>>();
-    auto match_helper =
+    match_helper_ =
         std::make_unique<AffiliatedMatchHelper>(mock_affiliation_service());
-    match_helper_ = match_helper.get();
-    // Initializing PasswordStore initializes AffiliatedMatchHelper.
-    password_store()->Init(/*prefs=*/nullptr, std::move(match_helper));
   }
 
   void TearDown() override {
-    if (password_store_)
-      DestroyPasswordStore();
     mock_affiliation_service_.reset();
     // Clean up on the background thread.
     RunUntilIdle();
@@ -381,9 +229,7 @@
   std::vector<std::string> last_result_realms_;
   bool expecting_result_callback_ = false;
 
-  scoped_refptr<TestPasswordStore> password_store_ =
-      base::MakeRefCounted<TestPasswordStore>();
-  raw_ptr<AffiliatedMatchHelper> match_helper_;
+  std::unique_ptr<AffiliatedMatchHelper> match_helper_;
 
   std::unique_ptr<OverloadedMockAffiliationService> mock_affiliation_service_;
 };
@@ -456,146 +302,6 @@
               testing::IsEmpty());
 }
 
-// Verifies that affiliations for Android applications with pre-existing
-// credentials on start-up are prefetched.
-TEST_P(
-    AffiliatedMatchHelperTest,
-    PrefetchAffiliationsAndBrandingForPreexistingAndroidCredentialsOnStartup) {
-  AddAndroidAndNonAndroidTestLogins();
-  RunUntilIdle();
-
-  ExpectKeepPrefetchForTestLogins();
-  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-}
-
-// Stores credentials for Android applications between Initialize() and
-// DoDeferredInitialization(). Verifies that corresponding affiliation
-// information gets prefetched.
-TEST_P(AffiliatedMatchHelperTest,
-       PrefetchAffiliationsForAndroidCredentialsAddedInInitializationDelay) {
-  // Wait until PasswordStore initialisation is complete and
-  // AffiliatedMatchHelper::Initialize is called.
-  RunUntilIdle();
-
-  AddAndroidAndNonAndroidTestLogins();
-
-  ExpectKeepPrefetchForTestLogins();
-  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-}
-
-// Stores credentials for Android applications after DoDeferredInitialization().
-// Verifies that corresponding affiliation information gets prefetched.
-TEST_P(AffiliatedMatchHelperTest,
-       PrefetchAffiliationsForAndroidCredentialsAddedAfterInitialization) {
-  mock_affiliation_service()->ExpectKeepPrefetchForFacets({});
-  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-
-  ExpectPrefetchForTestLogins();
-  AddAndroidAndNonAndroidTestLogins();
-}
-
-TEST_P(AffiliatedMatchHelperTest,
-       CancelPrefetchingAffiliationsAndBrandingForRemovedAndroidCredentials) {
-  AddAndroidAndNonAndroidTestLogins();
-
-  ExpectKeepPrefetchForTestLogins();
-  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-
-  ExpectCancelPrefetchForTestLogins();
-  ExpectTrimCacheForTestLogins();
-
-  RemoveAndroidAndNonAndroidTestLogins();
-}
-
-// Verify that whenever the primary key is updated for a credential (in which
-// case both REMOVE and ADD change notifications are sent out), then Prefetch()
-// is called in response to the addition before the call to
-// TrimCacheForFacetURI() in response to the removal, so that cached data is not
-// deleted and then immediately re-fetched.
-TEST_P(AffiliatedMatchHelperTest, PrefetchBeforeTrimForPrimaryKeyUpdates) {
-  AddAndroidAndNonAndroidTestLogins();
-
-  ExpectKeepPrefetchForTestLogins();
-  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-
-  mock_affiliation_service()->ExpectCallToCancelPrefetch(
-      kTestAndroidFacetURIAlpha3);
-
-  {
-    testing::InSequence in_sequence;
-    mock_affiliation_service()->ExpectCallToPrefetch(
-        kTestAndroidFacetURIAlpha3);
-    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
-        kTestAndroidFacetURIAlpha3);
-  }
-
-  PasswordForm old_form(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
-  PasswordForm new_form(old_form);
-  new_form.username_value = u"NewUserName";
-  UpdateLoginWithPrimaryKey(new_form, old_form);
-}
-
-// Stores and removes four credentials for the same an Android application, and
-// expects that Prefetch() and CancelPrefetch() will each be called four times.
-TEST_P(AffiliatedMatchHelperTest,
-       DuplicateCredentialsArePrefetchWithMultiplicity) {
-  mock_affiliation_service()->ExpectKeepPrefetchForFacets(
-      {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
-       FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
-       FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3)});
-
-  PasswordForm android_form(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
-  password_store()->AddLogin(android_form);
-
-  // Store two credentials before initialization.
-  PasswordForm android_form2(android_form);
-  android_form2.username_value = u"JohnDoe2";
-  password_store()->AddLogin(android_form2);
-
-  // Wait until PasswordStore initializes AffiliatedMatchHelper and processes
-  // added logins.
-  RunUntilIdle();
-
-  // Store one credential between initialization and deferred initialization.
-  PasswordForm android_form3(android_form);
-  android_form3.username_value = u"JohnDoe3";
-  password_store()->AddLogin(android_form3);
-
-  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
-
-  EXPECT_CALL(*mock_affiliation_service(),
-              Prefetch(FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
-                       base::Time::Max()))
-      .Times(1);
-
-  // Store one credential after deferred initialization.
-  PasswordForm android_form4(android_form);
-  android_form4.username_value = u"JohnDoe4";
-  AddLoginAndWait(android_form4);
-
-  for (size_t i = 0; i < 4; ++i) {
-    mock_affiliation_service()->ExpectCallToCancelPrefetch(
-        kTestAndroidFacetURIAlpha3);
-    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
-        kTestAndroidFacetURIAlpha3);
-  }
-
-  RemoveLogin(android_form);
-  RemoveLogin(android_form2);
-  RemoveLogin(android_form3);
-  RemoveLogin(android_form4);
-}
-
-TEST_P(AffiliatedMatchHelperTest, DestroyBeforeDeferredInitialization) {
-  // Wait until PasswordStore initialisation is complete and
-  // AffiliatedMatchHelper::Initialize is called.
-  RunUntilIdle();
-
-  // Destroy PasswordStore to destroy AffiliatedMatchHelper.
-  DestroyPasswordStore();
-  ASSERT_NO_FATAL_FAILURE(ExpectNoDeferredTasks());
-}
-
 TEST_P(AffiliatedMatchHelperTest, GetAffiliatedAndroidRealmsAndWebsites) {
   // Disable this test when filling across affiliated websites disabled.
   if (!base::FeatureList::IsEnabled(features::kFillingAcrossAffiliatedWebsites))
@@ -611,26 +317,6 @@
                                             kTestAndroidRealmAlpha3));
 }
 
-TEST_P(AffiliatedMatchHelperTest, OnLoginsRetained) {
-  std::vector<PasswordForm> forms = {
-      GetTestAndroidCredentials(kTestWebFacetURIAlpha1),
-      GetTestAndroidCredentials(kTestAndroidFacetURIBeta2)};
-  std::vector<FacetURI> expected_facets;
-
-  if (base::FeatureList::IsEnabled(
-          features::kFillingAcrossAffiliatedWebsites)) {
-    expected_facets = {FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1),
-                       FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2)};
-  } else {
-    expected_facets = {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2)};
-  }
-
-  mock_affiliation_service()->ExpectKeepPrefetchForFacets(expected_facets);
-
-  (static_cast<PasswordStoreInterface::Observer*>(match_helper()))
-      ->OnLoginsRetained(nullptr, forms);
-}
-
 INSTANTIATE_TEST_SUITE_P(FillingAcrossAffiliatedWebsites,
                          AffiliatedMatchHelperTest,
                          ::testing::Bool());
diff --git a/components/password_manager/core/browser/affiliation/affiliations_prefetcher.cc b/components/password_manager/core/browser/affiliation/affiliations_prefetcher.cc
new file mode 100644
index 0000000..7902a3d
--- /dev/null
+++ b/components/password_manager/core/browser/affiliation/affiliations_prefetcher.cc
@@ -0,0 +1,128 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/affiliation/affiliations_prefetcher.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/affiliation/affiliation_service.h"
+#include "components/password_manager/core/browser/affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/password_form.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+
+namespace password_manager {
+
+namespace {
+
+constexpr base::TimeDelta kInitializationDelayOnStartup = base::Seconds(30);
+
+bool IsFacetValidForAffiliation(const FacetURI& facet) {
+  return facet.IsValidAndroidFacetURI() ||
+         (facet.IsValidWebFacetURI() &&
+          (base::FeatureList::IsEnabled(
+               features::kFillingAcrossAffiliatedWebsites) ||
+           base::FeatureList::IsEnabled(features::kPasswordsGrouping)));
+}
+
+}  // namespace
+
+AffiliationsPrefetcher::AffiliationsPrefetcher() = default;
+
+AffiliationsPrefetcher::~AffiliationsPrefetcher() {
+  if (password_store_)
+    password_store_->RemoveObserver(this);
+}
+
+void AffiliationsPrefetcher::Init(AffiliationService* affiliation_service,
+                                  PasswordStoreInterface* password_store) {
+  affiliation_service_ = affiliation_service;
+  password_store_ = password_store;
+
+  // I/O heavy initialization on start-up will be delayed by this long.
+  // This should be high enough not to exacerbate start-up I/O contention too
+  // much, but also low enough that the user be able log-in shortly after
+  // browser start-up into web sites using Android credentials.
+  base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&AffiliationsPrefetcher::DoDeferredInitialization,
+                     weak_ptr_factory_.GetWeakPtr()),
+      kInitializationDelayOnStartup);
+}
+
+void AffiliationsPrefetcher::OnLoginsChanged(
+    PasswordStoreInterface* /*store*/,
+    const PasswordStoreChangeList& changes) {
+  std::vector<FacetURI> facet_uris_to_trim;
+  for (const PasswordStoreChange& change : changes) {
+    FacetURI facet_uri =
+        FacetURI::FromPotentiallyInvalidSpec(change.form().signon_realm);
+
+    if (!facet_uri.is_valid())
+      continue;
+    // Require a valid Android Facet if filling across affiliated websites is
+    // disabled.
+    if (!facet_uri.IsValidAndroidFacetURI() &&
+        !base::FeatureList::IsEnabled(
+            features::kFillingAcrossAffiliatedWebsites)) {
+      continue;
+    }
+
+    if (change.type() == PasswordStoreChange::ADD) {
+      affiliation_service_->Prefetch(facet_uri, base::Time::Max());
+    } else if (change.type() == PasswordStoreChange::REMOVE) {
+      // Stop keeping affiliation information fresh for deleted Android logins,
+      // and make a note to potentially remove any unneeded cached data later.
+      facet_uris_to_trim.push_back(facet_uri);
+      affiliation_service_->CancelPrefetch(facet_uri, base::Time::Max());
+    }
+  }
+
+  // When the primary key for a login is updated, |changes| will contain both a
+  // REMOVE and ADD change for that login. Cached affiliation data should not be
+  // deleted in this case. A simple solution is to call TrimCacheForFacetURI()
+  // always after Prefetch() calls -- the trimming logic will detect that there
+  // is an active prefetch and not delete the corresponding data.
+  for (const FacetURI& facet_uri : facet_uris_to_trim)
+    affiliation_service_->TrimCacheForFacetURI(facet_uri);
+}
+
+void AffiliationsPrefetcher::OnLoginsRetained(
+    PasswordStoreInterface* /*store*/,
+    const std::vector<PasswordForm>& retained_passwords) {
+  std::vector<FacetURI> facets;
+  for (const auto& form : retained_passwords) {
+    FacetURI facet_uri =
+        FacetURI::FromPotentiallyInvalidSpec(form.signon_realm);
+    if (IsFacetValidForAffiliation(facet_uri))
+      facets.push_back(std::move(facet_uri));
+  }
+  affiliation_service_->KeepPrefetchForFacets(std::move(facets));
+}
+
+void AffiliationsPrefetcher::OnGetPasswordStoreResults(
+    std::vector<std::unique_ptr<PasswordForm>> results) {
+  std::vector<FacetURI> facets;
+  for (const auto& form : results) {
+    FacetURI facet_uri =
+        FacetURI::FromPotentiallyInvalidSpec(form->signon_realm);
+    if (IsFacetValidForAffiliation(facet_uri))
+      facets.push_back(std::move(facet_uri));
+  }
+  affiliation_service_->KeepPrefetchForFacets(facets);
+  affiliation_service_->TrimUnusedCache(std::move(facets));
+}
+
+void AffiliationsPrefetcher::DoDeferredInitialization() {
+  // Must start observing for changes at the same time as when the snapshot is
+  // taken to avoid inconsistencies due to any changes taking place in-between.
+  password_store_->AddObserver(this);
+  password_store_->GetAllLogins(weak_ptr_factory_.GetWeakPtr());
+}
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/affiliation/affiliations_prefetcher.h b/components/password_manager/core/browser/affiliation/affiliations_prefetcher.h
new file mode 100644
index 0000000..c952dc9
--- /dev/null
+++ b/components/password_manager/core/browser/affiliation/affiliations_prefetcher.h
@@ -0,0 +1,56 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_AFFILIATIONS_PREFETCHER_H_
+#define COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_AFFILIATIONS_PREFETCHER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+#include "components/password_manager/core/browser/password_store_interface.h"
+
+namespace password_manager {
+
+struct PasswordForm;
+
+class AffiliationService;
+
+// This class prefetches affiliation information on start-up for all credentials
+// stored in a PasswordStore.
+class AffiliationsPrefetcher : public PasswordStoreInterface::Observer,
+                               public PasswordStoreConsumer {
+ public:
+  AffiliationsPrefetcher();
+  ~AffiliationsPrefetcher() override;
+
+  void Init(AffiliationService* affiliation_service,
+            PasswordStoreInterface* password_store);
+
+ private:
+  // PasswordStoreInterface::Observer:
+  void OnLoginsChanged(PasswordStoreInterface* store,
+                       const PasswordStoreChangeList& changes) override;
+  void OnLoginsRetained(
+      PasswordStoreInterface* store,
+      const std::vector<PasswordForm>& retained_passwords) override;
+
+  // PasswordStoreConsumer:
+  void OnGetPasswordStoreResults(
+      std::vector<std::unique_ptr<PasswordForm>> results) override;
+
+  void DoDeferredInitialization();
+
+  raw_ptr<AffiliationService> affiliation_service_ = nullptr;
+
+  raw_ptr<PasswordStoreInterface> password_store_ = nullptr;
+
+  base::WeakPtrFactory<AffiliationsPrefetcher> weak_ptr_factory_{this};
+};
+
+}  // namespace password_manager
+#endif  // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_AFFILIATION_AFFILIATIONS_PREFETCHER_H_
diff --git a/components/password_manager/core/browser/affiliation/affiliations_prefetcher_unittest.cc b/components/password_manager/core/browser/affiliation/affiliations_prefetcher_unittest.cc
new file mode 100644
index 0000000..d1f6ca4
--- /dev/null
+++ b/components/password_manager/core/browser/affiliation/affiliations_prefetcher_unittest.cc
@@ -0,0 +1,490 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/password_manager/core/browser/affiliation/affiliations_prefetcher.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_mock_time_message_loop_task_runner.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/time/time.h"
+#include "components/password_manager/core/browser/affiliation/affiliation_utils.h"
+#include "components/password_manager/core/browser/affiliation/mock_affiliation_service.h"
+#include "components/password_manager/core/browser/test_password_store.h"
+#include "components/password_manager/core/common/password_manager_features.h"
+#include "services/network/test/test_shared_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace password_manager {
+
+namespace {
+
+using StrategyOnCacheMiss = AffiliationService::StrategyOnCacheMiss;
+
+class OverloadedMockAffiliationService : public MockAffiliationService {
+ public:
+  OverloadedMockAffiliationService() {
+    testing::DefaultValue<AffiliatedFacets>::Set(AffiliatedFacets());
+  }
+
+  MOCK_METHOD(AffiliatedFacets,
+              OnGetAffiliationsAndBrandingCalled,
+              (const FacetURI&, StrategyOnCacheMiss));
+
+  void GetAffiliationsAndBranding(const FacetURI& facet_uri,
+                                  StrategyOnCacheMiss cache_miss_strategy,
+                                  ResultCallback result_callback) override {
+    AffiliatedFacets affiliation =
+        OnGetAffiliationsAndBrandingCalled(facet_uri, cache_miss_strategy);
+    std::move(result_callback).Run(affiliation, !affiliation.empty());
+  }
+
+  void ExpectCallToGetAffiliationsAndBrandingAndSucceedWithResult(
+      const FacetURI& expected_facet_uri,
+      StrategyOnCacheMiss expected_cache_miss_strategy,
+      const AffiliatedFacets& affiliations_to_return) {
+    EXPECT_CALL(*this, OnGetAffiliationsAndBrandingCalled(
+                           expected_facet_uri, expected_cache_miss_strategy))
+        .WillOnce(testing::Return(affiliations_to_return));
+  }
+
+  void ExpectCallToGetAffiliationsAndBrandingAndEmulateFailure(
+      const FacetURI& expected_facet_uri,
+      StrategyOnCacheMiss expected_cache_miss_strategy) {
+    EXPECT_CALL(*this, OnGetAffiliationsAndBrandingCalled(
+                           expected_facet_uri, expected_cache_miss_strategy))
+        .WillOnce(testing::Return(AffiliatedFacets()));
+  }
+
+  void ExpectCallToPrefetch(const char* expected_facet_uri_spec) {
+    EXPECT_CALL(*this,
+                Prefetch(FacetURI::FromCanonicalSpec(expected_facet_uri_spec),
+                         base::Time::Max()))
+        .RetiresOnSaturation();
+  }
+
+  void ExpectCallToCancelPrefetch(const char* expected_facet_uri_spec) {
+    EXPECT_CALL(*this, CancelPrefetch(
+                           FacetURI::FromCanonicalSpec(expected_facet_uri_spec),
+                           base::Time::Max()))
+        .RetiresOnSaturation();
+  }
+
+  void ExpectCallToTrimCacheForFacetURI(const char* expected_facet_uri_spec) {
+    EXPECT_CALL(*this, TrimCacheForFacetURI(FacetURI::FromCanonicalSpec(
+                           expected_facet_uri_spec)))
+        .RetiresOnSaturation();
+  }
+
+  void ExpectCallToTrimUnusedCache() {
+    EXPECT_CALL(*this, TrimUnusedCache).RetiresOnSaturation();
+  }
+
+  void ExpectKeepPrefetchForFacets(
+      const std::vector<FacetURI>& expected_facets) {
+    EXPECT_CALL(*this, KeepPrefetchForFacets(expected_facets))
+        .RetiresOnSaturation();
+  }
+};
+
+const char kTestWebFacetURIAlpha1[] = "https://one.alpha.example.com";
+const char kTestWebFacetURIAlpha2[] = "https://two.alpha.example.com";
+const char kTestAndroidFacetURIAlpha3[] =
+    "android://hash@com.example.alpha.android";
+const char kTestWebRealmAlpha1[] = "https://one.alpha.example.com/";
+const char kTestWebRealmAlpha2[] = "https://two.alpha.example.com/";
+const char kTestAndroidRealmAlpha3[] =
+    "android://hash@com.example.alpha.android/";
+
+const char kTestAndroidFacetURIBeta2[] =
+    "android://hash@com.example.beta.android";
+const char kTestAndroidFacetURIBeta3[] =
+    "android://hash@com.yetanother.beta.android";
+const char kTestAndroidRealmBeta2[] =
+    "android://hash@com.example.beta.android/";
+const char kTestAndroidRealmBeta3[] =
+    "android://hash@com.yetanother.beta.android/";
+
+const char kTestAndroidFacetURIGamma[] =
+    "android://hash@com.example.gamma.android";
+const char kTestAndroidRealmGamma[] =
+    "android://hash@com.example.gamma.android";
+
+const char16_t kTestUsername[] = u"JohnDoe";
+const char16_t kTestPassword[] = u"secret";
+
+PasswordForm GetTestAndroidCredentials(const char* signon_realm) {
+  PasswordForm form;
+  form.scheme = PasswordForm::Scheme::kHtml;
+  form.signon_realm = signon_realm;
+  form.username_value = kTestUsername;
+  form.password_value = kTestPassword;
+  return form;
+}
+
+PasswordForm GetTestBlocklistedAndroidCredentials(const char* signon_realm) {
+  PasswordForm form = GetTestAndroidCredentials(signon_realm);
+  form.blocked_by_user = true;
+  form.username_value.clear();
+  form.password_value.clear();
+  return form;
+}
+
+}  // namespace
+
+class AffiliationsPrefetcherTest : public testing::Test,
+                                   public ::testing::WithParamInterface<bool> {
+ public:
+  AffiliationsPrefetcherTest() {
+    feature_list_.InitWithFeatureState(
+        features::kFillingAcrossAffiliatedWebsites, GetParam());
+  }
+
+ protected:
+  void RunDeferredInitialization() {
+    task_environment_.RunUntilIdle();
+    mock_affiliation_service()->ExpectCallToTrimUnusedCache();
+    prefetcher_->Init(mock_affiliation_service(), password_store());
+    // task_environment_.RunUntilIdle();
+    task_environment_.FastForwardBy(base::Seconds(30));
+  }
+
+  void RunUntilIdle() { task_environment_.RunUntilIdle(); }
+
+  void AddLoginAndWait(const PasswordForm& form) {
+    password_store_->AddLogin(form);
+    RunUntilIdle();
+  }
+
+  void UpdateLoginWithPrimaryKey(const PasswordForm& new_form,
+                                 const PasswordForm& old_primary_key) {
+    password_store_->UpdateLoginWithPrimaryKey(new_form, old_primary_key);
+    RunUntilIdle();
+  }
+
+  void RemoveLogin(const PasswordForm& form) {
+    password_store_->RemoveLogin(form);
+    RunUntilIdle();
+  }
+
+  void AddAndroidAndNonAndroidTestLogins() {
+    AddLoginAndWait(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
+    AddLoginAndWait(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
+    AddLoginAndWait(
+        GetTestBlocklistedAndroidCredentials(kTestAndroidRealmBeta3));
+    AddLoginAndWait(GetTestAndroidCredentials(kTestAndroidRealmGamma));
+
+    AddLoginAndWait(GetTestAndroidCredentials(kTestWebRealmAlpha1));
+    AddLoginAndWait(GetTestAndroidCredentials(kTestWebRealmAlpha2));
+  }
+
+  void RemoveAndroidAndNonAndroidTestLogins() {
+    RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
+    RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmBeta2));
+    RemoveLogin(GetTestBlocklistedAndroidCredentials(kTestAndroidRealmBeta3));
+    RemoveLogin(GetTestAndroidCredentials(kTestAndroidRealmGamma));
+
+    RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha1));
+    RemoveLogin(GetTestAndroidCredentials(kTestWebRealmAlpha2));
+  }
+
+  void ExpectPrefetchForTestLogins() {
+    mock_affiliation_service()->ExpectCallToPrefetch(
+        kTestAndroidFacetURIAlpha3);
+    mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta2);
+    mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIBeta3);
+    mock_affiliation_service()->ExpectCallToPrefetch(kTestAndroidFacetURIGamma);
+
+    if (base::FeatureList::IsEnabled(
+            features::kFillingAcrossAffiliatedWebsites)) {
+      mock_affiliation_service()->ExpectCallToPrefetch(kTestWebFacetURIAlpha1);
+      mock_affiliation_service()->ExpectCallToPrefetch(kTestWebFacetURIAlpha2);
+    }
+  }
+
+  void ExpectKeepPrefetchForTestLogins() {
+    std::vector<FacetURI> expected_facets;
+    expected_facets.push_back(
+        FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3));
+    expected_facets.push_back(
+        FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2));
+    expected_facets.push_back(
+        FacetURI::FromCanonicalSpec(kTestAndroidFacetURIGamma));
+    expected_facets.push_back(
+        FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta3));
+
+    if (base::FeatureList::IsEnabled(
+            features::kFillingAcrossAffiliatedWebsites)) {
+      expected_facets.push_back(
+          FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1));
+      expected_facets.push_back(
+          FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha2));
+    }
+
+    mock_affiliation_service()->ExpectKeepPrefetchForFacets(expected_facets);
+  }
+
+  void ExpectCancelPrefetchForTestLogins() {
+    mock_affiliation_service()->ExpectCallToCancelPrefetch(
+        kTestAndroidFacetURIAlpha3);
+    mock_affiliation_service()->ExpectCallToCancelPrefetch(
+        kTestAndroidFacetURIBeta2);
+    mock_affiliation_service()->ExpectCallToCancelPrefetch(
+        kTestAndroidFacetURIBeta3);
+    mock_affiliation_service()->ExpectCallToCancelPrefetch(
+        kTestAndroidFacetURIGamma);
+
+    if (base::FeatureList::IsEnabled(
+            features::kFillingAcrossAffiliatedWebsites)) {
+      mock_affiliation_service()->ExpectCallToCancelPrefetch(
+          kTestWebFacetURIAlpha1);
+      mock_affiliation_service()->ExpectCallToCancelPrefetch(
+          kTestWebFacetURIAlpha2);
+    }
+  }
+
+  void ExpectTrimCacheForTestLogins() {
+    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+        kTestAndroidFacetURIAlpha3);
+    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+        kTestAndroidFacetURIBeta2);
+    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+        kTestAndroidFacetURIBeta3);
+    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+        kTestAndroidFacetURIGamma);
+
+    if (base::FeatureList::IsEnabled(
+            features::kFillingAcrossAffiliatedWebsites)) {
+      mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+          kTestWebFacetURIAlpha1);
+      mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+          kTestWebFacetURIAlpha2);
+    }
+  }
+
+  void DestroyPasswordStore() {
+    password_store_->ShutdownOnUIThread();
+    password_store_ = nullptr;
+  }
+
+  TestPasswordStore* password_store() { return password_store_.get(); }
+
+  OverloadedMockAffiliationService* mock_affiliation_service() {
+    return mock_affiliation_service_.get();
+  }
+
+  AffiliationsPrefetcher* prefetcher() { return prefetcher_.get(); }
+
+ private:
+  void OnAffiliatedRealmsCallback(
+      const std::vector<std::string>& affiliated_realms) {
+    EXPECT_TRUE(expecting_result_callback_);
+    expecting_result_callback_ = false;
+    last_result_realms_ = affiliated_realms;
+  }
+
+  // testing::Test:
+  void SetUp() override {
+    mock_affiliation_service_ = std::make_unique<
+        testing::StrictMock<OverloadedMockAffiliationService>>();
+    password_store()->Init(/*prefs=*/nullptr, nullptr);
+
+    prefetcher_ = std::make_unique<AffiliationsPrefetcher>();
+  }
+
+  void TearDown() override {
+    prefetcher_ = nullptr;
+    if (password_store_)
+      DestroyPasswordStore();
+    mock_affiliation_service_.reset();
+    // Clean up on the background thread.
+    RunUntilIdle();
+  }
+
+  base::test::ScopedFeatureList feature_list_;
+  base::test::SingleThreadTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
+
+  std::vector<std::string> last_result_realms_;
+  bool expecting_result_callback_ = false;
+
+  scoped_refptr<TestPasswordStore> password_store_ =
+      base::MakeRefCounted<TestPasswordStore>();
+  std::unique_ptr<AffiliationsPrefetcher> prefetcher_;
+
+  std::unique_ptr<OverloadedMockAffiliationService> mock_affiliation_service_;
+};
+
+// Verifies that affiliations for Android applications with pre-existing
+// credentials on start-up are prefetched.
+TEST_P(
+    AffiliationsPrefetcherTest,
+    PrefetchAffiliationsAndBrandingForPreexistingAndroidCredentialsOnStartup) {
+  RunUntilIdle();
+
+  AddAndroidAndNonAndroidTestLogins();
+  RunUntilIdle();
+
+  ExpectKeepPrefetchForTestLogins();
+  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+}
+
+// Stores credentials for Android applications between Initialize() and
+// DoDeferredInitialization(). Verifies that corresponding affiliation
+// information gets prefetched.
+TEST_P(AffiliationsPrefetcherTest,
+       PrefetchAffiliationsForAndroidCredentialsAddedInInitializationDelay) {
+  // Wait until PasswordStore initialisation is complete and
+  // AffiliationsPrefetcher::Initialize is called.
+  RunUntilIdle();
+
+  AddAndroidAndNonAndroidTestLogins();
+
+  ExpectKeepPrefetchForTestLogins();
+  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+}
+
+// Stores credentials for Android applications after DoDeferredInitialization().
+// Verifies that corresponding affiliation information gets prefetched.
+TEST_P(AffiliationsPrefetcherTest,
+       PrefetchAffiliationsForAndroidCredentialsAddedAfterInitialization) {
+  mock_affiliation_service()->ExpectKeepPrefetchForFacets({});
+  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+
+  ExpectPrefetchForTestLogins();
+  AddAndroidAndNonAndroidTestLogins();
+}
+
+TEST_P(AffiliationsPrefetcherTest,
+       CancelPrefetchingAffiliationsAndBrandingForRemovedAndroidCredentials) {
+  AddAndroidAndNonAndroidTestLogins();
+
+  ExpectKeepPrefetchForTestLogins();
+  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+
+  ExpectCancelPrefetchForTestLogins();
+  ExpectTrimCacheForTestLogins();
+
+  RemoveAndroidAndNonAndroidTestLogins();
+}
+
+// Verify that whenever the primary key is updated for a credential (in which
+// case both REMOVE and ADD change notifications are sent out), then Prefetch()
+// is called in response to the addition before the call to
+// TrimCacheForFacetURI() in response to the removal, so that cached data is not
+// deleted and then immediately re-fetched.
+TEST_P(AffiliationsPrefetcherTest, PrefetchBeforeTrimForPrimaryKeyUpdates) {
+  AddAndroidAndNonAndroidTestLogins();
+
+  ExpectKeepPrefetchForTestLogins();
+  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+
+  mock_affiliation_service()->ExpectCallToCancelPrefetch(
+      kTestAndroidFacetURIAlpha3);
+
+  {
+    testing::InSequence in_sequence;
+    mock_affiliation_service()->ExpectCallToPrefetch(
+        kTestAndroidFacetURIAlpha3);
+    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+        kTestAndroidFacetURIAlpha3);
+  }
+
+  PasswordForm old_form(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
+  PasswordForm new_form(old_form);
+  new_form.username_value = u"NewUserName";
+  UpdateLoginWithPrimaryKey(new_form, old_form);
+}
+
+// Stores and removes four credentials for the same an Android application, and
+// expects that Prefetch() and CancelPrefetch() will each be called four times.
+TEST_P(AffiliationsPrefetcherTest,
+       DuplicateCredentialsArePrefetchWithMultiplicity) {
+  mock_affiliation_service()->ExpectKeepPrefetchForFacets(
+      {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
+       FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
+       FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3)});
+
+  PasswordForm android_form(GetTestAndroidCredentials(kTestAndroidRealmAlpha3));
+  password_store()->AddLogin(android_form);
+
+  // Store two credentials before initialization.
+  PasswordForm android_form2(android_form);
+  android_form2.username_value = u"JohnDoe2";
+  password_store()->AddLogin(android_form2);
+
+  // Wait until PasswordStore initializes AffiliationsPrefetcher and processes
+  // added logins.
+  RunUntilIdle();
+
+  // Store one credential between initialization and deferred initialization.
+  PasswordForm android_form3(android_form);
+  android_form3.username_value = u"JohnDoe3";
+  password_store()->AddLogin(android_form3);
+
+  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+
+  EXPECT_CALL(*mock_affiliation_service(),
+              Prefetch(FacetURI::FromCanonicalSpec(kTestAndroidFacetURIAlpha3),
+                       base::Time::Max()))
+      .Times(1);
+
+  // Store one credential after deferred initialization.
+  PasswordForm android_form4(android_form);
+  android_form4.username_value = u"JohnDoe4";
+  AddLoginAndWait(android_form4);
+
+  for (size_t i = 0; i < 4; ++i) {
+    mock_affiliation_service()->ExpectCallToCancelPrefetch(
+        kTestAndroidFacetURIAlpha3);
+    mock_affiliation_service()->ExpectCallToTrimCacheForFacetURI(
+        kTestAndroidFacetURIAlpha3);
+  }
+
+  RemoveLogin(android_form);
+  RemoveLogin(android_form2);
+  RemoveLogin(android_form3);
+  RemoveLogin(android_form4);
+}
+
+TEST_P(AffiliationsPrefetcherTest, OnLoginsRetained) {
+  mock_affiliation_service()->ExpectKeepPrefetchForFacets({});
+  ASSERT_NO_FATAL_FAILURE(RunDeferredInitialization());
+
+  std::vector<PasswordForm> forms = {
+      GetTestAndroidCredentials(kTestWebFacetURIAlpha1),
+      GetTestAndroidCredentials(kTestAndroidFacetURIBeta2)};
+  std::vector<FacetURI> expected_facets;
+
+  if (base::FeatureList::IsEnabled(
+          features::kFillingAcrossAffiliatedWebsites)) {
+    expected_facets = {FacetURI::FromCanonicalSpec(kTestWebFacetURIAlpha1),
+                       FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2)};
+  } else {
+    expected_facets = {FacetURI::FromCanonicalSpec(kTestAndroidFacetURIBeta2)};
+  }
+
+  mock_affiliation_service()->ExpectKeepPrefetchForFacets(expected_facets);
+
+  (static_cast<PasswordStoreInterface::Observer*>(prefetcher()))
+      ->OnLoginsRetained(nullptr, forms);
+}
+
+INSTANTIATE_TEST_SUITE_P(FillingAcrossAffiliatedWebsites,
+                         AffiliationsPrefetcherTest,
+                         ::testing::Bool());
+
+}  // namespace password_manager
diff --git a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
index 2f4d5a9..d987a51 100644
--- a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
+++ b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator.cc
@@ -153,6 +153,13 @@
           password_manager::prefs::kTimeOfLastMigrationAttempt));
   if (time_passed_since_last_migration_attempt < kMigrationThreshold)
     return;
+
+  // Do not migrate if a migration is already running. By the time it ends, the
+  // two backends will be identical, therefore the second migration won't be
+  // needed.
+  if (migration_in_progress_type_ != MigrationType::kNone)
+    return;
+
   MigrationType migration_type =
       GetMigrationType(should_attempt_upm_reenrollment);
   if (migration_type != MigrationType::kNone)
@@ -197,11 +204,7 @@
   // already transmitted through sync.
   // Once the local storage is supported, android backend becomes the only
   // active backend and there is no need to do this migration.
-  // Do not migrate if a migration is already running. By the time it ends, the
-  // two backends will be identical, therefore the second migration won't be
-  // needed.
   if (prefs_->GetBoolean(prefs::kRequiresMigrationAfterSyncStatusChange) &&
-      (migration_in_progress_type_ == MigrationType::kNone) &&
       !features::ManagesLocalPasswordsInUnifiedPasswordManager()) {
     return IsPasswordSyncEnabled(sync_service_)
                ? MigrationType::kNonSyncableToAndroidBackend
diff --git a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
index 7ab3cde..5821d4ef3 100644
--- a/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
+++ b/components/password_manager/core/browser/built_in_backend_to_android_backend_migrator_unittest.cc
@@ -963,4 +963,41 @@
   RunUntilIdle();
 }
 
+TEST_F(BuiltInBackendToAndroidBackendMigratorWithMockAndroidBackendTest,
+       SecondMigrationCannotStartWhileTheFirstOneHasNotCompleted) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeatureWithParameters(
+      /*feature=*/features::kUnifiedPasswordManagerAndroid,
+      {{"migration_version", "1"}, {"stage", "0"}});
+  InitSyncService(/*is_password_sync_enabled=*/true);
+
+  // Add a form to the built-in backend to have something to migrate.
+  PasswordForm form = CreateTestPasswordForm();
+  built_in_backend().AddLoginAsync(form, base::DoNothing());
+
+  // Call StartMigrationIfNecessary for the first time.
+  migrator()->StartMigrationIfNecessary(
+      /*should_attempt_upm_reenrollment=*/false);
+  RunUntilIdle();
+
+  // If the user gets evicted from the experiment, migration-related prefs are
+  // cleared.
+  prefs()->ClearPref(password_manager::prefs::kTimeOfLastMigrationAttempt);
+
+  // Simulate some time passing before the second migration is triggered.
+  FastForwardBy(base::Milliseconds(123u));
+
+  // Call StartMigrationIfNecessary for the second time before the first
+  // migration finishes in an attempt to reenroll.
+  migrator()->StartMigrationIfNecessary(
+      /*should_attempt_upm_reenrollment=*/true);
+  RunUntilIdle();
+
+  // Check the recorded last migration attempt time. It should not be recorded
+  // after the pref was cleared, because the second migration should not be
+  // triggered.
+  EXPECT_EQ(0, prefs()->GetDouble(
+                   password_manager::prefs::kTimeOfLastMigrationAttempt));
+}
+
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc
index 9b297fbb..fe006a4 100644
--- a/components/password_manager/core/browser/password_store.cc
+++ b/components/password_manager/core/browser/password_store.cc
@@ -26,6 +26,7 @@
 #include "build/build_config.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/password_manager/core/browser/affiliation/affiliated_match_helper.h"
+#include "components/password_manager/core/browser/affiliation/affiliation_service.h"
 #include "components/password_manager/core/browser/get_logins_with_affiliations_request_handler.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
diff --git a/components/password_manager/core/browser/password_store_built_in_backend.cc b/components/password_manager/core/browser/password_store_built_in_backend.cc
index f40629e..dd01ded 100644
--- a/components/password_manager/core/browser/password_store_built_in_backend.cc
+++ b/components/password_manager/core/browser/password_store_built_in_backend.cc
@@ -12,6 +12,7 @@
 #include "components/password_manager/core/browser/login_database_async_helper.h"
 #include "components/password_manager/core/browser/password_store_backend.h"
 #include "components/password_manager/core/browser/password_store_backend_metrics_recorder.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
 #include "components/password_manager/core/browser/password_store_util.h"
 #include "components/sync/model/proxy_model_type_controller_delegate.h"
 
diff --git a/components/permissions/unused_site_permissions_service.cc b/components/permissions/unused_site_permissions_service.cc
index dda3145..f593abb 100644
--- a/components/permissions/unused_site_permissions_service.cc
+++ b/components/permissions/unused_site_permissions_service.cc
@@ -6,11 +6,13 @@
 #include "base/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/metrics/field_trial_params.h"
 #include "base/run_loop.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/clock.h"
 #include "base/time/default_clock.h"
+#include "base/time/time.h"
 #include "components/content_settings/core/browser/content_settings_info.h"
 #include "components/content_settings/core/browser/content_settings_registry.h"
 #include "components/content_settings/core/browser/content_settings_utils.h"
@@ -18,6 +20,7 @@
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_types.h"
+#include "components/content_settings/core/common/features.h"
 #include "content/public/browser/browser_thread.h"
 #include "url/origin.h"
 
@@ -30,7 +33,7 @@
     scoped_refptr<HostContentSettingsMap> hcsm) {
   UnusedSitePermissionsService::UnusedPermissionMap recently_unused;
   base::Time threshold =
-      clock->Now() - content_settings::GetCoarseTimePrecision();
+      clock->Now() - content_settings::GetCoarseVisitedTimePrecision();
 
   auto* registry = content_settings::ContentSettingsRegistry::GetInstance();
   for (const content_settings::ContentSettingsInfo* info : *registry) {
@@ -91,8 +94,11 @@
 void UnusedSitePermissionsService::StartRepeatedUpdates() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   UpdateUnusedPermissionsAsync(base::NullCallback());
+  base::TimeDelta repeated_update_interval =
+      content_settings::features::
+          kSafetyCheckUnusedSitePermissionsRepeatedUpdateInterval.Get();
   update_timer_.Start(
-      FROM_HERE, base::Days(1),
+      FROM_HERE, repeated_update_interval,
       base::BindRepeating(
           &UnusedSitePermissionsService::UpdateUnusedPermissionsAsync,
           base::Unretained(this), base::NullCallback()));
diff --git a/components/permissions/unused_site_permissions_service_unittest.cc b/components/permissions/unused_site_permissions_service_unittest.cc
index b7c5a54b..90a37703 100644
--- a/components/permissions/unused_site_permissions_service_unittest.cc
+++ b/components/permissions/unused_site_permissions_service_unittest.cc
@@ -69,7 +69,8 @@
       .track_last_visit_for_autoexpiration = true};
 
   const base::Time now = clock()->Now();
-  const base::TimeDelta precision = content_settings::GetCoarseTimePrecision();
+  const base::TimeDelta precision =
+      content_settings::GetCoarseVisitedTimePrecision();
 
   // Add one setting for url1 and two settings for url2.
   hcsm()->SetContentSettingDefaultScope(
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 15da9d9..f5be5cc 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -8,7 +8,7 @@
 
 package enterprise_management;
 
-option go_package="chromium/policy/enterprise_management_proto";
+option go_package = "chromium/policy/enterprise_management_proto";
 
 import "private_membership_rlwe.proto";
 
@@ -36,6 +36,21 @@
   optional LicenseTypeEnum license_type = 1;
 }
 
+// The current active session type on the device, or `NO_SESSION` if no user
+// is currently logged in.
+// This enum is used in the response payload of the
+// `FETCH_CRD_AVAILABILITY_INFO` remote command.
+enum UserSessionType {
+  USER_SESSION_TYPE_UNKNOWN = 0;
+  AUTO_LAUNCHED_KIOSK_SESSION = 1;
+  MANUALLY_LAUNCHED_KIOSK_SESSION = 2;
+  AFFILIATED_USER_SESSION = 3;
+  UNAFFILIATED_USER_SESSION = 4;
+  MANAGED_GUEST_SESSION = 5;
+  GUEST_SESSION = 6;
+  NO_SESSION = 7;
+}
+
 // Data along with a cryptographic signature verifying their authenticity.
 message SignedData {
   // The data to be signed.
@@ -2162,7 +2177,7 @@
   // Policy name of the faulty value.
   optional string policy_name = 1;
 
-  //# LINT.IfChange
+  // # LINT.IfChange
   enum ValueValidationIssueSeverity {
     // Default value for when a severity is not specified.
     VALUE_VALIDATION_ISSUE_SEVERITY_UNSPECIFIED = 0;
diff --git a/components/power_bookmarks/storage/BUILD.gn b/components/power_bookmarks/storage/BUILD.gn
index 2fde6cf..9cefe7e 100644
--- a/components/power_bookmarks/storage/BUILD.gn
+++ b/components/power_bookmarks/storage/BUILD.gn
@@ -11,6 +11,8 @@
     "power_bookmark_database.h",
     "power_bookmark_database_impl.cc",
     "power_bookmark_database_impl.h",
+    "power_bookmark_sync_bridge.cc",
+    "power_bookmark_sync_bridge.h",
     "power_bookmark_sync_metadata_database.cc",
     "power_bookmark_sync_metadata_database.h",
   ]
@@ -19,6 +21,7 @@
     "//base",
     "//components/power_bookmarks/core:powers",
     "//components/power_bookmarks/metrics",
+    "//components/sync",
     "//components/sync/model",
     "//components/sync/protocol",
     "//sql",
diff --git a/components/power_bookmarks/storage/power_bookmark_backend.cc b/components/power_bookmarks/storage/power_bookmark_backend.cc
index b6f045c..ec89cf73 100644
--- a/components/power_bookmarks/storage/power_bookmark_backend.cc
+++ b/components/power_bookmarks/storage/power_bookmark_backend.cc
@@ -7,6 +7,9 @@
 #include "components/power_bookmarks/core/powers/search_params.h"
 #include "components/power_bookmarks/storage/empty_power_bookmark_database.h"
 #include "components/power_bookmarks/storage/power_bookmark_database_impl.h"
+#include "components/power_bookmarks/storage/power_bookmark_sync_bridge.h"
+#include "components/power_bookmarks/storage/power_bookmark_sync_metadata_database.h"
+#include "components/sync/model/client_tag_based_model_type_processor.h"
 
 namespace power_bookmarks {
 
@@ -28,7 +31,17 @@
 
   // Substitute a dummy implementation when the feature is disabled.
   if (use_database) {
-    db_ = std::make_unique<PowerBookmarkDatabaseImpl>(database_dir_);
+    auto database = std::make_unique<PowerBookmarkDatabaseImpl>(database_dir_);
+
+    // TODO(crbug.com/1392502): Plumb in syncer::ReportUnrecoverableError as the
+    // dump_stack callback.
+    auto change_processor =
+        std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
+            syncer::POWER_BOOKMARK, /*dump_stack=*/base::RepeatingClosure());
+    bridge_ = std::make_unique<PowerBookmarkSyncBridge>(
+        database->GetSyncMetadataDatabase(), database.get(),
+        std::move(change_processor));
+    db_ = std::move(database);
   } else {
     db_ = std::make_unique<EmptyPowerBookmarkDatabase>();
   }
diff --git a/components/power_bookmarks/storage/power_bookmark_backend.h b/components/power_bookmarks/storage/power_bookmark_backend.h
index 60c0a0a..7875c63 100644
--- a/components/power_bookmarks/storage/power_bookmark_backend.h
+++ b/components/power_bookmarks/storage/power_bookmark_backend.h
@@ -15,6 +15,7 @@
 namespace power_bookmarks {
 
 struct SearchParams;
+class PowerBookmarkSyncBridge;
 
 // Class responsible for marshalling calls from the browser thread which the
 // service is called from and the background thread which the database is
@@ -68,6 +69,10 @@
   std::unique_ptr<PowerBookmarkDatabase> db_
       GUARDED_BY_CONTEXT(sequence_checker_);
 
+  // Sync Bridge implementation. Only initialized when the sqlite database is
+  // used.
+  std::unique_ptr<PowerBookmarkSyncBridge> bridge_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/components/power_bookmarks/storage/power_bookmark_database_impl.cc b/components/power_bookmarks/storage/power_bookmark_database_impl.cc
index 630d933..4ad983cb 100644
--- a/components/power_bookmarks/storage/power_bookmark_database_impl.cc
+++ b/components/power_bookmarks/storage/power_bookmark_database_impl.cc
@@ -8,6 +8,7 @@
 #include "base/notreached.h"
 #include "base/strings/pattern.h"
 #include "base/strings/strcat.h"
+#include "base/strings/string_util.h"
 #include "components/power_bookmarks/core/powers/search_params.h"
 #include "components/power_bookmarks/metrics/power_bookmark_metrics.h"
 #include "components/power_bookmarks/storage/power_bookmark_sync_metadata_database.h"
@@ -554,6 +555,103 @@
   return transaction.Commit();
 }
 
+std::vector<std::unique_ptr<Power>>
+PowerBookmarkDatabaseImpl::GetPowersForGUIDs(
+    const std::vector<std::string>& guids) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  for (auto& guid : guids) {
+    DCHECK(base::IsValidGUID(guid));
+  }
+  static constexpr char kGetPowersForGUIDsSql[] =
+      // clang-format off
+      "SELECT blobs.id, blobs.specifics, saves.url "
+          "FROM blobs JOIN saves ON blobs.id=saves.id "
+          "WHERE saves.id IN ('";
+  // clang-format on
+  std::string sql_string = (std::string(kGetPowersForGUIDsSql) +
+                            base::JoinString(guids, "','") + "')");
+  db_.IsSQLValid(sql_string.c_str());
+  sql::Statement statement(
+      db_.GetCachedStatement(SQL_FROM_HERE, sql_string.c_str()));
+  std::vector<std::unique_ptr<Power>> powers;
+  while (statement.Step()) {
+    DCHECK_EQ(3, statement.ColumnCount());
+
+    absl::optional<sync_pb::PowerBookmarkSpecifics> specifics =
+        DeserializeOrDelete(
+            statement.ColumnString(1),
+            base::GUID::ParseLowercase(statement.ColumnString(0)));
+    if (!specifics.has_value())
+      continue;
+
+    powers.emplace_back(CreatePowerFromSpecifics(specifics.value()));
+  }
+
+  return powers;
+}
+
+std::vector<std::unique_ptr<Power>> PowerBookmarkDatabaseImpl::GetAllPowers() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  static constexpr char kGetPowersSql[] =
+      // clang-format off
+      "SELECT blobs.id, blobs.specifics, saves.url "
+          "FROM blobs JOIN saves ON blobs.id=saves.id";
+  // clang-format on
+  DCHECK(db_.IsSQLValid(kGetPowersSql));
+
+  sql::Statement statement(
+      db_.GetCachedStatement(SQL_FROM_HERE, kGetPowersSql));
+  std::vector<std::unique_ptr<Power>> powers;
+  while (statement.Step()) {
+    DCHECK_EQ(3, statement.ColumnCount());
+
+    absl::optional<sync_pb::PowerBookmarkSpecifics> specifics =
+        DeserializeOrDelete(
+            statement.ColumnString(1),
+            base::GUID::ParseLowercase(statement.ColumnString(0)));
+    if (!specifics.has_value())
+      continue;
+
+    powers.emplace_back(CreatePowerFromSpecifics(specifics.value()));
+  }
+
+  return powers;
+}
+
+std::unique_ptr<Power> PowerBookmarkDatabaseImpl::GetPowerForGUID(
+    const std::string& guid) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  static constexpr char kGetPowerForGUIDSql[] =
+      // clang-format off
+      "SELECT blobs.id, blobs.specifics, saves.url "
+          "FROM blobs JOIN saves ON blobs.id=saves.id "
+          "WHERE saves.id=?";
+  // clang-format on
+  DCHECK(db_.IsSQLValid(kGetPowerForGUIDSql));
+
+  sql::Statement statement(
+      db_.GetCachedStatement(SQL_FROM_HERE, kGetPowerForGUIDSql));
+  statement.BindString(0, guid);
+  std::vector<std::unique_ptr<Power>> powers;
+  while (statement.Step()) {
+    DCHECK_EQ(3, statement.ColumnCount());
+
+    absl::optional<sync_pb::PowerBookmarkSpecifics> specifics =
+        DeserializeOrDelete(
+            statement.ColumnString(1),
+            base::GUID::ParseLowercase(statement.ColumnString(0)));
+    if (!specifics.has_value())
+      continue;
+
+    return CreatePowerFromSpecifics(specifics.value());
+  }
+
+  return nullptr;
+}
+
 absl::optional<sync_pb::PowerBookmarkSpecifics>
 PowerBookmarkDatabaseImpl::DeserializeOrDelete(const std::string& data,
                                                const base::GUID& id) {
diff --git a/components/power_bookmarks/storage/power_bookmark_database_impl.h b/components/power_bookmarks/storage/power_bookmark_database_impl.h
index 8900e3d7..ca23a294 100644
--- a/components/power_bookmarks/storage/power_bookmark_database_impl.h
+++ b/components/power_bookmarks/storage/power_bookmark_database_impl.h
@@ -9,6 +9,7 @@
 #include "components/power_bookmarks/core/powers/power.h"
 #include "components/power_bookmarks/core/powers/power_overview.h"
 #include "components/power_bookmarks/storage/power_bookmark_database.h"
+#include "components/power_bookmarks/storage/power_bookmark_sync_bridge.h"
 #include "sql/database.h"
 #include "sql/meta_table.h"
 #include "url/gurl.h"
@@ -22,7 +23,8 @@
     FILE_PATH_LITERAL("PowerBookmarks.db");
 
 // Holds the SQL connection for the main Power Bookmarks tables.
-class PowerBookmarkDatabaseImpl : public PowerBookmarkDatabase {
+class PowerBookmarkDatabaseImpl : public PowerBookmarkDatabase,
+                                  public PowerBookmarkSyncBridge::Delegate {
  public:
   explicit PowerBookmarkDatabaseImpl(const base::FilePath& database_dir);
   PowerBookmarkDatabaseImpl(const PowerBookmarkDatabaseImpl&) = delete;
@@ -52,6 +54,12 @@
     return sync_db_.get();
   }
 
+  // PowerBookmarkSyncBridge::Delegate
+  std::vector<std::unique_ptr<Power>> GetAllPowers() override;
+  std::vector<std::unique_ptr<Power>> GetPowersForGUIDs(
+      const std::vector<std::string>& guids) override;
+  std::unique_ptr<Power> GetPowerForGUID(const std::string& guid) override;
+
  private:
   FRIEND_TEST_ALL_PREFIXES(PowerBookmarkDatabaseImplTest,
                            InitDatabaseWithErrorCallback);
diff --git a/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc b/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc
index f17ef94..836feee 100644
--- a/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc
+++ b/components/power_bookmarks/storage/power_bookmark_database_impl_unittest.cc
@@ -595,4 +595,64 @@
   EXPECT_EQ(0u, stored_powers.size());
 }
 
+TEST_F(PowerBookmarkDatabaseImplTest, GetAllPowers) {
+  std::unique_ptr<PowerBookmarkDatabaseImpl> pbdb =
+      std::make_unique<PowerBookmarkDatabaseImpl>(db_dir());
+  EXPECT_TRUE(pbdb->Init());
+
+  EXPECT_TRUE(pbdb->CreatePower(
+      MakePower(GURL("https://google.com"),
+                sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK)));
+
+  EXPECT_TRUE(pbdb->CreatePower(
+      MakePower(GURL("https://bing.com"),
+                sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK)));
+
+  std::vector<std::unique_ptr<Power>> stored_powers = pbdb->GetAllPowers();
+  EXPECT_EQ(2u, stored_powers.size());
+}
+
+TEST_F(PowerBookmarkDatabaseImplTest, GetPowersForGUIDs) {
+  std::unique_ptr<PowerBookmarkDatabaseImpl> pbdb =
+      std::make_unique<PowerBookmarkDatabaseImpl>(db_dir());
+  EXPECT_TRUE(pbdb->Init());
+
+  auto power1 = MakePower(GURL("https://google.com"),
+                          sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK);
+  auto power2 = MakePower(GURL("https://google.com"),
+                          sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK);
+  auto power3 = MakePower(GURL("https://bing.com"),
+                          sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK);
+  auto guid1 = power1->guid().AsLowercaseString();
+  auto guid2 = power2->guid().AsLowercaseString();
+
+  EXPECT_TRUE(pbdb->CreatePower(std::move(power1)));
+  EXPECT_TRUE(pbdb->CreatePower(std::move(power2)));
+  EXPECT_TRUE(pbdb->CreatePower(std::move(power3)));
+
+  std::vector<std::unique_ptr<Power>> stored_powers =
+      pbdb->GetPowersForGUIDs({guid1, guid2});
+  EXPECT_EQ(2u, stored_powers.size());
+  EXPECT_EQ(GURL("https://google.com"), stored_powers[0]->url());
+  EXPECT_EQ(GURL("https://google.com"), stored_powers[1]->url());
+}
+
+TEST_F(PowerBookmarkDatabaseImplTest, GetPowerForGUID) {
+  std::unique_ptr<PowerBookmarkDatabaseImpl> pbdb =
+      std::make_unique<PowerBookmarkDatabaseImpl>(db_dir());
+  EXPECT_TRUE(pbdb->Init());
+
+  auto power1 = MakePower(GURL("https://google.com"),
+                          sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK);
+  auto power2 = MakePower(GURL("https://bing.com"),
+                          sync_pb::PowerBookmarkSpecifics::POWER_TYPE_MOCK);
+  auto guid1 = power1->guid().AsLowercaseString();
+
+  EXPECT_TRUE(pbdb->CreatePower(std::move(power1)));
+  EXPECT_TRUE(pbdb->CreatePower(std::move(power2)));
+
+  std::unique_ptr<Power> stored_power = pbdb->GetPowerForGUID(guid1);
+  EXPECT_EQ(GURL("https://google.com"), stored_power->url());
+}
+
 }  // namespace power_bookmarks
diff --git a/components/power_bookmarks/storage/power_bookmark_sync_bridge.cc b/components/power_bookmarks/storage/power_bookmark_sync_bridge.cc
new file mode 100644
index 0000000..8b40fed8
--- /dev/null
+++ b/components/power_bookmarks/storage/power_bookmark_sync_bridge.cc
@@ -0,0 +1,85 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/power_bookmarks/storage/power_bookmark_sync_bridge.h"
+
+#include "components/power_bookmarks/core/powers/power.h"
+#include "components/power_bookmarks/storage/power_bookmark_sync_metadata_database.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/model/sync_metadata_store_change_list.h"
+
+namespace power_bookmarks {
+
+namespace {
+void WritePowersToSyncData(const std::vector<std::unique_ptr<Power>>& powers,
+                           PowerBookmarkSyncBridge::DataCallback callback) {
+  auto batch = std::make_unique<syncer::MutableDataBatch>();
+  for (const auto& power : powers) {
+    std::string guid = power->guid().AsLowercaseString();
+    auto entity_data = std::make_unique<syncer::EntityData>();
+    entity_data->name = guid;
+    power->ToPowerBookmarkSpecifics(
+        entity_data->specifics.mutable_power_bookmark());
+    batch->Put(guid, std::move(entity_data));
+  }
+  std::move(callback).Run(std::move(batch));
+}
+}  // namespace
+
+PowerBookmarkSyncBridge::PowerBookmarkSyncBridge(
+    PowerBookmarkSyncMetadataDatabase* meta_db,
+    Delegate* delegate,
+    std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor)
+    : syncer::ModelTypeSyncBridge(std::move(change_processor)),
+      meta_db_(meta_db),
+      delegate_(delegate) {}
+
+PowerBookmarkSyncBridge::~PowerBookmarkSyncBridge() = default;
+
+std::unique_ptr<syncer::MetadataChangeList>
+PowerBookmarkSyncBridge::CreateMetadataChangeList() {
+  return std::make_unique<syncer::SyncMetadataStoreChangeList>(
+      meta_db_, syncer::POWER_BOOKMARK,
+      base::BindRepeating(&syncer::ModelTypeChangeProcessor::ReportError,
+                          change_processor()->GetWeakPtr()));
+}
+
+absl::optional<syncer::ModelError> PowerBookmarkSyncBridge::MergeSyncData(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_changes) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+absl::optional<syncer::ModelError> PowerBookmarkSyncBridge::ApplySyncChanges(
+    std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+    syncer::EntityChangeList entity_changes) {
+  NOTIMPLEMENTED();
+  return {};
+}
+
+std::string PowerBookmarkSyncBridge::GetStorageKey(
+    const syncer::EntityData& entity_data) {
+  return entity_data.specifics.power_bookmark().guid();
+}
+
+std::string PowerBookmarkSyncBridge::GetClientTag(
+    const syncer::EntityData& entity_data) {
+  return GetStorageKey(entity_data);
+}
+
+void PowerBookmarkSyncBridge::GetData(StorageKeyList storage_keys,
+                                      DataCallback callback) {
+  WritePowersToSyncData(delegate_->GetPowersForGUIDs(storage_keys),
+                        std::move(callback));
+}
+
+void PowerBookmarkSyncBridge::GetAllDataForDebugging(DataCallback callback) {
+  WritePowersToSyncData(delegate_->GetAllPowers(), std::move(callback));
+}
+
+}  // namespace power_bookmarks
diff --git a/components/power_bookmarks/storage/power_bookmark_sync_bridge.h b/components/power_bookmarks/storage/power_bookmark_sync_bridge.h
new file mode 100644
index 0000000..93c4fa7
--- /dev/null
+++ b/components/power_bookmarks/storage/power_bookmark_sync_bridge.h
@@ -0,0 +1,62 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_POWER_BOOKMARKS_STORAGE_POWER_BOOKMARK_SYNC_BRIDGE_H_
+#define COMPONENTS_POWER_BOOKMARKS_STORAGE_POWER_BOOKMARK_SYNC_BRIDGE_H_
+
+#include "components/sync/model/model_type_sync_bridge.h"
+
+namespace syncer {
+class ModelError;
+}  // namespace syncer
+
+namespace power_bookmarks {
+
+class Power;
+class PowerBookmarkSyncMetadataDatabase;
+
+// PowerBookmarkSyncBridge is responsible for syncing all powers to different
+// devices. It runs on the same thread as the power bookmark database
+// implementation.
+class PowerBookmarkSyncBridge : public syncer::ModelTypeSyncBridge {
+ public:
+  class Delegate {
+   public:
+    virtual std::vector<std::unique_ptr<Power>> GetAllPowers() = 0;
+    virtual std::vector<std::unique_ptr<Power>> GetPowersForGUIDs(
+        const std::vector<std::string>& guids) = 0;
+    virtual std::unique_ptr<Power> GetPowerForGUID(const std::string& guid) = 0;
+  };
+  PowerBookmarkSyncBridge(
+      PowerBookmarkSyncMetadataDatabase* meta_db,
+      Delegate* delegate,
+      std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor);
+
+  PowerBookmarkSyncBridge(const PowerBookmarkSyncBridge&) = delete;
+  PowerBookmarkSyncBridge& operator=(const PowerBookmarkSyncBridge&) = delete;
+
+  ~PowerBookmarkSyncBridge() override;
+
+  // syncer::ModelTypeSyncBridge:
+  std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+      override;
+  absl::optional<syncer::ModelError> MergeSyncData(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_changes) override;
+  absl::optional<syncer::ModelError> ApplySyncChanges(
+      std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+      syncer::EntityChangeList entity_changes) override;
+  std::string GetStorageKey(const syncer::EntityData& entity_data) override;
+  std::string GetClientTag(const syncer::EntityData& entity_data) override;
+  void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+  void GetAllDataForDebugging(DataCallback callback) override;
+
+ private:
+  const raw_ptr<PowerBookmarkSyncMetadataDatabase> meta_db_;
+  const raw_ptr<Delegate> delegate_;
+};
+
+}  // namespace power_bookmarks
+
+#endif  // COMPONENTS_POWER_BOOKMARKS_STORAGE_POWER_BOOKMARK_SYNC_BRIDGE_H_
diff --git a/components/privacy_sandbox/privacy_sandbox_prefs.cc b/components/privacy_sandbox/privacy_sandbox_prefs.cc
index 8d4afa4..d66bbab 100644
--- a/components/privacy_sandbox/privacy_sandbox_prefs.cc
+++ b/components/privacy_sandbox/privacy_sandbox_prefs.cc
@@ -20,7 +20,7 @@
     "privacy_sandbox.m1.row_notice_acknowledged";
 
 const char kPrivacySandboxM1PromptSuppressed[] =
-    "privacy_sandbox.m1.prompt_supressed";
+    "privacy_sandbox.m1.prompt_suppressed";
 
 const char kPrivacySandboxM1TopicsEnabled[] =
     "privacy_sandbox.m1.topics_enabled";
diff --git a/components/segmentation_platform/public/BUILD.gn b/components/segmentation_platform/public/BUILD.gn
index 079f859..f215391 100644
--- a/components/segmentation_platform/public/BUILD.gn
+++ b/components/segmentation_platform/public/BUILD.gn
@@ -23,6 +23,8 @@
     "local_state_helper.h",
     "model_provider.cc",
     "model_provider.h",
+    "result.cc",
+    "result.h",
     "segment_selection_result.cc",
     "segment_selection_result.h",
     "segmentation_platform_service.cc",
@@ -88,4 +90,8 @@
 
     sources = [ "android/java/src/org/chromium/components/segmentation_platform/SegmentationPlatformConversionBridge.java" ]
   }
+
+  java_cpp_enum("execution_status_enums_java") {
+    sources = [ "result.h" ]
+  }
 }
diff --git a/components/segmentation_platform/public/result.cc b/components/segmentation_platform/public/result.cc
new file mode 100644
index 0000000..89541695
--- /dev/null
+++ b/components/segmentation_platform/public/result.cc
@@ -0,0 +1,18 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/segmentation_platform/public/result.h"
+
+namespace segmentation_platform {
+
+ClassificationResult::ClassificationResult(PredictionStatus status)
+    : status(status) {}
+
+ClassificationResult::~ClassificationResult() = default;
+
+RegressionResult::RegressionResult(PredictionStatus status) : status(status) {}
+
+RegressionResult::~RegressionResult() = default;
+
+}  // namespace segmentation_platform
\ No newline at end of file
diff --git a/components/segmentation_platform/public/result.h b/components/segmentation_platform/public/result.h
new file mode 100644
index 0000000..88d3669
--- /dev/null
+++ b/components/segmentation_platform/public/result.h
@@ -0,0 +1,71 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_RESULT_H_
+#define COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_RESULT_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback_helpers.h"
+
+namespace segmentation_platform {
+
+// Various status for PredictionResult.
+// GENERATED_JAVA_ENUM_PACKAGE: (
+//   org.chromium.components.segmentation_platform.prediction_status)
+enum class PredictionStatus {
+  kNotReady = 0,
+  kFailed = 1,
+  kSucceeded = 2,
+};
+
+// ClassificationResult is returned when Predictor specified by the client in
+// OutputConfig is one of BinaryClassifier, MultiClassClassifier or
+// BinnedClassifier.
+struct ClassificationResult {
+  explicit ClassificationResult(PredictionStatus status);
+  ~ClassificationResult();
+
+  // Disallow copy/assign.
+  ClassificationResult(const ClassificationResult&) = delete;
+  ClassificationResult& operator=(const ClassificationResult&) = delete;
+
+  // Various error codes such as model failed or insufficient data collection.
+  const PredictionStatus status;
+
+  // The list of labels arranged in descending order of result from model
+  // evaluation. For BinaryClassifier, it is eithier a `positive_label` or
+  // `negative_label`. For MultiClassClassifier, it is list of `top_k_outputs`
+  // labels based on the score for the label. For BinnedClassifier, it is a
+  // label from one of the bin depending on where the score from the model
+  // evaluation lies.
+  std::vector<std::string> ordered_labels;
+};
+
+// RegressionResult is returned when Predictor specified by the client in
+// OutputConfig is Regressor.
+struct RegressionResult {
+  explicit RegressionResult(PredictionStatus status);
+  ~RegressionResult();
+
+  // Disallow copy/assign.
+  RegressionResult(const RegressionResult&) = delete;
+  RegressionResult& operator=(const RegressionResult&) = delete;
+
+  // Various error codes such as model failed or insufficient data collection.
+  const PredictionStatus status;
+
+  // The result of regression.
+  float regression_result{0.0f};
+};
+
+using ClassificationResultCallback =
+    base::OnceCallback<void(const ClassificationResult&)>;
+using RegressionResultCallback =
+    base::OnceCallback<void(const RegressionResult&)>;
+
+}  // namespace segmentation_platform
+
+#endif  // COMPONENTS_SEGMENTATION_PLATFORM_PUBLIC_RESULT_H_
diff --git a/components/segmentation_platform/public/segmentation_platform_service.h b/components/segmentation_platform/public/segmentation_platform_service.h
index 1da575d..e6c6d35 100644
--- a/components/segmentation_platform/public/segmentation_platform_service.h
+++ b/components/segmentation_platform/public/segmentation_platform_service.h
@@ -47,6 +47,18 @@
   SegmentationPlatformService& operator=(const SegmentationPlatformService&) =
       delete;
 
+  struct PredictionOptions {
+    PredictionOptions() = default;
+    ~PredictionOptions() = default;
+
+    // Disallow copy/assign.
+    PredictionOptions(const PredictionOptions&) = delete;
+    PredictionOptions& operator=(const PredictionOptions&) = delete;
+
+    // Set to true if on demand execution is to be done.
+    bool on_demand_execution = false;
+  };
+
   // Registers preferences used by this class in the provided |registry|.  This
   // should be called for the Profile registry.
   static void RegisterProfilePrefs(PrefRegistrySimple* registry);
diff --git a/components/sync/base/model_type.cc b/components/sync/base/model_type.cc
index f1de8d5..e3c89f3b 100644
--- a/components/sync/base/model_type.cc
+++ b/components/sync/base/model_type.cc
@@ -26,7 +26,7 @@
   const char* const notification_type;
   // Root tag for Model Type
   // This should be the same as the model type but all lowercase.
-  const char* const root_tag;
+  const char* const lowercase_root_tag;
   // String value for Model Type
   // This should be the same as the model type but space separated and the
   // first letter of every word capitalized.
@@ -537,18 +537,17 @@
   return value;
 }
 
-// TODO(zea): remove all hardcoded tags in model associators and have them use
-// this instead.
-std::string ModelTypeToRootTag(ModelType type) {
-  DCHECK(ProtocolTypes().Has(type));
-  DCHECK(IsRealDataType(type));
-  const std::string root_tag = std::string(kModelTypeInfoMap[type].root_tag);
+std::string ModelTypeToProtocolRootTag(ModelType model_type) {
+  DCHECK(ProtocolTypes().Has(model_type));
+  DCHECK(IsRealDataType(model_type));
+  const std::string root_tag =
+      std::string(kModelTypeInfoMap[model_type].lowercase_root_tag);
   DCHECK(!root_tag.empty());
   return "google_chrome_" + root_tag;
 }
 
-const char* GetModelTypeRootTag(ModelType model_type) {
-  return kModelTypeInfoMap[model_type].root_tag;
+const char* GetModelTypeLowerCaseRootTag(ModelType model_type) {
+  return kModelTypeInfoMap[model_type].lowercase_root_tag;
 }
 
 bool RealModelTypeToNotificationType(ModelType model_type,
diff --git a/components/sync/base/model_type.h b/components/sync/base/model_type.h
index ab85e82..806743d 100644
--- a/components/sync/base/model_type.h
+++ b/components/sync/base/model_type.h
@@ -432,13 +432,18 @@
 // Generates a base::Value::List from |model_types|.
 base::Value::List ModelTypeSetToValue(ModelTypeSet model_types);
 
-// Returns a string corresponding to the syncable tag for this datatype.
-std::string ModelTypeToRootTag(ModelType type);
+// Returns a string corresponding to the root tag as exposed in the sync
+// protocol as the root entity's ID, which makes the root entity trivially
+// distinguishable from regular entities. Note that the existence of a root
+// entity in the sync protocol is a legacy artifact, and modern clients ignore
+// it except for bookmarks and Nigori. For this reason, the server may or may
+// not return the root entity.
+std::string ModelTypeToProtocolRootTag(ModelType model_type);
 
-// Returns root_tag for |model_type| in ModelTypeInfo.
-// Difference with ModelTypeToRootTag(), this just simply returns root_tag in
-// ModelTypeInfo.
-const char* GetModelTypeRootTag(ModelType model_type);
+// As opposed to ModelTypeToProtocolRootTag(), this returns a string that isn't
+// exposed in the sync protocol, but that is still stable and thus can be used
+// for local persistence. It is guaranteed to be lowercase.
+const char* GetModelTypeLowerCaseRootTag(ModelType model_type);
 
 // Convert a real model type to a notification type (used for
 // subscribing to server-issued notifications).  Returns true iff
diff --git a/components/sync/base/model_type_unittest.cc b/components/sync/base/model_type_unittest.cc
index 51ec969..1b8938c 100644
--- a/components/sync/base/model_type_unittest.cc
+++ b/components/sync/base/model_type_unittest.cc
@@ -116,9 +116,9 @@
   }
 }
 
-TEST_F(ModelTypeTest, ModelTypeToRootTagValues) {
+TEST_F(ModelTypeTest, ModelTypeToProtocolRootTagValues) {
   for (ModelType model_type : ProtocolTypes()) {
-    std::string root_tag = ModelTypeToRootTag(model_type);
+    std::string root_tag = ModelTypeToProtocolRootTag(model_type);
     if (IsRealDataType(model_type)) {
       EXPECT_TRUE(base::StartsWith(root_tag, "google_chrome_",
                                    base::CompareCase::INSENSITIVE_ASCII));
diff --git a/components/sync/engine/loopback_server/loopback_server.cc b/components/sync/engine/loopback_server/loopback_server.cc
index c061973a..4a228e0 100644
--- a/components/sync/engine/loopback_server/loopback_server.cc
+++ b/components/sync/engine/loopback_server/loopback_server.cc
@@ -286,7 +286,7 @@
   std::unique_ptr<LoopbackServerEntity> entity =
       PersistentPermanentEntity::CreateNew(
           syncer::BOOKMARKS, server_tag, name,
-          ModelTypeToRootTag(syncer::BOOKMARKS));
+          ModelTypeToProtocolRootTag(syncer::BOOKMARKS));
   if (!entity)
     return false;
 
diff --git a/components/sync/engine/loopback_server/loopback_server_entity.cc b/components/sync/engine/loopback_server/loopback_server_entity.cc
index 32443c185..92fe249 100644
--- a/components/sync/engine/loopback_server/loopback_server_entity.cc
+++ b/components/sync/engine/loopback_server/loopback_server_entity.cc
@@ -127,8 +127,8 @@
 
 // static
 std::string LoopbackServerEntity::GetTopLevelId(const ModelType& model_type) {
-  return LoopbackServerEntity::CreateId(model_type,
-                                        syncer::ModelTypeToRootTag(model_type));
+  return LoopbackServerEntity::CreateId(
+      model_type, syncer::ModelTypeToProtocolRootTag(model_type));
 }
 
 // static
diff --git a/components/sync/engine/loopback_server/persistent_permanent_entity.cc b/components/sync/engine/loopback_server/persistent_permanent_entity.cc
index d4030c06..2dc31ca 100644
--- a/components/sync/engine/loopback_server/persistent_permanent_entity.cc
+++ b/components/sync/engine/loopback_server/persistent_permanent_entity.cc
@@ -73,7 +73,7 @@
     return nullptr;
   }
 
-  string server_tag = syncer::ModelTypeToRootTag(model_type);
+  string server_tag = syncer::ModelTypeToProtocolRootTag(model_type);
   string name = syncer::ModelTypeToDebugString(model_type);
   string id = LoopbackServerEntity::GetTopLevelId(model_type);
   sync_pb::EntitySpecifics entity_specifics;
@@ -97,7 +97,8 @@
       current_server_entity.GetId(), current_server_entity.GetVersion(),
       model_type, current_server_entity.GetName(),
       current_server_entity.GetParentId(),
-      syncer::ModelTypeToRootTag(model_type), client_entity.specifics());
+      syncer::ModelTypeToProtocolRootTag(model_type),
+      client_entity.specifics());
 }
 
 PersistentPermanentEntity::PersistentPermanentEntity(
diff --git a/components/sync/model/blocking_model_type_store_impl.cc b/components/sync/model/blocking_model_type_store_impl.cc
index 731009d..cff9706 100644
--- a/components/sync/model/blocking_model_type_store_impl.cc
+++ b/components/sync/model/blocking_model_type_store_impl.cc
@@ -134,17 +134,17 @@
 
 // Formats key prefix for data records of |type|.
 std::string FormatDataPrefix(ModelType type) {
-  return std::string(GetModelTypeRootTag(type)) + kDataPrefix;
+  return std::string(GetModelTypeLowerCaseRootTag(type)) + kDataPrefix;
 }
 
 // Formats key prefix for metadata records of |type|.
 std::string FormatMetaPrefix(ModelType type) {
-  return std::string(GetModelTypeRootTag(type)) + kMetadataPrefix;
+  return std::string(GetModelTypeLowerCaseRootTag(type)) + kMetadataPrefix;
 }
 
 // Formats key for global metadata record of |type|.
 std::string FormatGlobalMetadataKey(ModelType type) {
-  return std::string(GetModelTypeRootTag(type)) + kGlobalMetadataKey;
+  return std::string(GetModelTypeLowerCaseRootTag(type)) + kGlobalMetadataKey;
 }
 
 BlockingModelTypeStoreImpl::BlockingModelTypeStoreImpl(
@@ -248,7 +248,8 @@
 absl::optional<ModelError>
 BlockingModelTypeStoreImpl::DeleteAllDataAndMetadata() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return backend_->DeleteDataAndMetadataForPrefix(GetModelTypeRootTag(type_));
+  return backend_->DeleteDataAndMetadataForPrefix(
+      GetModelTypeLowerCaseRootTag(type_));
 }
 
 // static
diff --git a/components/sync/test/mock_connection_manager.cc b/components/sync/test/mock_connection_manager.cc
index 06d199e1..ebafb77 100644
--- a/components/sync/test/mock_connection_manager.cc
+++ b/components/sync/test/mock_connection_manager.cc
@@ -258,7 +258,7 @@
   sync_pb::SyncEntity* ent = GetUpdateResponse()->add_entries();
   ent->set_id_string(id);
   ent->set_parent_id_string("0");
-  ent->set_server_defined_unique_tag(ModelTypeToRootTag(NIGORI));
+  ent->set_server_defined_unique_tag(ModelTypeToProtocolRootTag(NIGORI));
   ent->set_name("Nigori");
   ent->set_non_unique_name("Nigori");
   ent->set_version(version);
diff --git a/components/sync/test/mock_model_type_worker.cc b/components/sync/test/mock_model_type_worker.cc
index f9d300f8..fe4efd7 100644
--- a/components/sync/test/mock_model_type_worker.cc
+++ b/components/sync/test/mock_model_type_worker.cc
@@ -198,9 +198,10 @@
 syncer::UpdateResponseData MockModelTypeWorker::GenerateTypeRootUpdateData(
     const ModelType& model_type) {
   syncer::EntityData data;
-  data.id = syncer::ModelTypeToRootTag(model_type);
+  data.id = syncer::ModelTypeToProtocolRootTag(model_type);
   data.legacy_parent_id = "r";
-  data.server_defined_unique_tag = syncer::ModelTypeToRootTag(model_type);
+  data.server_defined_unique_tag =
+      syncer::ModelTypeToProtocolRootTag(model_type);
   syncer::AddDefaultFieldValue(model_type, &data.specifics);
   // These elements should have no effect on behavior, but we set them anyway
   // so we can test they are properly copied around the system if we want to.
diff --git a/components/sync/test/single_type_mock_server.cc b/components/sync/test/single_type_mock_server.cc
index a6276c6..34e1851 100644
--- a/components/sync/test/single_type_mock_server.cc
+++ b/components/sync/test/single_type_mock_server.cc
@@ -16,7 +16,7 @@
 
 SingleTypeMockServer::SingleTypeMockServer(ModelType type)
     : type_(type),
-      type_root_id_(ModelTypeToRootTag(type)),
+      type_root_id_(ModelTypeToProtocolRootTag(type)),
       progress_marker_token_("non_null_progress_token") {}
 
 SingleTypeMockServer::~SingleTypeMockServer() = default;
@@ -29,7 +29,7 @@
   entity.set_version(1000);
   entity.set_ctime(TimeToProtoTime(base::Time::UnixEpoch()));
   entity.set_mtime(TimeToProtoTime(base::Time::UnixEpoch()));
-  entity.set_server_defined_unique_tag(ModelTypeToRootTag(type_));
+  entity.set_server_defined_unique_tag(ModelTypeToProtocolRootTag(type_));
   entity.set_deleted(false);
   AddDefaultFieldValue(type_, entity.mutable_specifics());
 
diff --git a/components/sync_bookmarks/bookmark_model_merger.cc b/components/sync_bookmarks/bookmark_model_merger.cc
index 7282b9e..3d2c2dbc 100644
--- a/components/sync_bookmarks/bookmark_model_merger.cc
+++ b/components/sync_bookmarks/bookmark_model_merger.cc
@@ -379,7 +379,7 @@
     // Special-case the root folder to avoid recording
     // |RemoteBookmarkUpdateError::kUnsupportedPermanentFolder|.
     if (update_entity.server_defined_unique_tag ==
-        syncer::ModelTypeToRootTag(syncer::BOOKMARKS)) {
+        syncer::ModelTypeToProtocolRootTag(syncer::BOOKMARKS)) {
       ++num_valid_updates;
       continue;
     }
diff --git a/components/sync_bookmarks/bookmark_model_merger_unittest.cc b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
index 8a92bce..8064d29 100644
--- a/components/sync_bookmarks/bookmark_model_merger_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_merger_unittest.cc
@@ -1806,7 +1806,7 @@
   updates.push_back(CreateBookmarkBarNodeUpdateData());
   updates.back().entity.id = kRootNodeId;
   updates.back().entity.server_defined_unique_tag =
-      syncer::ModelTypeToRootTag(syncer::BOOKMARKS);
+      syncer::ModelTypeToProtocolRootTag(syncer::BOOKMARKS);
 
   updates.push_back(CreateUpdateResponseData(
       /*guid=*/base::GUID::GenerateRandomV4(),
diff --git a/components/sync_bookmarks/bookmark_model_type_processor.cc b/components/sync_bookmarks/bookmark_model_type_processor.cc
index 5acc24b5..30e18ba 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor.cc
+++ b/components/sync_bookmarks/bookmark_model_type_processor.cc
@@ -566,10 +566,15 @@
 
   TRACE_EVENT0("sync", "BookmarkModelTypeProcessor::OnInitialUpdateReceived");
 
+  // |updates| can contain an additional root folder. The server may or may not
+  // deliver a root node - it is not guaranteed, but this works as an
+  // approximated safeguard.
+  const size_t max_initial_updates_count = max_bookmarks_till_sync_enabled_ + 1;
+
   // Report error if count of remote updates is more than the limit.
   // Note that we are not having this check for incremental updates as it is
   // very unlikely that there will be many updates downloaded.
-  if (updates.size() > max_bookmarks_till_sync_enabled_ &&
+  if (updates.size() > max_initial_updates_count &&
       base::FeatureList::IsEnabled(syncer::kSyncEnforceBookmarksCountLimit)) {
     last_initial_merge_remote_updates_exceeded_limit_ = true;
     error_handler_.Run(
diff --git a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
index 5a36c60c..f3c9d3b 100644
--- a/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
+++ b/components/sync_bookmarks/bookmark_model_type_processor_unittest.cc
@@ -1239,6 +1239,13 @@
   syncer::UpdateResponseDataList updates =
       CreateUpdateResponseDataListForPermanentNodes();
 
+  // Entry for the root folder. The server may or may not send a root node, but
+  // the current implementation still handles it.
+  updates.push_back(CreateUpdateResponseData(
+      {kBookmarksRootId, std::string(), std::string(), std::string(),
+       syncer::ModelTypeToProtocolRootTag(syncer::BOOKMARKS)},
+      kRandomPosition, /*response_version=*/0));
+
   // Add update for another node under the bookmarks bar.
   const std::string kNodeId = "node_id";
   const std::string kTitle = "title";
@@ -1266,7 +1273,7 @@
 }
 
 TEST_F(BookmarkModelTypeProcessorTest,
-       ShouldSaveRemoteUpdatesCountDuringInitialMerge) {
+       ShouldSaveRemoteUpdatesCountExceedingLimitResultDuringInitialMerge) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(syncer::kSyncEnforceBookmarksCountLimit);
   // Set a limit of 3 bookmarks, i.e. limit it to the 3 permanent nodes.
@@ -1282,6 +1289,13 @@
   syncer::UpdateResponseDataList updates =
       CreateUpdateResponseDataListForPermanentNodes();
 
+  // Entry for the root folder. The server may or may not send a root node, but
+  // the current implementation still handles it.
+  updates.push_back(CreateUpdateResponseData(
+      {kBookmarksRootId, std::string(), std::string(), std::string(),
+       syncer::ModelTypeToProtocolRootTag(syncer::BOOKMARKS)},
+      kRandomPosition, /*response_version=*/0));
+
   // Add update for another node under the bookmarks bar.
   const std::string kNodeId = "node_id";
   const std::string kTitle = "title";
@@ -1332,6 +1346,13 @@
   syncer::UpdateResponseDataList updates =
       CreateUpdateResponseDataListForPermanentNodes();
 
+  // Entry for the root folder. The server may or may not send a root node, but
+  // the current implementation still handles it.
+  updates.push_back(CreateUpdateResponseData(
+      {kBookmarksRootId, std::string(), std::string(), std::string(),
+       syncer::ModelTypeToProtocolRootTag(syncer::BOOKMARKS)},
+      kRandomPosition, /*response_version=*/0));
+
   // Add update for another node under the bookmarks bar.
   const std::string kNodeId = "node_id";
   const std::string kTitle = "title";
@@ -1359,7 +1380,6 @@
       model_metadata.last_initial_merge_remote_updates_exceeded_limit());
 
   ResetModelTypeProcessor();
-  processor()->SetMaxBookmarksTillSyncEnabledForTest(3);
   // Expect failure.
   error_reported = false;
   processor()->ModelReadyToSync(metadata_str, schedule_save_closure()->Get(),
@@ -1379,7 +1399,7 @@
 }
 
 TEST_F(BookmarkModelTypeProcessorTest,
-       ShouldPersistRemoteBookmarksCountAcrossBrowserRestarts) {
+       ShouldPersistRemoteBookmarksCountExceedingLimitAcrossBrowserRestarts) {
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(syncer::kSyncEnforceBookmarksCountLimit);
   // Set a limit of 3 bookmarks, i.e. limit it to the 3 permanent nodes.
@@ -1398,19 +1418,14 @@
       syncer::UniquePosition::InitialPosition(
           syncer::UniquePosition::RandomSuffix());
 
-  syncer::UpdateResponseDataList updates;
-  // Add update for the permanent folders.
-  updates.push_back(
-      CreateUpdateResponseData({kBookmarkBarId, std::string(), std::string(),
-                                kBookmarksRootId, kBookmarkBarTag},
-                               kRandomPosition, /*response_version=*/0));
-  updates.push_back(
-      CreateUpdateResponseData({kOtherBookmarksId, std::string(), std::string(),
-                                kBookmarksRootId, kOtherBookmarksTag},
-                               kRandomPosition, /*response_version=*/0));
+  syncer::UpdateResponseDataList updates =
+      CreateUpdateResponseDataListForPermanentNodes();
+
+  // Entry for the root folder. The server may or may not send a root node, but
+  // the current implementation still handles it.
   updates.push_back(CreateUpdateResponseData(
-      {kMobileBookmarksId, std::string(), std::string(), kBookmarksRootId,
-       kMobileBookmarksTag},
+      {kBookmarksRootId, std::string(), std::string(), std::string(),
+       syncer::ModelTypeToProtocolRootTag(syncer::BOOKMARKS)},
       kRandomPosition, /*response_version=*/0));
 
   // Add update for another node under the bookmarks bar.
@@ -1442,7 +1457,6 @@
 
   // Simulate browser restart.
   ResetModelTypeProcessor();
-  processor()->SetMaxBookmarksTillSyncEnabledForTest(3);
   // Expect failure.
   error_reported = false;
   processor()->ModelReadyToSync(metadata_str, schedule_save_closure()->Get(),
@@ -1462,7 +1476,6 @@
 
   // Simulate browser restart again.
   ResetModelTypeProcessor();
-  processor()->SetMaxBookmarksTillSyncEnabledForTest(3);
   // Expect failure.
   error_reported = false;
   processor()->ModelReadyToSync(metadata_str, schedule_save_closure()->Get(),
diff --git a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
index 50bc518..613cba0 100644
--- a/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
+++ b/components/sync_bookmarks/bookmark_remote_updates_handler_unittest.cc
@@ -200,9 +200,9 @@
 
 syncer::UpdateResponseData CreateBookmarkRootUpdateData() {
   syncer::EntityData data;
-  data.id = syncer::ModelTypeToRootTag(syncer::BOOKMARKS);
+  data.id = syncer::ModelTypeToProtocolRootTag(syncer::BOOKMARKS);
   data.server_defined_unique_tag =
-      syncer::ModelTypeToRootTag(syncer::BOOKMARKS);
+      syncer::ModelTypeToProtocolRootTag(syncer::BOOKMARKS);
 
   data.specifics.mutable_bookmark();
 
diff --git a/components/viz/common/quads/render_pass_io.cc b/components/viz/common/quads/render_pass_io.cc
index cac75cf..654fbfb 100644
--- a/components/viz/common/quads/render_pass_io.cc
+++ b/components/viz/common/quads/render_pass_io.cc
@@ -548,7 +548,8 @@
 
   dict.SetIntKey("type", type);
   if (type != cc::FilterOperation::COLOR_MATRIX &&
-      type != cc::FilterOperation::REFERENCE) {
+      type != cc::FilterOperation::REFERENCE &&
+      type != cc::FilterOperation::OFFSET) {
     dict.SetDoubleKey("amount", filter.amount());
   }
   switch (type) {
@@ -557,8 +558,7 @@
       dict.SetKey("shape", ShapeRectsToList(filter.shape()));
       break;
     case cc::FilterOperation::DROP_SHADOW:
-      dict.SetKey("drop_shadow_offset",
-                  PointToDict(filter.drop_shadow_offset()));
+      dict.SetKey("offset", PointToDict(filter.offset()));
       dict.SetKey("drop_shadow_color",
                   SkColor4fToDict(filter.drop_shadow_color()));
       break;
@@ -576,6 +576,9 @@
       dict.SetIntKey("blur_tile_mode",
                      static_cast<int>(filter.blur_tile_mode()));
       break;
+    case cc::FilterOperation::OFFSET:
+      dict.SetKey("offset", PointToDict(filter.offset()));
+      break;
     default:
       break;
   }
@@ -593,8 +596,7 @@
   absl::optional<int> type = dict.FindInt("type");
   absl::optional<double> amount = dict.FindDouble("amount");
   absl::optional<double> outer_threshold = dict.FindDouble("outer_threshold");
-  const base::Value::Dict* drop_shadow_offset =
-      dict.FindDict("drop_shadow_offset");
+  const base::Value::Dict* offset = dict.FindDict("offset");
   const std::string* image_filter = dict.FindString("image_filter");
   const base::Value::List* matrix = dict.FindList("matrix");
   absl::optional<int> zoom_inset = dict.FindInt("zoom_inset");
@@ -609,7 +611,8 @@
       static_cast<cc::FilterOperation::FilterType>(type.value());
   filter.set_type(filter_type);
   if (filter_type != cc::FilterOperation::COLOR_MATRIX &&
-      filter_type != cc::FilterOperation::REFERENCE) {
+      filter_type != cc::FilterOperation::REFERENCE &&
+      filter_type != cc::FilterOperation::OFFSET) {
     if (!amount)
       return false;
     filter.set_amount(static_cast<float>(amount.value()));
@@ -625,11 +628,11 @@
       filter.set_shape(shape_rects);
     } break;
     case cc::FilterOperation::DROP_SHADOW: {
-      gfx::Point offset;
-      if (!drop_shadow_offset || !PointFromDict(*drop_shadow_offset, &offset)) {
+      gfx::Point drop_shadow_offset;
+      if (!offset || !PointFromDict(*offset, &drop_shadow_offset)) {
         return false;
       }
-      filter.set_drop_shadow_offset(offset);
+      filter.set_offset(drop_shadow_offset);
 
       SkColor4f t_drop_shadow_color;
       if (!ColorFromDict(dict, "drop_shadow_color", &t_drop_shadow_color)) {
@@ -659,6 +662,13 @@
       filter.set_blur_tile_mode(
           static_cast<SkTileMode>(blur_tile_mode.value()));
       break;
+    case cc::FilterOperation::OFFSET: {
+      gfx::Point filter_offset;
+      if (!offset || !PointFromDict(*offset, &filter_offset)) {
+        return false;
+      }
+      filter.set_offset(filter_offset);
+    } break;
     default:
       break;
   }
diff --git a/components/viz/common/quads/render_pass_io_unittest.cc b/components/viz/common/quads/render_pass_io_unittest.cc
index 24060a8b..78dabb53 100644
--- a/components/viz/common/quads/render_pass_io_unittest.cc
+++ b/components/viz/common/quads/render_pass_io_unittest.cc
@@ -91,7 +91,7 @@
     EXPECT_EQ(SkColors::kYellow,
               render_pass1->backdrop_filters.at(0).drop_shadow_color());
     EXPECT_EQ(gfx::Point(1.0f, 2.0f),
-              render_pass1->backdrop_filters.at(0).drop_shadow_offset());
+              render_pass1->backdrop_filters.at(0).offset());
     EXPECT_EQ(cc::FilterOperation::INVERT,
               render_pass1->backdrop_filters.at(1).type());
     EXPECT_EQ(0.64f, render_pass1->backdrop_filters.at(1).amount());
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 5bffafd..0fd4889 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -1645,6 +1645,8 @@
       // size of the RenderPassDrawQuad rect. Therefore when we find the damage
       // under the quad intersects quad render pass output rect, we extend the
       // damage rect to include the rpdq->rect.
+      // TODO(crbug/1379125): Work out how to correctly compute damage when
+      // offset backdrop filters may be involved.
 
       // For the pixel-moving foreground filters, all effects can be expanded
       // outside the RenderPassDrawQuad rect to the size of rect +
diff --git a/content/browser/devtools/protocol/page_handler.cc b/content/browser/devtools/protocol/page_handler.cc
index 1563cc2..2ecedb8 100644
--- a/content/browser/devtools/protocol/page_handler.cc
+++ b/content/browser/devtools/protocol/page_handler.cc
@@ -1579,6 +1579,8 @@
           ActivationNavigationParameterMismatch;
     case PrerenderFinalStatus::kActivatedInBackground:
       return Page::PrerenderFinalStatusEnum::ActivatedInBackground;
+    case PrerenderFinalStatus::kEmbedderHostDisallowed:
+      return Page::PrerenderFinalStatusEnum::EmbedderHostDisallowed;
   }
 }
 
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc
index 2ed1d88..14a09855 100644
--- a/content/browser/preloading/prerender/prerender_browsertest.cc
+++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -4282,7 +4282,8 @@
     feature_list_.InitWithFeaturesAndParameters(
         {{blink::features::kPrerender2,
           {{"max_num_of_running_speculation_rules",
-            base::NumberToString(MaxNumOfRunningPrerenders())}}},
+            base::NumberToString(MaxNumOfRunningPrerenders())},
+           {"embedder_blocked_hosts", "a.test,b.test,c.test"}}},
          {blink::features::kPrerender2SequentialPrerendering, {}}},
         {});
   }
@@ -4691,6 +4692,37 @@
   EXPECT_TRUE(embedder_observer.was_activated());
 }
 
+// Test that hosts in the embedder blocklist are not prerendered.
+IN_PROC_BROWSER_TEST_F(PrerenderSequentialPrerenderingBrowserTest,
+                       EmbedderHostBlocklisted) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const GURL kInitialUrl = embedded_test_server()->GetURL("/empty.html");
+  // b.test was added to embedder_blocked_hosts in the test setup.
+  const GURL kEmbedderPrerender =
+      embedded_test_server()->GetURL("b.test", "/empty.html?embedder");
+
+  ASSERT_TRUE(NavigateToURL(shell(), kInitialUrl));
+  ASSERT_EQ(shell()->web_contents()->GetLastCommittedURL(), kInitialUrl);
+
+  // Start prerendering by embedder triggered prerendering. This should be
+  // blocked because b.test is in embedder_blocked_hosts.
+  std::unique_ptr<PrerenderHandle> prerender_handle =
+      web_contents_impl()->StartPrerendering(
+          kEmbedderPrerender, PrerenderTriggerType::kEmbedder,
+          "EmbedderSuffixForTest",
+          ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
+                                    ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
+          nullptr);
+
+  EXPECT_FALSE(prerender_handle);
+  EXPECT_EQ(GetHostForUrl(kEmbedderPrerender),
+            RenderFrameHost::kNoFrameTreeNodeId);
+  histogram_tester().ExpectUniqueSample(
+      "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_"
+      "EmbedderSuffixForTest",
+      PrerenderFinalStatus::kEmbedderHostDisallowed, 1);
+}
+
 // Tests that if the running prerender is cancelled by
 // PrerenderHostRegistry::CancelHost(), the next pending prerender starts its
 // navigation.
diff --git a/content/browser/preloading/prerender/prerender_final_status.h b/content/browser/preloading/prerender/prerender_final_status.h
index 3090b83..525255c 100644
--- a/content/browser/preloading/prerender/prerender_final_status.h
+++ b/content/browser/preloading/prerender/prerender_final_status.h
@@ -75,7 +75,8 @@
   // from the initial prerendering navigation so Prerender fails to activate it.
   kActivationNavigationParameterMismatch = 50,
   kActivatedInBackground = 51,
-  kMaxValue = kActivatedInBackground,
+  kEmbedderHostDisallowed = 52,
+  kMaxValue = kEmbedderHostDisallowed,
 };
 
 #endif  // CONTENT_BROWSER_PRELOADING_PRERENDER_PRERENDER_FINAL_STATUS_H_
diff --git a/content/browser/preloading/prerender/prerender_host.cc b/content/browser/preloading/prerender/prerender_host.cc
index f8c5a0c..a3df3f5 100644
--- a/content/browser/preloading/prerender/prerender_host.cc
+++ b/content/browser/preloading/prerender/prerender_host.cc
@@ -968,6 +968,7 @@
     case PrerenderFinalStatus::kSameSiteCrossOriginNavigationNotOptIn:
     case PrerenderFinalStatus::kActivationNavigationParameterMismatch:
     case PrerenderFinalStatus::kActivatedInBackground:
+    case PrerenderFinalStatus::kEmbedderHostDisallowed:
       attempt_->SetFailureReason(ToPreloadingFailureReason(status));
       // We reset the attempt to ensure we don't update once we have reported it
       // as failure or accidentally use it for any other prerender attempts as
diff --git a/content/browser/preloading/prerender/prerender_navigation_throttle.cc b/content/browser/preloading/prerender/prerender_navigation_throttle.cc
index b1bae50a..1579552 100644
--- a/content/browser/preloading/prerender/prerender_navigation_throttle.cc
+++ b/content/browser/preloading/prerender/prerender_navigation_throttle.cc
@@ -5,6 +5,8 @@
 #include "content/browser/preloading/prerender/prerender_navigation_throttle.h"
 
 #include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
+#include "base/strings/string_split.h"
 #include "content/browser/preloading/prerender/prerender_final_status.h"
 #include "content/browser/preloading/prerender/prerender_host.h"
 #include "content/browser/preloading/prerender/prerender_host_registry.h"
@@ -15,6 +17,7 @@
 #include "content/browser/renderer_host/navigation_request.h"
 #include "content/browser/renderer_host/render_frame_host_delegate.h"
 #include "content/public/browser/prerender_trigger_type.h"
+#include "content/public/common/content_features.h"
 #include "services/network/public/mojom/parsed_headers.mojom.h"
 #include "third_party/blink/public/common/features.h"
 #include "url/origin.h"
@@ -72,6 +75,28 @@
   }
 }
 
+// Prerender2 Embedders trigger based on rules decided by the browser. Prevent
+// the browser from triggering on the hosts listed.
+// Blocked hosts are expected to be passed as a comma separated string.
+// e.g. example1.test,example2.test
+const base::FeatureParam<std::string> kPrerender2EmbedderBlockedHosts{
+    &blink::features::kPrerender2, "embedder_blocked_hosts", ""};
+
+bool ShouldSkipHostInBlockList(const GURL& url) {
+  if (!base::FeatureList::IsEnabled(blink::features::kPrerender2)) {
+    return false;
+  }
+
+  // Keep this as static because the blocked origins are served via feature
+  // parameters and are never changed until browser restart.
+  const static base::NoDestructor<std::vector<std::string>>
+      embedder_blocked_hosts(
+          base::SplitString(kPrerender2EmbedderBlockedHosts.Get(), ",",
+                            base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY));
+
+  return base::Contains(*embedder_blocked_hosts, url.host());
+}
+
 }  // namespace
 
 PrerenderNavigationThrottle::~PrerenderNavigationThrottle() = default;
@@ -155,9 +180,18 @@
     return CANCEL;
   }
 
+  GURL prerendering_url = navigation_handle()->GetURL();
+
+  if ((prerender_host->trigger_type() == PrerenderTriggerType::kEmbedder) &&
+      ShouldSkipHostInBlockList(prerendering_url)) {
+    prerender_host_registry->CancelHost(
+        frame_tree_node->frame_tree_node_id(),
+        PrerenderFinalStatus::kEmbedderHostDisallowed);
+    return CANCEL;
+  }
+
   // Allow only HTTP(S) schemes.
   // https://wicg.github.io/nav-speculation/prerendering.html#no-bad-navs
-  GURL prerendering_url = navigation_handle()->GetURL();
   if (!prerendering_url.SchemeIsHTTPOrHTTPS()) {
     prerender_host_registry->CancelHost(
         frame_tree_node->frame_tree_node_id(),
diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc
index 6b19fab..b9875db 100644
--- a/content/common/child_process_host_impl.cc
+++ b/content/common/child_process_host_impl.cc
@@ -34,7 +34,6 @@
 #include "ipc/ipc_channel_mojo.h"
 #include "ipc/ipc_logging.h"
 #include "ipc/message_filter.h"
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "services/resource_coordinator/public/mojom/memory_instrumentation/constants.mojom.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 
@@ -126,8 +125,7 @@
             kChildProcessReceiverAttachmentName),
         IPC::Channel::MODE_SERVER, this,
         base::SingleThreadTaskRunner::GetCurrentDefault(),
-        base::SingleThreadTaskRunner::GetCurrentDefault(),
-        mojo::internal::MessageQuotaChecker::MaybeCreate());
+        base::SingleThreadTaskRunner::GetCurrentDefault());
   } else if (ipc_mode_ == IpcMode::kNormal) {
     child_process_.Bind(mojo::PendingRemote<mojom::ChildProcess>(
         mojo_invitation_->AttachMessagePipe(
@@ -223,8 +221,7 @@
     channel_ = IPC::ChannelMojo::Create(
         std::move(bootstrap), IPC::Channel::MODE_SERVER, this,
         base::SingleThreadTaskRunner::GetCurrentDefault(),
-        base::SingleThreadTaskRunner::GetCurrentDefault(),
-        mojo::internal::MessageQuotaChecker::MaybeCreate());
+        base::SingleThreadTaskRunner::GetCurrentDefault());
   }
   DCHECK(channel_);
 
diff --git a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
index 60b7180..b138ced 100644
--- a/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
+++ b/content/renderer/accessibility/render_accessibility_impl_browsertest.cc
@@ -942,6 +942,11 @@
   }
   EXPECT_TRUE(input_range.IsFocused());
 
+  // Increment/decrement actions produce synthesized keydown and keyup events,
+  // and the keyup event is delayed 100ms to look more natural. We need to wait
+  // for them to happen to finish the test cleanly in the TearDown phase.
+  task_environment_.FastForwardBy(base::Seconds(1));
+
   gfx::RectF expected_bounds;
   blink::WebAXObject offset_container;
   gfx::Transform container_transform;
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.cc b/content/renderer/media/renderer_webaudiodevice_impl.cc
index 1163ac7..bf8eee3a2 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl.cc
+++ b/content/renderer/media/renderer_webaudiodevice_impl.cc
@@ -18,6 +18,7 @@
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
 #include "media/audio/null_audio_sink.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/limits.h"
 #include "media/base/silent_sink_suspender.h"
@@ -263,10 +264,11 @@
     silent_sink_suspender_->SetDetectSilence(enable_silence_detection);
 }
 
-int RendererWebAudioDeviceImpl::Render(base::TimeDelta delay,
-                                       base::TimeTicks delay_timestamp,
-                                       int prior_frames_skipped,
-                                       media::AudioBus* dest) {
+int RendererWebAudioDeviceImpl::Render(
+    base::TimeDelta delay,
+    base::TimeTicks delay_timestamp,
+    const media::AudioGlitchInfo& glitch_info,
+    media::AudioBus* dest) {
   // Wrap the output pointers using WebVector.
   CHECK_EQ(dest->channels(), sink_params_.channels());
   for (int i = 0; i < dest->channels(); ++i)
diff --git a/content/renderer/media/renderer_webaudiodevice_impl.h b/content/renderer/media/renderer_webaudiodevice_impl.h
index 62fc7d37..302fa0e 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl.h
+++ b/content/renderer/media/renderer_webaudiodevice_impl.h
@@ -66,7 +66,7 @@
   // AudioRendererSink::RenderCallback implementation.
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const media::AudioGlitchInfo& glitch_info,
              media::AudioBus* dest) override;
 
   void OnRenderError() override;
diff --git a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
index ee3284d..71d91f6d0 100644
--- a/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
+++ b/content/renderer/media/renderer_webaudiodevice_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "media/base/audio_capturer_source.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/base/limits.h"
 #include "media/base/mock_audio_renderer_sink.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -296,7 +297,7 @@
   SetupDevice(blink::WebAudioSinkDescriptor(kFrameToken));
   webaudio_device_->Start();
   mock_audio_renderer_sink_->callback_->Render(
-      base::TimeDelta::Min(), base::TimeTicks::Now(), 0,
+      base::TimeDelta::Min(), base::TimeTicks::Now(), {},
       media::AudioBus::Create(1, kHardwareBufferSize).get());
   webaudio_device_->Stop();
 }
@@ -341,12 +342,12 @@
   SetupDevice(blink::WebAudioSinkDescriptor(kFrameToken));
   webaudio_device_->Start();
   mock_audio_renderer_sink_->callback_->Render(
-      base::TimeDelta::Min(), base::TimeTicks::Now(), 0,
+      base::TimeDelta::Min(), base::TimeTicks::Now(), {},
       media::AudioBus::Create(1, kHardwareBufferSize).get());
   webaudio_device_->Stop();
   webaudio_device_->Start();
   mock_audio_renderer_sink_->callback_->Render(
-      base::TimeDelta::Min(), base::TimeTicks::Now(), 0,
+      base::TimeDelta::Min(), base::TimeTicks::Now(), {},
       media::AudioBus::Create(1, kHardwareBufferSize).get());
   webaudio_device_->Stop();
 }
diff --git a/content/web_test/renderer/web_ax_object_proxy.cc b/content/web_test/renderer/web_ax_object_proxy.cc
index 852df0c..dbca534c 100644
--- a/content/web_test/renderer/web_ax_object_proxy.cc
+++ b/content/web_test/renderer/web_ax_object_proxy.cc
@@ -398,6 +398,7 @@
       .SetMethod("isAttributeSettable", &WebAXObjectProxy::IsAttributeSettable)
       .SetMethod("isPressActionSupported",
                  &WebAXObjectProxy::IsPressActionSupported)
+      .SetMethod("hasDefaultAction", &WebAXObjectProxy::HasDefaultAction)
       .SetMethod("parentElement", &WebAXObjectProxy::ParentElement)
       .SetMethod("increment", &WebAXObjectProxy::Increment)
       .SetMethod("decrement", &WebAXObjectProxy::Decrement)
@@ -1375,6 +1376,11 @@
   return accessibility_object_.Action() == ax::mojom::DefaultActionVerb::kPress;
 }
 
+bool WebAXObjectProxy::HasDefaultAction() {
+  UpdateLayout();
+  return accessibility_object_.Action() != ax::mojom::DefaultActionVerb::kNone;
+}
+
 v8::Local<v8::Object> WebAXObjectProxy::ParentElement() {
   UpdateLayout();
   blink::WebAXObject parent_object = accessibility_object_.ParentObject();
diff --git a/content/web_test/renderer/web_ax_object_proxy.h b/content/web_test/renderer/web_ax_object_proxy.h
index bf3a7a4e..5622efa 100644
--- a/content/web_test/renderer/web_ax_object_proxy.h
+++ b/content/web_test/renderer/web_ax_object_proxy.h
@@ -197,6 +197,7 @@
   bool IsPressActionSupported();
   bool IsIncrementActionSupported();
   bool IsDecrementActionSupported();
+  bool HasDefaultAction();
   v8::Local<v8::Object> ParentElement();
   void Increment();
   void Decrement();
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index 6959f42..5fc45e0 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -1802,6 +1802,7 @@
   DEVELOPERPRIVATE_UPDATESITEACCESS = 1739,
   AUTOTESTPRIVATE_REFRESHREMOTECOMMANDS = 1740,
   FILESYSTEMPROVIDERINTERNAL_RESPONDTOMOUNTREQUEST = 1741,
+  OS_DIAGNOSTICS_RUNEMMCLIFETIMEROUTINE = 1742,
   // Last entry: Add new entries above, then run:
   // tools/metrics/histograms/update_extension_histograms.py
   ENUM_BOUNDARY
diff --git a/fuchsia_web/runners/cast/cast_runner.cc b/fuchsia_web/runners/cast/cast_runner.cc
index de3e47a..ac2d410f 100644
--- a/fuchsia_web/runners/cast/cast_runner.cc
+++ b/fuchsia_web/runners/cast/cast_runner.cc
@@ -249,13 +249,14 @@
 
 }  // namespace
 
-CastRunner::CastRunner(WebInstanceHost* web_instance_host, bool is_headless)
+CastRunner::CastRunner(WebInstanceHost& web_instance_host, Options options)
     : web_instance_host_(web_instance_host),
-      is_headless_(is_headless),
+      is_headless_(options.headless),
+      disable_codegen_(options.disable_codegen),
       main_services_(std::make_unique<base::FilteredServiceDirectory>(
           base::ComponentContextForProcess()->svc())),
       main_context_(std::make_unique<WebContentRunner>(
-          web_instance_host_,
+          *web_instance_host_,
           base::BindRepeating(&CastRunner::GetMainWebInstanceConfig,
                               base::Unretained(this)))),
       isolated_services_(std::make_unique<base::FilteredServiceDirectory>(
@@ -428,6 +429,11 @@
         fuchsia::web::ContextFeatureFlags::VULKAN;
   }
 
+  if (disable_codegen_) {
+    *config.params.mutable_features() |=
+        fuchsia::web::ContextFeatureFlags::DISABLE_DYNAMIC_CODE_GENERATION;
+  }
+
   // When tests require that VULKAN be disabled, DRM must also be disabled.
   if (disable_vulkan_for_test_) {
     *config.params.mutable_features() &=
@@ -545,8 +551,8 @@
 WebContentRunner* CastRunner::CreateIsolatedRunner(
     WebContentRunner::WebInstanceConfig config) {
   // Create an isolated context which will own the CastComponent.
-  auto context =
-      std::make_unique<WebContentRunner>(web_instance_host_, std::move(config));
+  auto context = std::make_unique<WebContentRunner>(*web_instance_host_,
+                                                    std::move(config));
   context->SetOnEmptyCallback(
       base::BindOnce(&CastRunner::OnIsolatedContextEmpty,
                      base::Unretained(this), base::Unretained(context.get())));
diff --git a/fuchsia_web/runners/cast/cast_runner.h b/fuchsia_web/runners/cast/cast_runner.h
index 28c9aa1..bbf2a4a 100644
--- a/fuchsia_web/runners/cast/cast_runner.h
+++ b/fuchsia_web/runners/cast/cast_runner.h
@@ -32,14 +32,23 @@
                          public chromium::cast::DataReset,
                          public PendingCastComponent::Delegate {
  public:
+  struct Options {
+    // Set to true to run components without generating output via Scenic.
+    bool headless = false;
+
+    // Set to true to run components without without web optimizations (e.g.
+    // JavaScript Just-In-Time compilation) or features (e.g. WebAssembly) that
+    // require dynamic code generation.
+    bool disable_codegen = false;
+  };
+
   static constexpr uint16_t kRemoteDebuggingPort = 9222;
 
   // Creates the Runner for Cast components.
-  // |web_instance_host|: Used to create an isolated web_instance
-  //     Component in which to host the fuchsia.web.Context.
-  // |is_headless|: True if this instance should create Contexts with the
-  //                HEADLESS feature set.
-  CastRunner(WebInstanceHost* web_instance_host, bool is_headless);
+  // `web_instance_host` is used to create a "main" instance to host Cast apps
+  // and serve `FrameHost` instances, and isolated containers for apps that
+  // need them.
+  CastRunner(WebInstanceHost& web_instance_host, Options options);
   ~CastRunner() override;
 
   CastRunner(const CastRunner&) = delete;
@@ -124,11 +133,15 @@
   bool WasPersistedCacheErased();
 
   // Passed to WebContentRunners to use to create web_instance Components.
-  WebInstanceHost* const web_instance_host_;
+  const raw_ref<WebInstanceHost> web_instance_host_;
 
   // True if this Runner uses Context(s) with the HEADLESS feature set.
   const bool is_headless_;
 
+  // True if this Runner should create web Contexts with dynamic code generation
+  // disabled.
+  const bool disable_codegen_;
+
   // Holds the main fuchsia.web.Context used to host CastComponents.
   // Note that although |main_context_| is actually a WebContentRunner, that is
   // only being used to maintain the Context for the hosted components.
diff --git a/fuchsia_web/runners/cast/main.cc b/fuchsia_web/runners/cast/main.cc
index dfa624d..1b3db1d 100644
--- a/fuchsia_web/runners/cast/main.cc
+++ b/fuchsia_web/runners/cast/main.cc
@@ -42,6 +42,9 @@
 // Config-data key to enable the fuchsia.web.FrameHost provider component.
 constexpr char kFrameHostConfigKey[] = "enable-frame-host-component";
 
+// Config-data key for disable dynamic code generation by the web runtime.
+constexpr char kDisableCodeGenConfigKey[] = "disable-codegen";
+
 // Returns the value of |config_key| or false if it is not set.
 bool GetConfigBool(base::StringPiece config_key) {
   const absl::optional<base::Value::Dict>& config =
@@ -136,10 +139,11 @@
 
   // Publish the fuchsia.component.runner.ComponentRunner for Cast apps.
   WebInstanceHost web_instance_host;
-  const bool enable_headless =
-      command_line->HasSwitch(kForceHeadlessForTestsSwitch) ||
-      GetConfigBool(kHeadlessConfigKey);
-  CastRunner runner(&web_instance_host, enable_headless);
+  CastRunner runner(
+      web_instance_host,
+      {.headless = command_line->HasSwitch(kForceHeadlessForTestsSwitch) ||
+                   GetConfigBool(kHeadlessConfigKey),
+       .disable_codegen = GetConfigBool(kDisableCodeGenConfigKey)});
   const base::ScopedServiceBinding<fuchsia::component::runner::ComponentRunner>
       runner_binding(outgoing_directory, &runner);
 
diff --git a/fuchsia_web/runners/common/web_content_runner.cc b/fuchsia_web/runners/common/web_content_runner.cc
index 7db3e95..8c4b9a7 100644
--- a/fuchsia_web/runners/common/web_content_runner.cc
+++ b/fuchsia_web/runners/common/web_content_runner.cc
@@ -53,16 +53,15 @@
 WebContentRunner::WebInstanceConfig::operator=(WebInstanceConfig&&) = default;
 
 WebContentRunner::WebContentRunner(
-    WebInstanceHost* web_instance_host,
+    WebInstanceHost& web_instance_host,
     GetWebInstanceConfigCallback get_web_instance_config_callback)
     : web_instance_host_(web_instance_host),
       get_web_instance_config_callback_(
           std::move(get_web_instance_config_callback)) {
-  DCHECK(web_instance_host_);
   DCHECK(get_web_instance_config_callback_);
 }
 
-WebContentRunner::WebContentRunner(WebInstanceHost* web_instance_host,
+WebContentRunner::WebContentRunner(WebInstanceHost& web_instance_host,
                                    WebInstanceConfig web_instance_config)
     : web_instance_host_(web_instance_host) {
   CreateWebInstanceAndContext(std::move(web_instance_config));
diff --git a/fuchsia_web/runners/common/web_content_runner.h b/fuchsia_web/runners/common/web_content_runner.h
index b75a7aa..461c52e 100644
--- a/fuchsia_web/runners/common/web_content_runner.h
+++ b/fuchsia_web/runners/common/web_content_runner.h
@@ -43,12 +43,12 @@
   // |get_web_instance_config_callback|: Returns parameters for the Runner's
   //     fuchsia.web.Context.
   WebContentRunner(
-      WebInstanceHost* web_instance_host,
+      WebInstanceHost& web_instance_host,
       GetWebInstanceConfigCallback get_web_instance_config_callback);
 
   // Creates a Runner using a Context configured with `web_instance_config`.
   // The Runner becomes non-functional if the Context terminates.
-  WebContentRunner(WebInstanceHost* web_instance_host,
+  WebContentRunner(WebInstanceHost& web_instance_host,
                    WebInstanceConfig web_instance_config);
 
   ~WebContentRunner() override;
@@ -106,7 +106,7 @@
   // Starts the web_instance and connects |context_| to it.
   void CreateWebInstanceAndContext(WebInstanceConfig web_instance_config);
 
-  WebInstanceHost* const web_instance_host_;
+  const raw_ref<WebInstanceHost> web_instance_host_;
   const GetWebInstanceConfigCallback get_web_instance_config_callback_;
 
   // If set, invoked whenever a WebComponent is created.
diff --git a/fuchsia_web/runners/web/main.cc b/fuchsia_web/runners/web/main.cc
index 810914d..d8b427b 100644
--- a/fuchsia_web/runners/web/main.cc
+++ b/fuchsia_web/runners/web/main.cc
@@ -69,7 +69,7 @@
   LogComponentStartWithVersion("web_runner");
 
   WebInstanceHost web_instance_host;
-  WebContentRunner runner(&web_instance_host,
+  WebContentRunner runner(web_instance_host,
                           base::BindRepeating(&GetWebInstanceConfig));
   base::ScopedServiceBinding<fuchsia::sys::Runner> binding(
       base::ComponentContextForProcess()->outgoing().get(), &runner);
diff --git a/fuchsia_web/webengine/renderer/web_engine_audio_output_device.cc b/fuchsia_web/webengine/renderer/web_engine_audio_output_device.cc
index b68df4a..18b36f2 100644
--- a/fuchsia_web/webengine/renderer/web_engine_audio_output_device.cc
+++ b/fuchsia_web/webengine/renderer/web_engine_audio_output_device.cc
@@ -11,6 +11,7 @@
 #include "base/no_destructor.h"
 #include "base/threading/thread.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_timestamp_helper.h"
 
 namespace {
@@ -419,7 +420,7 @@
       return;
 
     frames_filled =
-        callback_->Render(playback_time - now, now, 0, audio_bus_.get());
+        callback_->Render(playback_time - now, now, {}, audio_bus_.get());
   }
 
   if (frames_filled) {
diff --git a/fuchsia_web/webengine/renderer/web_engine_audio_output_device_test.cc b/fuchsia_web/webengine/renderer/web_engine_audio_output_device_test.cc
index 1d21a91..98152e3 100644
--- a/fuchsia_web/webengine/renderer/web_engine_audio_output_device_test.cc
+++ b/fuchsia_web/webengine/renderer/web_engine_audio_output_device_test.cc
@@ -34,10 +34,9 @@
   // AudioRendererSink::Renderer interface.
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const media::AudioGlitchInfo& audio_glitch_info,
              media::AudioBus* dest) override {
     EXPECT_EQ(dest->channels(), kNumChannels);
-    frames_skipped_ += prior_frames_skipped;
     frames_rendered_ += dest->frames();
 
     EXPECT_GT(delay, base::TimeDelta());
@@ -52,7 +51,6 @@
   int frames_rendered() const { return frames_rendered_; }
   void reset_frames_rendered() { frames_rendered_ = 0; }
 
-  int frames_skipped() const { return frames_skipped_; }
   int num_render_errors() const { return num_render_errors_; }
 
   base::TimeTicks last_presentation_time() const {
@@ -61,7 +59,6 @@
 
  private:
   int frames_rendered_ = 0;
-  int frames_skipped_ = 0;
   int num_render_errors_ = 0;
   base::TimeTicks last_presentation_time_;
 };
@@ -161,7 +158,6 @@
   for (int i = 0; i < 3; ++i) {
     task_environment_.FastForwardBy(kPeriod);
     EXPECT_EQ(renderer_.frames_rendered(), kFramesPerPeriod);
-    EXPECT_EQ(renderer_.frames_skipped(), 0);
     renderer_.reset_frames_rendered();
   }
 }
@@ -175,7 +171,6 @@
   // Render().
   task_environment_.FastForwardBy(kPeriod);
   EXPECT_EQ(renderer_.frames_rendered(), kFramesPerPeriod);
-  EXPECT_EQ(renderer_.frames_skipped(), 0);
   renderer_.reset_frames_rendered();
 
   // Render() should not be called while paused.
@@ -187,7 +182,6 @@
   output_device_->Play();
   task_environment_.FastForwardBy(kPeriod);
   EXPECT_GT(renderer_.frames_rendered(), 0);
-  EXPECT_EQ(renderer_.frames_skipped(), 0);
 }
 
 TEST_F(WebEngineAudioOutputDeviceTest, Underflow) {
@@ -201,14 +195,12 @@
   task_environment_.AdvanceClock(kPeriod * 2);
   task_environment_.RunUntilIdle();
   EXPECT_EQ(renderer_.frames_rendered(), kFramesPerPeriod * 2);
-  EXPECT_EQ(renderer_.frames_skipped(), 0);
   renderer_.reset_frames_rendered();
 
   // Advance time by 100ms, causing some frames to be skipped.
   task_environment_.AdvanceClock(kPeriod * 10);
   task_environment_.RunUntilIdle();
   EXPECT_EQ(renderer_.frames_rendered(), kFramesPerPeriod * 3);
-  EXPECT_EQ(renderer_.frames_skipped(), 0);
   renderer_.reset_frames_rendered();
 
   ValidatePresentationTime();
diff --git a/fuchsia_web/webinstance_host/web_instance_host.cc b/fuchsia_web/webinstance_host/web_instance_host.cc
index cff5befc..d59b86e1 100644
--- a/fuchsia_web/webinstance_host/web_instance_host.cc
+++ b/fuchsia_web/webinstance_host/web_instance_host.cc
@@ -104,10 +104,10 @@
 // process, and the calling thread must have an async_dispatcher.
 void RegisterWebInstanceProductData() {
   // LINT.IfChange(web_engine_crash_product_name)
-  constexpr char kCrashProductName[] = "FuchsiaWebEngine";
+  static constexpr char kCrashProductName[] = "FuchsiaWebEngine";
   // LINT.ThenChange(//fuchsia_web/webengine/context_provider_main.cc:web_engine_crash_product_name)
 
-  constexpr char kFeedbackAnnotationsNamespace[] = "web-engine";
+  static constexpr char kFeedbackAnnotationsNamespace[] = "web-engine";
 
   fuchsia_component_support::RegisterProductDataForCrashReporting(
       kWebInstanceComponentUrl, kCrashProductName);
@@ -122,16 +122,16 @@
 // |value|, otherwise the switch will be set to |value|.
 void AppendToSwitch(base::StringPiece switch_name,
                     base::StringPiece value,
-                    base::CommandLine* command_line) {
-  if (!command_line->HasSwitch(switch_name)) {
-    command_line->AppendSwitchNative(switch_name, value);
+                    base::CommandLine& command_line) {
+  if (!command_line.HasSwitch(switch_name)) {
+    command_line.AppendSwitchNative(switch_name, value);
     return;
   }
 
-  std::string new_value = base::StrCat(
-      {command_line->GetSwitchValueASCII(switch_name), ",", value});
-  command_line->RemoveSwitch(switch_name);
-  command_line->AppendSwitchNative(switch_name, new_value);
+  std::string new_value =
+      base::StrCat({command_line.GetSwitchValueASCII(switch_name), ",", value});
+  command_line.RemoveSwitch(switch_name);
+  command_line.AppendSwitchNative(switch_name, new_value);
 }
 
 // File names must not contain directory separators, nor match the special
@@ -149,89 +149,89 @@
   return true;
 }
 
-bool HandleDataDirectoryParam(fuchsia::web::CreateContextParams* params,
-                              base::CommandLine* launch_args,
-                              fuchsia::sys::LaunchInfo* launch_info) {
-  if (!params->has_data_directory()) {
+bool HandleDataDirectoryParam(fuchsia::web::CreateContextParams& params,
+                              base::CommandLine& launch_args,
+                              fuchsia::sys::LaunchInfo& launch_info) {
+  if (!params.has_data_directory()) {
     // Caller requested a web instance without any peristence.
-    launch_args->AppendSwitch(switches::kIncognito);
+    launch_args.AppendSwitch(switches::kIncognito);
     return true;
   }
 
-  launch_info->flat_namespace->paths.push_back(
+  launch_info.flat_namespace->paths.push_back(
       base::kPersistedDataDirectoryPath);
-  launch_info->flat_namespace->directories.push_back(
-      std::move(*params->mutable_data_directory()));
-  if (params->has_data_quota_bytes()) {
-    launch_args->AppendSwitchNative(
+  launch_info.flat_namespace->directories.push_back(
+      std::move(*params.mutable_data_directory()));
+  if (params.has_data_quota_bytes()) {
+    launch_args.AppendSwitchNative(
         switches::kDataQuotaBytes,
-        base::NumberToString(params->data_quota_bytes()));
+        base::NumberToString(params.data_quota_bytes()));
   }
 
   return true;
 }
 
-bool HandleCdmDataDirectoryParam(fuchsia::web::CreateContextParams* params,
-                                 base::CommandLine* launch_args,
-                                 fuchsia::sys::LaunchInfo* launch_info) {
-  if (!params->has_cdm_data_directory())
+bool HandleCdmDataDirectoryParam(fuchsia::web::CreateContextParams& params,
+                                 base::CommandLine& launch_args,
+                                 fuchsia::sys::LaunchInfo& launch_info) {
+  if (!params.has_cdm_data_directory())
     return true;
 
   const char kCdmDataPath[] = "/cdm_data";
 
-  launch_args->AppendSwitchNative(switches::kCdmDataDirectory, kCdmDataPath);
-  launch_info->flat_namespace->paths.push_back(kCdmDataPath);
-  launch_info->flat_namespace->directories.push_back(
-      std::move(*params->mutable_cdm_data_directory()));
-  if (params->has_cdm_data_quota_bytes()) {
-    launch_args->AppendSwitchNative(
+  launch_args.AppendSwitchNative(switches::kCdmDataDirectory, kCdmDataPath);
+  launch_info.flat_namespace->paths.push_back(kCdmDataPath);
+  launch_info.flat_namespace->directories.push_back(
+      std::move(*params.mutable_cdm_data_directory()));
+  if (params.has_cdm_data_quota_bytes()) {
+    launch_args.AppendSwitchNative(
         switches::kCdmDataQuotaBytes,
-        base::NumberToString(params->cdm_data_quota_bytes()));
+        base::NumberToString(params.cdm_data_quota_bytes()));
   }
 
   return true;
 }
 
-bool HandleUserAgentParams(fuchsia::web::CreateContextParams* params,
-                           base::CommandLine* launch_args) {
-  if (!params->has_user_agent_product()) {
-    if (params->has_user_agent_version()) {
+bool HandleUserAgentParams(const fuchsia::web::CreateContextParams& params,
+                           base::CommandLine& launch_args) {
+  if (!params.has_user_agent_product()) {
+    if (params.has_user_agent_version()) {
       LOG(ERROR) << "Embedder version without product.";
       return false;
     }
     return true;
   }
 
-  if (!net::HttpUtil::IsToken(params->user_agent_product())) {
+  if (!net::HttpUtil::IsToken(params.user_agent_product())) {
     LOG(ERROR) << "Invalid embedder product.";
     return false;
   }
 
-  std::string product_and_version(params->user_agent_product());
-  if (params->has_user_agent_version()) {
-    if (!net::HttpUtil::IsToken(params->user_agent_version())) {
+  std::string product_and_version(params.user_agent_product());
+  if (params.has_user_agent_version()) {
+    if (!net::HttpUtil::IsToken(params.user_agent_version())) {
       LOG(ERROR) << "Invalid embedder version.";
       return false;
     }
-    base::StrAppend(&product_and_version, {"/", params->user_agent_version()});
+    base::StrAppend(&product_and_version, {"/", params.user_agent_version()});
   }
-  launch_args->AppendSwitchNative(switches::kUserAgentProductAndVersion,
-                                  std::move(product_and_version));
+  launch_args.AppendSwitchNative(switches::kUserAgentProductAndVersion,
+                                 std::move(product_and_version));
   return true;
 }
 
 void HandleUnsafelyTreatInsecureOriginsAsSecureParam(
-    fuchsia::web::CreateContextParams* params,
-    base::CommandLine* launch_args) {
-  if (!params->has_unsafely_treat_insecure_origins_as_secure())
+    const fuchsia::web::CreateContextParams& params,
+    base::CommandLine& launch_args) {
+  if (!params.has_unsafely_treat_insecure_origins_as_secure())
     return;
 
   const std::vector<std::string>& insecure_origins =
-      params->unsafely_treat_insecure_origins_as_secure();
+      params.unsafely_treat_insecure_origins_as_secure();
   for (auto origin : insecure_origins) {
 #if BUILDFLAG(ENABLE_CAST_RECEIVER)
     if (origin == switches::kAllowRunningInsecureContent) {
-      launch_args->AppendSwitch(switches::kAllowRunningInsecureContent);
+      launch_args.AppendSwitch(switches::kAllowRunningInsecureContent);
       continue;
     }
     if (origin == kDisableMixedContentAutoupgradeOrigin) {
@@ -247,31 +247,54 @@
   }
 }
 
-void HandleCorsExemptHeadersParam(fuchsia::web::CreateContextParams* params,
-                                  base::CommandLine* launch_args) {
-  if (!params->has_cors_exempt_headers())
+void HandleCorsExemptHeadersParam(
+    const fuchsia::web::CreateContextParams& params,
+    base::CommandLine& launch_args) {
+  if (!params.has_cors_exempt_headers())
     return;
 
   std::vector<base::StringPiece> cors_exempt_headers;
-  cors_exempt_headers.reserve(params->cors_exempt_headers().size());
-  for (const auto& header : params->cors_exempt_headers()) {
+  cors_exempt_headers.reserve(params.cors_exempt_headers().size());
+  for (const auto& header : params.cors_exempt_headers()) {
     cors_exempt_headers.push_back(BytesAsString(header));
   }
 
-  launch_args->AppendSwitchNative(switches::kCorsExemptHeaders,
-                                  base::JoinString(cors_exempt_headers, ","));
+  launch_args.AppendSwitchNative(switches::kCorsExemptHeaders,
+                                 base::JoinString(cors_exempt_headers, ","));
 }
 
-bool HandleContentDirectoriesParam(fuchsia::web::CreateContextParams* params,
-                                   base::CommandLine* launch_args,
-                                   fuchsia::sys::LaunchInfo* launch_info) {
-  DCHECK(launch_info);
-  DCHECK(launch_info->flat_namespace);
+void HandleDisableCodeGenerationParam(
+    fuchsia::web::ContextFeatureFlags features,
+    base::CommandLine& launch_args) {
+  if ((features &
+       fuchsia::web::ContextFeatureFlags::DISABLE_DYNAMIC_CODE_GENERATION) !=
+      fuchsia::web::ContextFeatureFlags::DISABLE_DYNAMIC_CODE_GENERATION) {
+    return;
+  }
 
-  if (!params->has_content_directories())
+  // These flag constants must match the values defined in Blink and V8,
+  // respectively. They are duplicated here rather than creating dependencies
+  // of `WebInstanceHost` uses on those sub-projects.
+  static constexpr char kJavaScriptFlags[] = "js-flags";
+  static constexpr char kV8JitlessFlag[] = "--jitless";
+
+  // Add the JIT-less option to the comma-separated set of V8 flags passed to
+  // Blink.
+  AppendToSwitch(kJavaScriptFlags, kV8JitlessFlag, launch_args);
+
+  // TODO(crbug.com/1290907): Disable use of VmexResource in this case, once
+  // migrated off of ambient VMEX.
+}
+
+bool HandleContentDirectoriesParam(fuchsia::web::CreateContextParams& params,
+                                   base::CommandLine& launch_args,
+                                   fuchsia::sys::LaunchInfo& launch_info) {
+  DCHECK(launch_info.flat_namespace);
+
+  if (!params.has_content_directories())
     return true;
 
-  auto* directories = params->mutable_content_directories();
+  auto* directories = params.mutable_content_directories();
   for (size_t i = 0; i < directories->size(); ++i) {
     fuchsia::web::ContentDirectoryProvider& directory = directories->at(i);
 
@@ -281,19 +304,19 @@
     }
 
     const base::FilePath kContentDirectories("/content-directories");
-    launch_info->flat_namespace->paths.push_back(
+    launch_info.flat_namespace->paths.push_back(
         kContentDirectories.Append(directory.name()).value());
-    launch_info->flat_namespace->directories.push_back(
+    launch_info.flat_namespace->directories.push_back(
         std::move(*directory.mutable_directory()));
   }
 
-  launch_args->AppendSwitch(switches::kEnableContentDirectories);
+  launch_args.AppendSwitch(switches::kEnableContentDirectories);
 
   return true;
 }
 
 bool HandleKeyboardFeatureFlags(fuchsia::web::ContextFeatureFlags features,
-                                base::CommandLine* launch_args) {
+                                base::CommandLine& launch_args) {
   const bool enable_keyboard =
       (features & fuchsia::web::ContextFeatureFlags::KEYBOARD) ==
       fuchsia::web::ContextFeatureFlags::KEYBOARD;
@@ -570,7 +593,7 @@
     // Vulkan requires use of SkiaRenderer, configured to a use Vulkan context.
     launch_args.AppendSwitch(switches::kUseVulkan);
     AppendToSwitch(switches::kEnableFeatures, features::kVulkan.name,
-                   &launch_args);
+                   launch_args);
     launch_args.AppendSwitchASCII(switches::kUseGL,
                                   gl::kGLImplementationANGLEName);
   } else {
@@ -632,27 +655,28 @@
     }
 
     AppendToSwitch(switches::kDisableFeatures,
-                   features::kEnableSoftwareOnlyVideoCodecs.name, &launch_args);
+                   features::kEnableSoftwareOnlyVideoCodecs.name, launch_args);
   }
 
-  if (!HandleCdmDataDirectoryParam(&params, &launch_args, &launch_info)) {
+  if (!HandleCdmDataDirectoryParam(params, launch_args, launch_info)) {
     return ZX_ERR_INVALID_ARGS;
   }
-  if (!HandleDataDirectoryParam(&params, &launch_args, &launch_info)) {
+  if (!HandleDataDirectoryParam(params, launch_args, launch_info)) {
     return ZX_ERR_INVALID_ARGS;
   }
-  if (!HandleContentDirectoriesParam(&params, &launch_args, &launch_info)) {
+  if (!HandleContentDirectoriesParam(params, launch_args, launch_info)) {
     return ZX_ERR_INVALID_ARGS;
   }
-  if (!HandleUserAgentParams(&params, &launch_args)) {
+  if (!HandleUserAgentParams(params, launch_args)) {
     return ZX_ERR_INVALID_ARGS;
   }
-  if (!HandleKeyboardFeatureFlags(features, &launch_args)) {
+  if (!HandleKeyboardFeatureFlags(features, launch_args)) {
     return ZX_ERR_INVALID_ARGS;
   }
 
-  HandleUnsafelyTreatInsecureOriginsAsSecureParam(&params, &launch_args);
-  HandleCorsExemptHeadersParam(&params, &launch_args);
+  HandleUnsafelyTreatInsecureOriginsAsSecureParam(params, launch_args);
+  HandleCorsExemptHeadersParam(params, launch_args);
+  HandleDisableCodeGenerationParam(features, launch_args);
 
   // In tests the ContextProvider is configured to log to stderr, so clone the
   // handle to allow web instances to also log there.
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
index dbff3db..2057aa2 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
@@ -88,6 +88,28 @@
   return viz::SharedImageFormat::SinglePlane(format);
 }
 
+WGPUTextureFormat DXGIToWGPUFormat(DXGI_FORMAT dxgi_format) {
+  switch (dxgi_format) {
+    case DXGI_FORMAT_R8G8B8A8_UNORM:
+      return WGPUTextureFormat_RGBA8Unorm;
+    case DXGI_FORMAT_B8G8R8A8_UNORM:
+      return WGPUTextureFormat_BGRA8Unorm;
+    case DXGI_FORMAT_R8_UNORM:
+      return WGPUTextureFormat_R8Unorm;
+    case DXGI_FORMAT_R8G8_UNORM:
+      return WGPUTextureFormat_RG8Unorm;
+    case DXGI_FORMAT_R16G16B16A16_FLOAT:
+      return WGPUTextureFormat_RGBA16Float;
+    case DXGI_FORMAT_R10G10B10A2_UNORM:
+      return WGPUTextureFormat_RGB10A2Unorm;
+    case DXGI_FORMAT_NV12:
+      return WGPUTextureFormat_R8BG8Biplanar420Unorm;
+    default:
+      NOTREACHED();
+      return WGPUTextureFormat_Undefined;
+  }
+}
+
 gfx::Size PlaneSize(DXGI_FORMAT dxgi_format,
                     const gfx::Size& size,
                     size_t plane) {
@@ -546,10 +568,14 @@
       WGPUTextureUsage_TextureBinding | WGPUTextureUsage_RenderAttachment;
   switch (wgpu_format) {
     case WGPUTextureFormat_BGRA8Unorm:
+    case WGPUTextureFormat_R8Unorm:
+    case WGPUTextureFormat_RG8Unorm:
       return kBasicUsage;
     case WGPUTextureFormat_RGBA8Unorm:
     case WGPUTextureFormat_RGBA16Float:
       return kBasicUsage | WGPUTextureUsage_StorageBinding;
+    case WGPUTextureFormat_R8BG8Biplanar420Unorm:
+      return WGPUTextureUsage_TextureBinding;
     default:
       return WGPUTextureUsage_None;
   }
@@ -569,21 +595,31 @@
         device);
   }
 #endif
-  const WGPUTextureFormat wgpu_format = ToWGPUFormat(format());
+  D3D11_TEXTURE2D_DESC desc;
+  d3d11_texture_->GetDesc(&desc);
+  const WGPUTextureFormat wgpu_format = DXGIToWGPUFormat(desc.Format);
   if (wgpu_format == WGPUTextureFormat_Undefined) {
-    LOG(ERROR) << "Unsupported viz format found: " << format().ToString();
-    return nullptr;
-  }
-  const WGPUTextureUsageFlags usage = GetAllowedDawnUsages(wgpu_format);
-  if (usage == WGPUTextureUsage_None) {
-    LOG(ERROR) << "WGPUTextureUsage is unknown for viz format: "
-               << format().ToString();
+    LOG(ERROR) << "Unsupported DXGI_FORMAT found: " << desc.Format;
     return nullptr;
   }
 
+  WGPUTextureUsageFlags allowed_usage = GetAllowedDawnUsages(wgpu_format);
+  if (allowed_usage == WGPUTextureUsage_None) {
+    LOG(ERROR) << "Allowed WGPUTextureUsage is unknown for WGPUTextureFormat: "
+               << wgpu_format;
+    return nullptr;
+  }
+
+  // We need to have an internal usage of CopySrc in order to use
+  // CopyTextureToTextureInternal if texture format allows these usage.
+  WGPUTextureUsageFlags internal_usage =
+      (WGPUTextureUsage_CopySrc | WGPUTextureUsage_RenderAttachment |
+       WGPUTextureUsage_TextureBinding) &
+      allowed_usage;
+
   WGPUTextureDescriptor texture_descriptor = {};
   texture_descriptor.format = wgpu_format;
-  texture_descriptor.usage = static_cast<uint32_t>(usage);
+  texture_descriptor.usage = allowed_usage;
   texture_descriptor.dimension = WGPUTextureDimension_2D;
   texture_descriptor.size = {static_cast<uint32_t>(size().width()),
                              static_cast<uint32_t>(size().height()), 1};
@@ -594,12 +630,11 @@
   texture_descriptor.viewFormats = view_formats.data();
 
   // We need to have internal usages of CopySrc for copies,
-  // RenderAttachment for clears, and TextureBinding for copyTextureForBrowser.
+  // RenderAttachment for clears, and TextureBinding for copyTextureForBrowser
+  // if texture format allows these usages.
   WGPUDawnTextureInternalUsageDescriptor internalDesc = {};
   internalDesc.chain.sType = WGPUSType_DawnTextureInternalUsageDescriptor;
-  internalDesc.internalUsage = WGPUTextureUsage_CopySrc |
-                               WGPUTextureUsage_RenderAttachment |
-                               WGPUTextureUsage_TextureBinding;
+  internalDesc.internalUsage = internal_usage;
   texture_descriptor.nextInChain =
       reinterpret_cast<WGPUChainedStruct*>(&internalDesc);
 
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json
index 47a0ef2..1a8315a 100644
--- a/gpu/config/gpu_driver_bug_list.json
+++ b/gpu/config/gpu_driver_bug_list.json
@@ -4220,6 +4220,26 @@
       "features": [
         "disable_media_foundation_clear_playback"
       ]
+    },
+    {
+      "id": 409,
+      "cr_bugs": [1393646],
+      "description": "QC7c requires a flush prior to the creation of a fence.",
+      "comment": [
+        "The hypothesis behind why need a flush on the QC7c is as follows.",
+        "The driver has associated queues per buffer. These buffers are in ",
+        "order say A, B and C. We append our fence to queue C. On submission ",
+        "to the gpu these are reordered to say C, B, and then A. By flushing ",
+        "prior to fence insertion we insure that A, B, and C are submitted to ",
+        "the GPU prior to the fence."
+      ],
+      "os": {
+        "type": "chromeos"
+      },
+      "gl_vendor": "freedreno",
+      "features": [
+        "flush_before_create_fence"
+      ]
     }
   ]
 }
diff --git a/gpu/config/gpu_workaround_list.txt b/gpu/config/gpu_workaround_list.txt
index 8644da5..272a105 100644
--- a/gpu/config/gpu_workaround_list.txt
+++ b/gpu/config/gpu_workaround_list.txt
@@ -79,6 +79,7 @@
 enable_webgl_timer_query_extensions
 etc1_power_of_two_only
 exit_on_context_lost
+flush_before_create_fence
 flush_on_framebuffer_change
 force_cube_complete
 force_cube_map_positive_x_allocation
diff --git a/gpu/ipc/client/gpu_channel_host.cc b/gpu/ipc/client/gpu_channel_host.cc
index 11e23861..a364f0ea 100644
--- a/gpu/ipc/client/gpu_channel_host.cc
+++ b/gpu/ipc/client/gpu_channel_host.cc
@@ -17,7 +17,6 @@
 #include "gpu/ipc/common/command_buffer_id.h"
 #include "gpu/ipc/common/gpu_watchdog_timeout.h"
 #include "ipc/ipc_channel_mojo.h"
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/sync_call_restrictions.h"
 #include "url/gurl.h"
 
@@ -215,9 +214,9 @@
     mojo::PendingAssociatedReceiver<mojom::GpuChannel> receiver,
     scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) {
   base::AutoLock lock(lock_);
-  channel_ = IPC::ChannelMojo::Create(
-      std::move(handle), IPC::Channel::MODE_CLIENT, this, io_task_runner,
-      io_task_runner, mojo::internal::MessageQuotaChecker::MaybeCreate());
+  channel_ =
+      IPC::ChannelMojo::Create(std::move(handle), IPC::Channel::MODE_CLIENT,
+                               this, io_task_runner, io_task_runner);
   DCHECK(channel_);
   bool result = channel_->Connect();
   DCHECK(result);
diff --git a/gpu/ipc/service/gpu_init.cc b/gpu/ipc/service/gpu_init.cc
index 5f601b06..16ca4009 100644
--- a/gpu/ipc/service/gpu_init.cc
+++ b/gpu/ipc/service/gpu_init.cc
@@ -780,6 +780,9 @@
 #if defined(USE_EGL) && !BUILDFLAG(IS_MAC)
   if (gpu_feature_info_.IsWorkaroundEnabled(CHECK_EGL_FENCE_BEFORE_WAIT))
     gl::GLFenceEGL::CheckEGLFenceBeforeWait();
+
+  if (gpu_feature_info_.IsWorkaroundEnabled(FLUSH_BEFORE_CREATE_FENCE))
+    gl::GLFenceEGL::FlushBeforeCreateFence();
 #endif
 
   return true;
diff --git a/ios/chrome/browser/optimization_guide/optimization_guide_service.mm b/ios/chrome/browser/optimization_guide/optimization_guide_service.mm
index 7a5561d..6f68187 100644
--- a/ios/chrome/browser/optimization_guide/optimization_guide_service.mm
+++ b/ios/chrome/browser/optimization_guide/optimization_guide_service.mm
@@ -130,11 +130,9 @@
         optimization_guide::kOptimizationGuidePredictionModelDownloads);
   }
   if (optimization_guide::features::IsOptimizationTargetPredictionEnabled()) {
-    // TODO(crbug.com/1284363): Support the new prediction model store.
     prediction_manager_ =
         std::make_unique<optimization_guide::PredictionManager>(
-            prediction_model_and_features_store,
-            /*prediction_model_store=*/nullptr, url_loader_factory,
+            prediction_model_and_features_store, url_loader_factory,
             pref_service, off_the_record_, application_locale, models_dir,
             optimization_guide_logger_.get(),
             std::move(background_download_service_provider),
diff --git a/ios/chrome/browser/overlays/public/web_content_area/BUILD.gn b/ios/chrome/browser/overlays/public/web_content_area/BUILD.gn
index 17ec60a1..c8a512c 100644
--- a/ios/chrome/browser/overlays/public/web_content_area/BUILD.gn
+++ b/ios/chrome/browser/overlays/public/web_content_area/BUILD.gn
@@ -10,6 +10,8 @@
     "app_launcher_overlay.mm",
     "http_auth_overlay.h",
     "http_auth_overlay.mm",
+    "java_script_alert_dialog_overlay.h",
+    "java_script_alert_dialog_overlay.mm",
     "java_script_dialog_overlay.h",
     "java_script_dialog_overlay.mm",
     "java_script_dialog_overlay_utils.h",
@@ -45,6 +47,7 @@
   sources = [
     "app_launcher_overlay_unittest.mm",
     "http_auth_overlay_unittest.mm",
+    "java_script_alert_dialog_overlay_unittest.mm",
     "java_script_dialog_overlay_unittest.mm",
     "java_script_dialog_overlay_utils_unittest.mm",
   ]
diff --git a/ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay.h b/ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay.h
new file mode 100644
index 0000000..165f02ec
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay.h
@@ -0,0 +1,63 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_WEB_CONTENT_AREA_JAVA_SCRIPT_ALERT_DIALOG_OVERLAY_H_
+#define IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_WEB_CONTENT_AREA_JAVA_SCRIPT_ALERT_DIALOG_OVERLAY_H_
+
+#import "base/memory/weak_ptr.h"
+#import "ios/chrome/browser/overlays/public/overlay_request_config.h"
+#import "ios/chrome/browser/overlays/public/overlay_response_info.h"
+#import "ios/web/public/web_state.h"
+#import "url/gurl.h"
+
+// Configuration object for OverlayRequests for JavaScript alert dialogs.
+class JavaScriptAlertDialogRequest
+    : public OverlayRequestConfig<JavaScriptAlertDialogRequest> {
+ public:
+  ~JavaScriptAlertDialogRequest() override;
+
+  web::WebState* web_state() const { return weak_web_state_.get(); }
+  const GURL& url() const { return url_; }
+  bool is_main_frame() const { return is_main_frame_; }
+  NSString* message() const { return message_; }
+
+ private:
+  OVERLAY_USER_DATA_SETUP(JavaScriptAlertDialogRequest);
+  JavaScriptAlertDialogRequest(web::WebState* web_state,
+                               const GURL& url,
+                               bool is_main_frame,
+                               NSString* message);
+
+  // OverlayUserData:
+  void CreateAuxiliaryData(base::SupportsUserData* user_data) override;
+
+  base::WeakPtr<web::WebState> weak_web_state_;
+  const GURL url_;
+  bool is_main_frame_;
+  NSString* message_ = nil;
+};
+
+// Response type used for JavaScript alert dialogs.
+class JavaScriptAlertDialogResponse
+    : public OverlayResponseInfo<JavaScriptAlertDialogResponse> {
+ public:
+  ~JavaScriptAlertDialogResponse() override;
+
+  // The action selected by the user.
+  enum class Action : short {
+    kConfirm,      // Used when the user taps the OK button on a dialog.
+    kBlockDialogs  // Used when the user taps the blocking option on a dialog,
+    // indicating that subsequent dialogs from the page should be
+    // blocked.
+  };
+  Action action() const { return action_; }
+
+ private:
+  OVERLAY_USER_DATA_SETUP(JavaScriptAlertDialogResponse);
+  JavaScriptAlertDialogResponse(Action action);
+
+  Action action_;
+};
+
+#endif  // IOS_CHROME_BROWSER_OVERLAYS_PUBLIC_WEB_CONTENT_AREA_JAVA_SCRIPT_ALERT_DIALOG_OVERLAY_H_
diff --git a/ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay.mm b/ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay.mm
new file mode 100644
index 0000000..f32da4b
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay.mm
@@ -0,0 +1,91 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay.h"
+
+#import "base/bind.h"
+#import "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/overlays/public/overlay_response.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/alert_constants.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/alert_overlay.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/java_script_dialog_overlay_utils.h"
+#import "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using alert_overlays::AlertRequest;
+using alert_overlays::AlertResponse;
+using alert_overlays::ButtonConfig;
+using java_script_dialog_overlay::BlockDialogsButtonConfig;
+using java_script_dialog_overlay::DialogMessage;
+using java_script_dialog_overlay::DialogTitle;
+using java_script_dialog_overlay::ShouldAddBlockDialogsButton;
+
+namespace {
+// The index of the block button for JS alerts.
+const size_t kAlertBlockButtonIndex = 1;
+
+// Creates a JavaScriptAlertDialogResponse from a response created with an
+// AlertResponse.
+std::unique_ptr<OverlayResponse> CreateDialogResponse(
+    std::unique_ptr<OverlayResponse> response) {
+  AlertResponse* alert_response = response->GetInfo<AlertResponse>();
+  if (!alert_response)
+    return nullptr;
+
+  JavaScriptAlertDialogResponse::Action action =
+      JavaScriptAlertDialogResponse::Action::kConfirm;
+  size_t button_index = alert_response->tapped_button_index();
+  if (button_index == kAlertBlockButtonIndex) {
+    action = JavaScriptAlertDialogResponse::Action::kBlockDialogs;
+  }
+
+  return OverlayResponse::CreateWithInfo<JavaScriptAlertDialogResponse>(action);
+}
+
+}  // namespace
+
+#pragma mark - JavaScriptAlertDialogRequest
+
+OVERLAY_USER_DATA_SETUP_IMPL(JavaScriptAlertDialogRequest);
+
+JavaScriptAlertDialogRequest::JavaScriptAlertDialogRequest(
+    web::WebState* web_state,
+    const GURL& url,
+    bool is_main_frame,
+    NSString* message)
+    : weak_web_state_(web_state->GetWeakPtr()),
+      url_(url),
+      is_main_frame_(is_main_frame),
+      message_(message) {}
+
+JavaScriptAlertDialogRequest::~JavaScriptAlertDialogRequest() = default;
+
+void JavaScriptAlertDialogRequest::CreateAuxiliaryData(
+    base::SupportsUserData* user_data) {
+  NSString* alert_title = DialogTitle(is_main_frame_, message());
+  NSString* alert_message = DialogMessage(is_main_frame_, message());
+
+  std::vector<ButtonConfig> button_configs{
+      ButtonConfig(l10n_util::GetNSString(IDS_OK))};
+  if (ShouldAddBlockDialogsButton(web_state())) {
+    button_configs.push_back(BlockDialogsButtonConfig());
+  }
+
+  AlertRequest::CreateForUserData(user_data, alert_title, alert_message,
+                                  kJavaScriptDialogAccessibilityIdentifier,
+                                  /*text_field_configs=*/nil, button_configs,
+                                  base::BindRepeating(&CreateDialogResponse));
+}
+
+#pragma mark - JavaScriptAlertDialogResponse
+
+OVERLAY_USER_DATA_SETUP_IMPL(JavaScriptAlertDialogResponse);
+
+JavaScriptAlertDialogResponse::JavaScriptAlertDialogResponse(Action action)
+    : action_(action) {}
+
+JavaScriptAlertDialogResponse::~JavaScriptAlertDialogResponse() = default;
diff --git a/ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay_unittest.mm b/ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay_unittest.mm
new file mode 100644
index 0000000..55f7ce3
--- /dev/null
+++ b/ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay_unittest.mm
@@ -0,0 +1,162 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/overlays/public/web_content_area/java_script_alert_dialog_overlay.h"
+
+#import "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/overlays/public/overlay_request.h"
+#import "ios/chrome/browser/overlays/public/overlay_response.h"
+#import "ios/chrome/browser/overlays/public/web_content_area/alert_overlay.h"
+#import "ios/chrome/browser/ui/dialogs/java_script_dialog_blocking_state.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ios/web/public/test/fakes/fake_web_state.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+#import "ui/base/l10n/l10n_util.h"
+#import "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+using alert_overlays::AlertRequest;
+using alert_overlays::AlertResponse;
+using alert_overlays::ButtonConfig;
+
+namespace {
+// Message string for dialog overaly request.
+static NSString* kDialogMessage = @"message";
+}  // namespace
+
+// Test fixture for JavaScript alert dialog overlays.
+class JavaScriptAlertDialogOverlayTest : public PlatformTest {
+ protected:
+  JavaScriptAlertDialogOverlayTest() {}
+
+  std::unique_ptr<OverlayRequest> CreateRequest(bool is_main_frame = true) {
+    return OverlayRequest::CreateWithConfig<JavaScriptAlertDialogRequest>(
+        &web_state_, GURL("http://www.chromium.test"), is_main_frame,
+        kDialogMessage);
+  }
+
+  web::FakeWebState web_state_;
+};
+
+// Tests that the alert config's values are set correctly for dialogs from the
+// main frame.
+TEST_F(JavaScriptAlertDialogOverlayTest, MainFrameDialogTitleAndMessage) {
+  std::unique_ptr<OverlayRequest> request = CreateRequest();
+  AlertRequest* config = request->GetConfig<AlertRequest>();
+  ASSERT_TRUE(config);
+
+  // Check the title and message strings.
+  EXPECT_NSEQ(kDialogMessage, config->title());
+  EXPECT_FALSE(config->message());
+}
+
+// Tests that the alert config's values are set correctly for dialogs from an
+// iframe.
+TEST_F(JavaScriptAlertDialogOverlayTest, IFrameDialogTitleAndMessage) {
+  std::unique_ptr<OverlayRequest> request =
+      CreateRequest(/*is_main_frame=*/false);
+  AlertRequest* config = request->GetConfig<AlertRequest>();
+  ASSERT_TRUE(config);
+
+  // Check the title and message strings.
+  NSString* iframe_title = l10n_util::GetNSString(
+      IDS_JAVASCRIPT_MESSAGEBOX_TITLE_NONSTANDARD_URL_IFRAME);
+  EXPECT_NSEQ(iframe_title, config->title());
+  EXPECT_NSEQ(kDialogMessage, config->message());
+}
+
+// Tests that the alert dialog has no text field.
+TEST_F(JavaScriptAlertDialogOverlayTest, TextFieldConfigSetup) {
+  std::unique_ptr<OverlayRequest> alert_request = CreateRequest();
+  AlertRequest* alert_config = alert_request->GetConfig<AlertRequest>();
+  ASSERT_TRUE(alert_config);
+  EXPECT_FALSE([alert_config->text_field_configs() firstObject]);
+}
+
+// Tests that the alert dialog buttons are set up correctly.
+TEST_F(JavaScriptAlertDialogOverlayTest, ButtonConfigSetup) {
+  std::unique_ptr<OverlayRequest> alert_request = CreateRequest();
+  AlertRequest* alert_config = alert_request->GetConfig<AlertRequest>();
+  ASSERT_TRUE(alert_config);
+  const std::vector<ButtonConfig>& alert_button_configs =
+      alert_config->button_configs();
+  ASSERT_EQ(1U, alert_button_configs.size());
+  EXPECT_NSEQ(l10n_util::GetNSString(IDS_OK), alert_button_configs[0].title);
+  EXPECT_EQ(UIAlertActionStyleDefault, alert_button_configs[0].style);
+}
+
+// Tests that the blocking option is successfully added.
+TEST_F(JavaScriptAlertDialogOverlayTest, BlockingOptionSetup) {
+  JavaScriptDialogBlockingState::CreateForWebState(&web_state_);
+  JavaScriptDialogBlockingState::FromWebState(&web_state_)
+      ->JavaScriptDialogWasShown();
+  NSString* blocking_option_title =
+      l10n_util::GetNSString(IDS_IOS_JAVA_SCRIPT_DIALOG_BLOCKING_BUTTON_TEXT);
+
+  std::unique_ptr<OverlayRequest> alert_request = CreateRequest();
+  AlertRequest* alert_config = alert_request->GetConfig<AlertRequest>();
+  ASSERT_TRUE(alert_config);
+  const std::vector<ButtonConfig>& alert_button_configs =
+      alert_config->button_configs();
+  ASSERT_FALSE(alert_button_configs.empty());
+  EXPECT_NSEQ(blocking_option_title, alert_button_configs.back().title);
+  EXPECT_EQ(UIAlertActionStyleDestructive, alert_button_configs.back().style);
+}
+
+// Tests that an alert is correctly converted to a JavaScriptAlertDialogResponse
+// after tapping the OK button.
+TEST_F(JavaScriptAlertDialogOverlayTest, ResponseConversionOk) {
+  std::unique_ptr<OverlayRequest> request = CreateRequest();
+  AlertRequest* config = request->GetConfig<AlertRequest>();
+  ASSERT_TRUE(config);
+
+  // Simulate a response where the OK button is tapped.
+  std::unique_ptr<OverlayResponse> alert_response =
+      OverlayResponse::CreateWithInfo<AlertResponse>(
+          /*tapped_button_index=*/0, @[ @"" ]);
+
+  std::unique_ptr<OverlayResponse> response =
+      config->response_converter().Run(std::move(alert_response));
+  ASSERT_TRUE(response.get());
+
+  JavaScriptAlertDialogResponse* dialog_response =
+      response->GetInfo<JavaScriptAlertDialogResponse>();
+  ASSERT_TRUE(dialog_response);
+
+  EXPECT_EQ(JavaScriptAlertDialogResponse::Action::kConfirm,
+            dialog_response->action());
+}
+
+// Tests that an alert response after tapping the blocking option is correctly
+// converted to a JavaScriptDialogResponse.
+TEST_F(JavaScriptAlertDialogOverlayTest, ResponseConversionBlockDialogs) {
+  JavaScriptDialogBlockingState::CreateForWebState(&web_state_);
+  JavaScriptDialogBlockingState::FromWebState(&web_state_)
+      ->JavaScriptDialogWasShown();
+
+  std::unique_ptr<OverlayRequest> request = CreateRequest();
+  AlertRequest* config = request->GetConfig<AlertRequest>();
+  ASSERT_TRUE(config);
+
+  // Simulate a response where the blocking option is tapped.
+  size_t blocking_option_button_index = config->button_configs().size() - 1;
+  std::unique_ptr<OverlayResponse> alert_response =
+      OverlayResponse::CreateWithInfo<AlertResponse>(
+          blocking_option_button_index, @[ @"" ]);
+
+  std::unique_ptr<OverlayResponse> response =
+      config->response_converter().Run(std::move(alert_response));
+  ASSERT_TRUE(response.get());
+
+  JavaScriptAlertDialogResponse* dialog_response =
+      response->GetInfo<JavaScriptAlertDialogResponse>();
+  ASSERT_TRUE(dialog_response);
+
+  EXPECT_EQ(JavaScriptAlertDialogResponse::Action::kBlockDialogs,
+            dialog_response->action());
+}
diff --git a/ios/chrome/browser/providers/BUILD.gn b/ios/chrome/browser/providers/BUILD.gn
index b8d16350..48df5eb 100644
--- a/ios/chrome/browser/providers/BUILD.gn
+++ b/ios/chrome/browser/providers/BUILD.gn
@@ -38,6 +38,7 @@
     "//ios/chrome/browser/providers/follow:chromium_follow",
     "//ios/chrome/browser/providers/font:chromium_font",
     "//ios/chrome/browser/providers/fullscreen:chromium_fullscreen",
+    "//ios/chrome/browser/providers/keyboard:chromium_keyboard",
     "//ios/chrome/browser/providers/lens:chromium_lens",
     "//ios/chrome/browser/providers/mailto_handler:chromium_mailto_handler",
     "//ios/chrome/browser/providers/modals:chromium_modals",
diff --git a/ios/chrome/browser/providers/keyboard/BUILD.gn b/ios/chrome/browser/providers/keyboard/BUILD.gn
new file mode 100644
index 0000000..b071063
--- /dev/null
+++ b/ios/chrome/browser/providers/keyboard/BUILD.gn
@@ -0,0 +1,9 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("chromium_keyboard") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [ "chromium_keyboard.mm" ]
+  deps = [ "//ios/public/provider/chrome/browser/keyboard:keyboard_api" ]
+}
diff --git a/ios/chrome/browser/providers/keyboard/chromium_keyboard.mm b/ios/chrome/browser/providers/keyboard/chromium_keyboard.mm
new file mode 100644
index 0000000..5c9befa
--- /dev/null
+++ b/ios/chrome/browser/providers/keyboard/chromium_keyboard.mm
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/public/provider/chrome/browser/keyboard/keyboard_api.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios {
+namespace provider {
+
+UIWindow* GetKeyboardWindow() {
+  return [[[UIApplication sharedApplication] windows] lastObject];
+}
+
+}  // namespace provider
+}  // namespace ios
diff --git a/ios/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.mm b/ios/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.mm
index 9a91cda..c860638 100644
--- a/ios/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.mm
+++ b/ios/chrome/browser/safe_browsing/chrome_password_protection_service_unittest.mm
@@ -388,11 +388,11 @@
       GURL("https://www.mydomain.com")));
 
   // Verify URL is allowed after setting allowlist in prefs.
-  base::Value allowlist(base::Value::Type::LIST);
+  base::Value::List allowlist;
   allowlist.Append("mydomain.com");
   allowlist.Append("mydomain.net");
-  browser_state_->GetPrefs()->Set(prefs::kSafeBrowsingAllowlistDomains,
-                                  allowlist);
+  browser_state_->GetPrefs()->SetList(prefs::kSafeBrowsingAllowlistDomains,
+                                      std::move(allowlist));
   EXPECT_TRUE(service_->IsURLAllowlistedForPasswordEntry(
       GURL("https://www.mydomain.com")));
 
@@ -414,10 +414,10 @@
       prefs::kPasswordProtectionChangePasswordURL);
   EXPECT_FALSE(service_->IsURLAllowlistedForPasswordEntry(
       GURL("https://www.mydomain.com")));
-  base::Value login_urls(base::Value::Type::LIST);
+  base::Value::List login_urls;
   login_urls.Append("https://mydomain.com/login.html");
-  browser_state_->GetPrefs()->Set(prefs::kPasswordProtectionLoginURLs,
-                                  login_urls);
+  browser_state_->GetPrefs()->SetList(prefs::kPasswordProtectionLoginURLs,
+                                      std::move(login_urls));
   EXPECT_TRUE(service_->IsURLAllowlistedForPasswordEntry(
       GURL("https://mydomain.com/login.html#ref?user_name=alice")));
 }
@@ -572,11 +572,11 @@
     browser_state_->GetPrefs()->SetInteger(
         prefs::kPasswordProtectionWarningTrigger,
         safe_browsing::PHISHING_REUSE);
-    base::Value allowlist(base::Value::Type::LIST);
+    base::Value::List allowlist;
     allowlist.Append("mydomain.com");
     allowlist.Append("mydomain.net");
-    browser_state_->GetPrefs()->Set(prefs::kSafeBrowsingAllowlistDomains,
-                                    allowlist);
+    browser_state_->GetPrefs()->SetList(prefs::kSafeBrowsingAllowlistDomains,
+                                        std::move(allowlist));
     EXPECT_EQ(RequestOutcome::MATCHED_ENTERPRISE_ALLOWLIST,
               service_->GetPingNotSentReason(
                   LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
diff --git a/ios/chrome/browser/safe_browsing/password_protection_java_script_feature.mm b/ios/chrome/browser/safe_browsing/password_protection_java_script_feature.mm
index f06b59c..ce15c5b7 100644
--- a/ios/chrome/browser/safe_browsing/password_protection_java_script_feature.mm
+++ b/ios/chrome/browser/safe_browsing/password_protection_java_script_feature.mm
@@ -58,13 +58,14 @@
   if (!message.body()->is_dict()) {
     return;
   }
+  const base::Value::Dict& dict = message.body()->GetDict();
 
-  std::string* event_type = message.body()->FindStringKey("eventType");
+  const std::string* event_type = dict.FindString("eventType");
   if (!event_type || event_type->empty()) {
     return;
   }
 
-  std::string* text = message.body()->FindStringKey("text");
+  const std::string* text = dict.FindString("text");
   if (!text || text->empty()) {
     return;
   }
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
index 0fc8aa0..a65bbcae 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
+++ b/ios/chrome/browser/ui/autofill/manual_fill/BUILD.gn
@@ -64,6 +64,7 @@
     "//ios/chrome/browser/web_state_list:web_state_list",
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/reauthentication",
+    "//ios/public/provider/chrome/browser/keyboard:keyboard_api",
     "//ios/web/public",
     "//ios/web/public/js_messaging",
     "//ui/base",
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.mm b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.mm
index b9ea0283..ce34f43 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/fallback_coordinator.mm
@@ -16,6 +16,7 @@
 #import "ios/chrome/browser/ui/table_view/chrome_table_view_controller.h"
 #import "ios/chrome/browser/ui/table_view/table_view_navigation_controller.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
+#import "ios/public/provider/chrome/browser/keyboard/keyboard_api.h"
 #import "ui/base/device_form_factor.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -63,8 +64,7 @@
   // `topFrontWindow` is used in order to present above the keyboard. This way
   // the popover will be dismissed on keyboard interaction and it won't be
   // covered when the keyboard is near the top of the screen.
-  UIWindow* topFrontWindow =
-      [[[UIApplication sharedApplication] windows] lastObject];
+  UIWindow* topFrontWindow = ios::provider::GetKeyboardWindow();
   [topFrontWindow.rootViewController presentViewController:self.viewController
                                                   animated:YES
                                                 completion:nil];
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_carousel_cell.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_carousel_cell.mm
index ed5c46a..fa81b63 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_carousel_cell.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_carousel_cell.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/omnibox/popup/omnibox_popup_carousel_cell.h"
 
 #import "base/check.h"
+#import "base/i18n/rtl.h"
 #import "base/notreached.h"
 #import "ios/chrome/browser/ui/omnibox/omnibox_ui_features.h"
 #import "ios/chrome/browser/ui/omnibox/popup/carousel_item.h"
@@ -26,7 +27,8 @@
 const CGFloat kStackMargin = 8.0f;
 /// Minimum spacing between items in the StackView.
 const CGFloat kMinStackSpacing = 8.0f;
-/// Width of the gradient applied at the end of the carousel.
+/// Width of the gradient applied on the leading and trailing edge of the
+/// carousel.
 const CGFloat kGradientWidth = 20.0f;
 
 /// Horizontal UIScrollView used in OmniboxPopupCarouselCell.
@@ -61,9 +63,13 @@
       colorWithAlphaComponent:1.0];
   UIColor* transparentColor = [[UIColor
       colorNamed:kGroupedSecondaryBackgroundColor] colorWithAlphaComponent:0.0];
-  maskLayer.colors = @[ (id)opaqueColor.CGColor, (id)transparentColor.CGColor ];
+  maskLayer.colors = @[
+    (id)transparentColor.CGColor, (id)opaqueColor.CGColor,
+    (id)opaqueColor.CGColor, (id)transparentColor.CGColor
+  ];
+  // locations are computed with `kGradientWidth` in `updateGradient`.
   maskLayer.anchorPoint = CGPointZero;
-  // startPoint is computed with `kGradientWidth` in `updateGradient`.
+  maskLayer.startPoint = CGPointMake(0, 0.5);
   maskLayer.endPoint = CGPointMake(1, 0.5);
   return maskLayer;
 }
@@ -128,6 +134,10 @@
   if (self.shouldApplyLayoutMarginsGuide) {
     [self updateGradient];
   }
+  if (base::i18n::IsRTL()) {
+    self.scrollView.transform = CGAffineTransformMakeRotation(M_PI);
+    self.suggestionsStackView.transform = CGAffineTransformMakeRotation(M_PI);
+  }
   [super layoutSubviews];
 }
 
@@ -309,10 +319,17 @@
   NSArray<OmniboxPopupCarouselControl*>* allTiles =
       self.suggestionsStackView.arrangedSubviews;
 
-  if (keyboardAction == OmniboxKeyboardActionRightArrow) {
+  OmniboxKeyboardAction nextTileAction = base::i18n::IsRTL()
+                                             ? OmniboxKeyboardActionLeftArrow
+                                             : OmniboxKeyboardActionRightArrow;
+  OmniboxKeyboardAction previousTileAction =
+      base::i18n::IsRTL() ? OmniboxKeyboardActionRightArrow
+                          : OmniboxKeyboardActionLeftArrow;
+
+  if (keyboardAction == nextTileAction) {
     nextHighlightedIndex =
         MIN(prevHighlightedIndex + 1, (NSInteger)allTiles.count - 1);
-  } else if (keyboardAction == OmniboxKeyboardActionLeftArrow) {
+  } else if (keyboardAction == previousTileAction) {
     nextHighlightedIndex = MAX(prevHighlightedIndex - 1, 0);
   } else {
     NOTREACHED();
@@ -390,9 +407,13 @@
       CGRectGetWidth(self.contentView.layoutMarginsGuide.layoutFrame);
   CGRect gradientFrame = CGRectMake(self.contentView.layoutMargins.left, 0,
                                     contentWidth, CGRectGetHeight(self.bounds));
-  CGFloat gradientStart = 1.0 - kGradientWidth / contentWidth;
+  CGFloat gradientWidth = kGradientWidth / contentWidth;
   self.gradientLayer.frame = gradientFrame;
-  self.gradientLayer.startPoint = CGPointMake(gradientStart, 0.5);
+  NSNumber* endOfFirstGradient = [NSNumber numberWithFloat:gradientWidth];
+  NSNumber* startOfSecondGradient =
+      [NSNumber numberWithFloat:1.0 - gradientWidth];
+  self.gradientLayer.locations =
+      @[ @0.0, endOfFirstGradient, startOfSecondGradient, @1.0 ];
 }
 
 @end
diff --git a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
index 31fe3fb..45b3594 100644
--- a/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
+++ b/ios/chrome/browser/ui/omnibox/popup/omnibox_popup_view_controller.mm
@@ -648,6 +648,12 @@
     return FLT_MIN;
   }
 
+  // When most visited tiles are enabled, only allow section separator under the
+  // verbatim suggestion.
+  if (base::FeatureList::IsEnabled(omnibox::kMostVisitedTiles) && section > 0) {
+    return FLT_MIN;
+  }
+
   return IsOmniboxActionsVisualTreatment1() ? kFooterHeightVariation1
                                             : kFooterHeightVariation2;
 }
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm
index 9a908a4..819f17b 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_mediator.mm
@@ -474,6 +474,9 @@
     // is being selected, make sure that the consumer update its selected item.
     [self.consumer selectItemWithID:itemID];
     return;
+  } else {
+    base::RecordAction(
+        base::UserMetricsAction("MobileTabGridMoveToExistingTab"));
   }
 
   // Avoid a reentrant activation. This is a fix for crbug.com/1134663, although
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.h
index e4c08ad..7163e57 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.h
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.h
@@ -51,6 +51,8 @@
 - (void)setCancelSearchButtonTarget:(id)target action:(SEL)action;
 // Sets the delegate for the searchbar.
 - (void)setSearchBarDelegate:(id<UISearchBarDelegate>)delegate;
+// Set `enabled` on the search button.
+- (void)setSearchButtonEnabled:(BOOL)enabled;
 // Set `enabled` on the new tab button.
 - (void)setNewTabButtonEnabled:(BOOL)enabled;
 // Set `enabled` on the select all button.
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm
index a5314cd..cb02c50 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_top_toolbar.mm
@@ -145,6 +145,10 @@
   _cancelSearchButton.action = action;
 }
 
+- (void)setSearchButtonEnabled:(BOOL)enabled {
+  _searchButton.enabled = enabled;
+}
+
 - (void)setNewTabButtonEnabled:(BOOL)enabled {
   _newTabButton.enabled = enabled;
 }
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
index bc409651..fd4c0edf 100644
--- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
@@ -2393,6 +2393,7 @@
   [self.topToolbar setNewTabButtonEnabled:NO];
   [self.topToolbar setSelectAllButtonEnabled:NO];
   [self.topToolbar setEditButtonEnabled:NO];
+  [self.topToolbar setSearchButtonEnabled:NO];
   [self.bottomToolbar setEditButtonEnabled:NO];
   [self.bottomToolbar setAddToButtonEnabled:NO];
   [self.bottomToolbar setShareTabsButtonEnabled:NO];
@@ -2406,6 +2407,8 @@
     (GridViewController*)gridViewController {
   self.dragSeesionInProgress = NO;
 
+  [self.topToolbar setSearchButtonEnabled:YES];
+
   // -configureDoneButtonBasedOnPage will enable the page control.
   [self configureDoneButtonBasedOnPage:self.currentPage];
   [self configureCloseAllButtonForCurrentPageAndUndoAvailability];
diff --git a/ios/chrome/test/providers/BUILD.gn b/ios/chrome/test/providers/BUILD.gn
index 9dd6d42..07a8241 100644
--- a/ios/chrome/test/providers/BUILD.gn
+++ b/ios/chrome/test/providers/BUILD.gn
@@ -17,6 +17,7 @@
     "//ios/chrome/test/providers/follow",
     "//ios/chrome/test/providers/font",
     "//ios/chrome/test/providers/fullscreen",
+    "//ios/chrome/test/providers/keyboard",
     "//ios/chrome/test/providers/lens",
     "//ios/chrome/test/providers/mailto_handler",
     "//ios/chrome/test/providers/modals",
diff --git a/ios/chrome/test/providers/keyboard/BUILD.gn b/ios/chrome/test/providers/keyboard/BUILD.gn
new file mode 100644
index 0000000..c729933
--- /dev/null
+++ b/ios/chrome/test/providers/keyboard/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("keyboard") {
+  testonly = true
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [ "test_keyboard.mm" ]
+  deps = [ "//ios/public/provider/chrome/browser/keyboard:keyboard_api" ]
+}
diff --git a/ios/chrome/test/providers/keyboard/test_keyboard.mm b/ios/chrome/test/providers/keyboard/test_keyboard.mm
new file mode 100644
index 0000000..5c9befa
--- /dev/null
+++ b/ios/chrome/test/providers/keyboard/test_keyboard.mm
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/public/provider/chrome/browser/keyboard/keyboard_api.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace ios {
+namespace provider {
+
+UIWindow* GetKeyboardWindow() {
+  return [[[UIApplication sharedApplication] windows] lastObject];
+}
+
+}  // namespace provider
+}  // namespace ios
diff --git a/ios/public/provider/chrome/browser/BUILD.gn b/ios/public/provider/chrome/browser/BUILD.gn
index c7c6df49..1b2ee62 100644
--- a/ios/public/provider/chrome/browser/BUILD.gn
+++ b/ios/public/provider/chrome/browser/BUILD.gn
@@ -24,6 +24,7 @@
     "//ios/public/provider/chrome/browser/discover_feed:discover_feed_api",
     "//ios/public/provider/chrome/browser/font:font_api",
     "//ios/public/provider/chrome/browser/fullscreen:fullscreen_api",
+    "//ios/public/provider/chrome/browser/keyboard:keyboard_api",
     "//ios/public/provider/chrome/browser/lens:lens_api",
     "//ios/public/provider/chrome/browser/mailto_handler:mailto_handler_api",
     "//ios/public/provider/chrome/browser/modals:modals_api",
diff --git a/ios/public/provider/chrome/browser/keyboard/BUILD.gn b/ios/public/provider/chrome/browser/keyboard/BUILD.gn
new file mode 100644
index 0000000..f55d89d
--- /dev/null
+++ b/ios/public/provider/chrome/browser/keyboard/BUILD.gn
@@ -0,0 +1,10 @@
+# Copyright 2022 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("keyboard_api") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [ "keyboard_api.h" ]
+
+  frameworks = [ "UIKit.framework" ]
+}
diff --git a/ios/public/provider/chrome/browser/keyboard/keyboard_api.h b/ios/public/provider/chrome/browser/keyboard/keyboard_api.h
new file mode 100644
index 0000000..e4bcc87
--- /dev/null
+++ b/ios/public/provider/chrome/browser/keyboard/keyboard_api.h
@@ -0,0 +1,19 @@
+// Copyright 2022 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_PUBLIC_PROVIDER_CHROME_BROWSER_KEYBOARD_KEYBOARD_API_H_
+#define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_KEYBOARD_KEYBOARD_API_H_
+
+#import <UIKit/UIKit.h>
+
+namespace ios {
+namespace provider {
+
+// Returns the keyboard window over which accessories can be presented.
+UIWindow* GetKeyboardWindow();
+
+}  // namespace provider
+}  // namespace ios
+
+#endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_KEYBOARD_KEYBOARD_API_H_
diff --git a/ipc/ipc_channel_common.cc b/ipc/ipc_channel_common.cc
index f3597d3..9eb94f4 100644
--- a/ipc/ipc_channel_common.cc
+++ b/ipc/ipc_channel_common.cc
@@ -6,7 +6,6 @@
 #include "build/build_config.h"
 #include "ipc/ipc_channel.h"
 #include "ipc/ipc_channel_mojo.h"
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
 namespace IPC {
@@ -41,8 +40,7 @@
   return ChannelMojo::Create(
       mojo::ScopedMessagePipeHandle(channel_handle.mojo_handle),
       Channel::MODE_CLIENT, listener, ipc_task_runner,
-      base::SingleThreadTaskRunner::GetCurrentDefault(),
-      mojo::internal::MessageQuotaChecker::MaybeCreate());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
 #endif
 }
 
@@ -58,8 +56,7 @@
   return ChannelMojo::Create(
       mojo::ScopedMessagePipeHandle(channel_handle.mojo_handle),
       Channel::MODE_SERVER, listener, ipc_task_runner,
-      base::SingleThreadTaskRunner::GetCurrentDefault(),
-      mojo::internal::MessageQuotaChecker::MaybeCreate());
+      base::SingleThreadTaskRunner::GetCurrentDefault());
 #endif
 }
 
diff --git a/ipc/ipc_channel_factory.cc b/ipc/ipc_channel_factory.cc
index 7f683632..a24bd67 100644
--- a/ipc/ipc_channel_factory.cc
+++ b/ipc/ipc_channel_factory.cc
@@ -8,7 +8,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "build/build_config.h"
 #include "ipc/ipc_channel_mojo.h"
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 
 namespace IPC {
 
@@ -20,10 +19,7 @@
       ChannelHandle handle,
       Channel::Mode mode,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner)
-      : handle_(handle),
-        mode_(mode),
-        ipc_task_runner_(ipc_task_runner),
-        quota_checker_(mojo::internal::MessageQuotaChecker::MaybeCreate()) {}
+      : handle_(handle), mode_(mode), ipc_task_runner_(ipc_task_runner) {}
 
   PlatformChannelFactory(const PlatformChannelFactory&) = delete;
   PlatformChannelFactory& operator=(const PlatformChannelFactory&) = delete;
@@ -35,8 +31,7 @@
     DCHECK(handle_.is_mojo_channel_handle());
     return ChannelMojo::Create(
         mojo::ScopedMessagePipeHandle(handle_.mojo_handle), mode_, listener,
-        ipc_task_runner_, base::SingleThreadTaskRunner::GetCurrentDefault(),
-        quota_checker_);
+        ipc_task_runner_, base::SingleThreadTaskRunner::GetCurrentDefault());
 #endif
   }
 
@@ -44,16 +39,10 @@
     return ipc_task_runner_;
   }
 
-  scoped_refptr<mojo::internal::MessageQuotaChecker> GetQuotaChecker()
-      override {
-    return quota_checker_;
-  }
-
  private:
   ChannelHandle handle_;
   Channel::Mode mode_;
   scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;
-  scoped_refptr<mojo::internal::MessageQuotaChecker> quota_checker_;
 };
 
 } // namespace
diff --git a/ipc/ipc_channel_factory.h b/ipc/ipc_channel_factory.h
index 5da6131..0ed4606b 100644
--- a/ipc/ipc_channel_factory.h
+++ b/ipc/ipc_channel_factory.h
@@ -14,12 +14,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "ipc/ipc_channel.h"
 
-namespace mojo {
-namespace internal {
-class MessageQuotaChecker;
-}  // namespace internal
-}  // namespace mojo
-
 namespace IPC {
 
 // Encapsulates how a Channel is created. A ChannelFactory can be
@@ -37,8 +31,6 @@
   virtual ~ChannelFactory() { }
   virtual std::unique_ptr<Channel> BuildChannel(Listener* listener) = 0;
   virtual scoped_refptr<base::SingleThreadTaskRunner> GetIPCTaskRunner() = 0;
-  virtual scoped_refptr<mojo::internal::MessageQuotaChecker>
-  GetQuotaChecker() = 0;
 };
 
 }  // namespace IPC
diff --git a/ipc/ipc_channel_mojo.cc b/ipc/ipc_channel_mojo.cc
index d6c78468..974b96fc 100644
--- a/ipc/ipc_channel_mojo.cc
+++ b/ipc/ipc_channel_mojo.cc
@@ -30,7 +30,6 @@
 #include "mojo/public/cpp/bindings/associated_receiver.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/thread_safe_proxy.h"
 #include "mojo/public/cpp/system/platform_handle.h"
@@ -49,33 +48,25 @@
       : handle_(std::move(handle)),
         mode_(mode),
         ipc_task_runner_(ipc_task_runner),
-        proxy_task_runner_(proxy_task_runner),
-        quota_checker_(mojo::internal::MessageQuotaChecker::MaybeCreate()) {}
+        proxy_task_runner_(proxy_task_runner) {}
 
   MojoChannelFactory(const MojoChannelFactory&) = delete;
   MojoChannelFactory& operator=(const MojoChannelFactory&) = delete;
 
   std::unique_ptr<Channel> BuildChannel(Listener* listener) override {
     return ChannelMojo::Create(std::move(handle_), mode_, listener,
-                               ipc_task_runner_, proxy_task_runner_,
-                               quota_checker_);
+                               ipc_task_runner_, proxy_task_runner_);
   }
 
   scoped_refptr<base::SingleThreadTaskRunner> GetIPCTaskRunner() override {
     return ipc_task_runner_;
   }
 
-  scoped_refptr<mojo::internal::MessageQuotaChecker> GetQuotaChecker()
-      override {
-    return quota_checker_;
-  }
-
  private:
   mojo::ScopedMessagePipeHandle handle_;
   const Channel::Mode mode_;
   scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;
   scoped_refptr<base::SingleThreadTaskRunner> proxy_task_runner_;
-  scoped_refptr<mojo::internal::MessageQuotaChecker> quota_checker_;
 };
 
 class ThreadSafeChannelProxy : public mojo::ThreadSafeProxy {
@@ -135,11 +126,9 @@
     Mode mode,
     Listener* listener,
     const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
-    const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker) {
+    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner) {
   return base::WrapUnique(new ChannelMojo(std::move(handle), mode, listener,
-                                          ipc_task_runner, proxy_task_runner,
-                                          quota_checker));
+                                          ipc_task_runner, proxy_task_runner));
 }
 
 // static
@@ -167,12 +156,11 @@
     Mode mode,
     Listener* listener,
     const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
-    const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker)
+    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner)
     : task_runner_(ipc_task_runner), pipe_(handle.get()), listener_(listener) {
   weak_ptr_ = weak_factory_.GetWeakPtr();
   bootstrap_ = MojoBootstrap::Create(std::move(handle), mode, ipc_task_runner,
-                                     proxy_task_runner, quota_checker);
+                                     proxy_task_runner);
 }
 
 void ChannelMojo::ForwardMessage(mojo::Message message) {
diff --git a/ipc/ipc_channel_mojo.h b/ipc/ipc_channel_mojo.h
index 66f2e5e..4eb44e0 100644
--- a/ipc/ipc_channel_mojo.h
+++ b/ipc/ipc_channel_mojo.h
@@ -49,8 +49,7 @@
       Mode mode,
       Listener* listener,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
-      const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker);
+      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner);
 
   // Create a factory object for ChannelMojo.
   // The factory is used to create Mojo-based ChannelProxy family.
@@ -102,8 +101,7 @@
       Mode mode,
       Listener* listener,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
-      const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker);
+      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner);
 
   void ForwardMessage(mojo::Message message);
 
diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc
index fe3dd50..6073ff6 100644
--- a/ipc/ipc_channel_proxy.cc
+++ b/ipc/ipc_channel_proxy.cc
@@ -224,9 +224,6 @@
 
 // Called on the IPC::Channel thread
 void ChannelProxy::Context::OnSendMessage(std::unique_ptr<Message> message) {
-  if (quota_checker_)
-    quota_checker_->AfterMessagesDequeued(1);
-
   if (!channel_) {
     OnChannelClosed();
     return;
@@ -422,9 +419,6 @@
 }
 
 void ChannelProxy::Context::Send(Message* message) {
-  if (quota_checker_)
-    quota_checker_->BeforeMessagesEnqueued(1);
-
   ipc_task_runner()->PostTask(
       FROM_HERE, base::BindOnce(&ChannelProxy::Context::OnSendMessage, this,
                                 base::WrapUnique(message)));
@@ -503,9 +497,6 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!did_init_);
 
-  DCHECK(!context_->quota_checker_);
-  context_->quota_checker_ = factory->GetQuotaChecker();
-
   if (create_pipe_now) {
     // Create the channel immediately.  This effectively sets up the
     // low-level pipe so that the client can connect.  Without creating
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
index 58a6937..c9be29e 100644
--- a/ipc/ipc_channel_proxy.h
+++ b/ipc/ipc_channel_proxy.h
@@ -27,7 +27,6 @@
 #include "ipc/ipc_sender.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
 #include "mojo/public/cpp/bindings/generic_pending_associated_receiver.h"
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/bindings/pending_associated_remote.h"
 #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
@@ -368,9 +367,6 @@
     std::unique_ptr<Channel> channel_;
     bool channel_connected_called_;
 
-    // The quota checker associated with this channel, if any.
-    scoped_refptr<mojo::internal::MessageQuotaChecker> quota_checker_;
-
     // Lock for |channel_| value. This is only relevant in the context of
     // thread-safe send.
     base::Lock channel_lifetime_lock_;
diff --git a/ipc/ipc_mojo_bootstrap.cc b/ipc/ipc_mojo_bootstrap.cc
index d68f1a3..6ecb81b 100644
--- a/ipc/ipc_mojo_bootstrap.cc
+++ b/ipc/ipc_mojo_bootstrap.cc
@@ -140,11 +140,9 @@
   ChannelAssociatedGroupController(
       bool set_interface_id_namespace_bit,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
-      const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker)
+      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner)
       : task_runner_(ipc_task_runner),
         proxy_task_runner_(proxy_task_runner),
-        quota_checker_(quota_checker),
         set_interface_id_namespace_bit_(set_interface_id_namespace_bit),
         dispatcher_(this),
         control_message_handler_(this),
@@ -202,8 +200,6 @@
       base::AutoLock lock(outgoing_messages_lock_);
       std::swap(outgoing_messages, outgoing_messages_);
     }
-    if (quota_checker_ && outgoing_messages.size())
-      quota_checker_->AfterMessagesDequeued(outgoing_messages.size());
 
     for (auto& message : outgoing_messages)
       SendMessage(&message);
@@ -220,8 +216,6 @@
         base::BindOnce(&ChannelAssociatedGroupController::OnPipeError,
                        base::Unretained(this)));
     connector_->set_enforce_errors_from_incoming_receiver(false);
-    if (quota_checker_)
-      connector_->SetMessageQuotaChecker(quota_checker_);
 
     // Don't let the Connector do any sort of queuing on our behalf. Individual
     // messages bound for the IPC::ChannelProxy thread (i.e. that vast majority
@@ -272,9 +266,6 @@
     connector_.reset();
 
     base::AutoLock lock(outgoing_messages_lock_);
-    if (quota_checker_ && outgoing_messages_.size())
-      quota_checker_->AfterMessagesDequeued(outgoing_messages_.size());
-
     outgoing_messages_.clear();
   }
 
@@ -821,8 +812,6 @@
       if (!connector_ || paused_) {
         if (!shut_down_) {
           base::AutoLock lock(outgoing_messages_lock_);
-          if (quota_checker_)
-            quota_checker_->BeforeMessagesEnqueued(1);
           outgoing_messages_.emplace_back(std::move(*message));
         }
         return true;
@@ -1163,7 +1152,6 @@
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 
   const scoped_refptr<base::SingleThreadTaskRunner> proxy_task_runner_;
-  const scoped_refptr<mojo::internal::MessageQuotaChecker> quota_checker_;
   const bool set_interface_id_namespace_bit_;
   bool paused_ = false;
   std::unique_ptr<mojo::Connector> connector_;
@@ -1292,12 +1280,11 @@
     mojo::ScopedMessagePipeHandle handle,
     Channel::Mode mode,
     const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
-    const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker) {
+    const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner) {
   return std::make_unique<MojoBootstrapImpl>(
-      std::move(handle), base::MakeRefCounted<ChannelAssociatedGroupController>(
-                             mode == Channel::MODE_SERVER, ipc_task_runner,
-                             proxy_task_runner, quota_checker));
+      std::move(handle),
+      base::MakeRefCounted<ChannelAssociatedGroupController>(
+          mode == Channel::MODE_SERVER, ipc_task_runner, proxy_task_runner));
 }
 
 }  // namespace IPC
diff --git a/ipc/ipc_mojo_bootstrap.h b/ipc/ipc_mojo_bootstrap.h
index 43156e5..b432e44 100644
--- a/ipc/ipc_mojo_bootstrap.h
+++ b/ipc/ipc_mojo_bootstrap.h
@@ -18,7 +18,6 @@
 #include "ipc/ipc_listener.h"
 #include "mojo/public/cpp/bindings/associated_group.h"
 #include "mojo/public/cpp/bindings/associated_remote.h"
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
 #include "mojo/public/cpp/system/message_pipe.h"
 
@@ -102,8 +101,7 @@
       mojo::ScopedMessagePipeHandle handle,
       Channel::Mode mode,
       const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner,
-      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner,
-      const scoped_refptr<mojo::internal::MessageQuotaChecker>& quota_checker);
+      const scoped_refptr<base::SingleThreadTaskRunner>& proxy_task_runner);
 
   // Initialize the Channel pipe and interface endpoints. This performs all
   // setup except actually starting to read messages off the pipe.
diff --git a/ipc/ipc_mojo_bootstrap_unittest.cc b/ipc/ipc_mojo_bootstrap_unittest.cc
index 723dfc2..8771bcd 100644
--- a/ipc/ipc_mojo_bootstrap_unittest.cc
+++ b/ipc/ipc_mojo_bootstrap_unittest.cc
@@ -118,13 +118,12 @@
 
 TEST_F(IPCMojoBootstrapTest, Connect) {
   base::test::SingleThreadTaskEnvironment task_environment;
-  Connection connection(
-      IPC::MojoBootstrap::Create(
-          helper_.StartChild("IPCMojoBootstrapTestClient"),
-          IPC::Channel::MODE_SERVER,
-          base::SingleThreadTaskRunner::GetCurrentDefault(),
-          base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr),
-      kTestServerPid);
+  Connection connection(IPC::MojoBootstrap::Create(
+                            helper_.StartChild("IPCMojoBootstrapTestClient"),
+                            IPC::Channel::MODE_SERVER,
+                            base::SingleThreadTaskRunner::GetCurrentDefault(),
+                            base::SingleThreadTaskRunner::GetCurrentDefault()),
+                        kTestServerPid);
 
   mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
   connection.TakeReceiver(&receiver);
@@ -149,7 +148,7 @@
           std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
           IPC::Channel::MODE_CLIENT,
           base::SingleThreadTaskRunner::GetCurrentDefault(),
-          base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr),
+          base::SingleThreadTaskRunner::GetCurrentDefault()),
       kTestClientPid);
 
   mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
@@ -171,7 +170,7 @@
           helper_.StartChild("IPCMojoBootstrapTestEmptyMessage"),
           IPC::Channel::MODE_SERVER,
           base::SingleThreadTaskRunner::GetCurrentDefault(),
-          base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr),
+          base::SingleThreadTaskRunner::GetCurrentDefault()),
       kTestServerPid);
 
   mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
@@ -199,7 +198,7 @@
           std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
           IPC::Channel::MODE_CLIENT,
           base::SingleThreadTaskRunner::GetCurrentDefault(),
-          base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr),
+          base::SingleThreadTaskRunner::GetCurrentDefault()),
       kTestClientPid);
 
   mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
diff --git a/ipc/ipc_test_base.cc b/ipc/ipc_test_base.cc
index 865d6cd..49dc738 100644
--- a/ipc/ipc_test_base.cc
+++ b/ipc/ipc_test_base.cc
@@ -33,7 +33,7 @@
   channel_ = IPC::ChannelMojo::Create(
       TakeHandle(), IPC::Channel::MODE_SERVER, listener,
       base::SingleThreadTaskRunner::GetCurrentDefault(),
-      base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr);
+      base::SingleThreadTaskRunner::GetCurrentDefault());
 }
 
 bool IPCChannelMojoTestBase::ConnectChannel() {
@@ -60,7 +60,7 @@
   channel_ = IPC::ChannelMojo::Create(
       std::move(handle_), IPC::Channel::MODE_CLIENT, listener,
       base::SingleThreadTaskRunner::GetCurrentDefault(),
-      base::SingleThreadTaskRunner::GetCurrentDefault(), nullptr);
+      base::SingleThreadTaskRunner::GetCurrentDefault());
   CHECK(channel_->Connect());
 }
 
diff --git a/media/audio/audio_output_device_thread_callback.cc b/media/audio/audio_output_device_thread_callback.cc
index 542f587f..d84ea4f 100644
--- a/media/audio/audio_output_device_thread_callback.cc
+++ b/media/audio/audio_output_device_thread_callback.cc
@@ -88,7 +88,8 @@
   // frames, and ask client to render audio.  Since |output_bus_| is wrapping
   // the shared memory the Render() call is writing directly into the shared
   // memory.
-  render_callback_->Render(delay, delay_timestamp, 0, output_bus_.get());
+  render_callback_->Render(delay, delay_timestamp, glitch_info,
+                           output_bus_.get());
 
   if (audio_parameters_.IsBitstreamFormat()) {
     buffer->params.bitstream_data_size = output_bus_->GetBitstreamDataSize();
diff --git a/media/audio/audio_output_device_unittest.cc b/media/audio/audio_output_device_unittest.cc
index 0e1136b1e..220c3ef 100644
--- a/media/audio/audio_output_device_unittest.cc
+++ b/media/audio/audio_output_device_unittest.cc
@@ -56,7 +56,7 @@
   MOCK_METHOD4(Render,
                int(base::TimeDelta delay,
                    base::TimeTicks timestamp,
-                   int prior_frames_skipped,
+                   const AudioGlitchInfo& glitch_info,
                    AudioBus* dest));
   MOCK_METHOD0(OnRenderError, void());
 };
diff --git a/media/audio/clockless_audio_sink.cc b/media/audio/clockless_audio_sink.cc
index ab42142..ad2931c 100644
--- a/media/audio/clockless_audio_sink.cc
+++ b/media/audio/clockless_audio_sink.cc
@@ -13,6 +13,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/threading/simple_thread.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_hash.h"
 
 namespace media {
diff --git a/media/audio/null_audio_sink.cc b/media/audio/null_audio_sink.cc
index 12c4fe7..a82d045 100644
--- a/media/audio/null_audio_sink.cc
+++ b/media/audio/null_audio_sink.cc
@@ -10,6 +10,7 @@
 #include "base/location.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_hash.h"
 #include "media/base/fake_audio_worker.h"
 
diff --git a/media/base/audio_bus_perftest.cc b/media/base/audio_bus_perftest.cc
index e0b6f43..d31a1c55 100644
--- a/media/base/audio_bus_perftest.cc
+++ b/media/base/audio_bus_perftest.cc
@@ -60,7 +60,7 @@
 TEST(AudioBusPerfTest, Interleave) {
   std::unique_ptr<AudioBus> bus = AudioBus::Create(2, kSampleRate * 120);
   FakeAudioRenderCallback callback(0.2, kSampleRate);
-  callback.Render(base::TimeDelta(), base::TimeTicks::Now(), 0, bus.get());
+  callback.Render(base::TimeDelta(), base::TimeTicks::Now(), {}, bus.get());
 
   // Only benchmark these two types since they're the only commonly used ones.
   RunInterleaveBench<int16_t, SignedInt16SampleTypeTraits>(bus.get(),
@@ -71,7 +71,7 @@
 TEST(AudioBusPerfTest, DISABLED_ToInterleavedFloat) {
   std::unique_ptr<AudioBus> bus = AudioBus::Create(2, kSampleRate * 120);
   FakeAudioRenderCallback callback(0.2, kSampleRate);
-  callback.Render(base::TimeDelta(), base::TimeTicks::Now(), 0, bus.get());
+  callback.Render(base::TimeDelta(), base::TimeTicks::Now(), {}, bus.get());
 
   RunInterleaveBench<float, Float32SampleTypeTraits>(
       bus.get(), "to_interleave_float", true);
@@ -85,7 +85,7 @@
   std::unique_ptr<AudioBus> bus = AudioBus::Create(2, kSampleRate * 120);
   std::unique_ptr<AudioBus> dest = AudioBus::Create(2, kSampleRate * 120);
   FakeAudioRenderCallback callback(0.2, kSampleRate);
-  callback.Render(base::TimeDelta(), base::TimeTicks::Now(), 0, bus.get());
+  callback.Render(base::TimeDelta(), base::TimeTicks::Now(), {}, bus.get());
 
   // Warmup.
   for (int i = 0; i < kBenchmarkIterations; ++i)
diff --git a/media/base/audio_converter_unittest.cc b/media/base/audio_converter_unittest.cc
index c1bdd7f..8cc61c8 100644
--- a/media/base/audio_converter_unittest.cc
+++ b/media/base/audio_converter_unittest.cc
@@ -116,7 +116,7 @@
     converter_->Convert(audio_bus_.get());
 
     // Render expected audio data.
-    expected_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(), 0,
+    expected_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(), {},
                                expected_audio_bus_.get());
 
     // Zero out unused channels in the expected AudioBus just as AudioConverter
diff --git a/media/base/audio_glitch_info.cc b/media/base/audio_glitch_info.cc
index e33740a8..8b2ef5c 100644
--- a/media/base/audio_glitch_info.cc
+++ b/media/base/audio_glitch_info.cc
@@ -6,8 +6,17 @@
 
 #include <utility>
 
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+
 namespace media {
 
+std::string AudioGlitchInfo::ToString() const {
+  return base::StrCat(
+      {"duration (ms): ", base::NumberToString(duration.InMilliseconds()),
+       ", count: ", base::NumberToString(count)});
+}
+
 AudioGlitchInfo& AudioGlitchInfo::operator+=(const AudioGlitchInfo& other) {
   duration += other.duration;
   count += other.count;
diff --git a/media/base/audio_glitch_info.h b/media/base/audio_glitch_info.h
index 2182fabc..2557c4c 100644
--- a/media/base/audio_glitch_info.h
+++ b/media/base/audio_glitch_info.h
@@ -19,6 +19,9 @@
   // Number of glitches.
   unsigned int count = 0;
 
+  // Stringifies the info for human-readable logging.
+  std::string ToString() const;
+
   AudioGlitchInfo& operator+=(const AudioGlitchInfo& other);
 
   class MEDIA_EXPORT Accumulator;
diff --git a/media/base/audio_glitch_info_unittest.cc b/media/base/audio_glitch_info_unittest.cc
index 17fb4994..7ddff8f 100644
--- a/media/base/audio_glitch_info_unittest.cc
+++ b/media/base/audio_glitch_info_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "media/base/audio_glitch_info.h"
 
+#include "base/time/time.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
@@ -36,4 +37,10 @@
   EXPECT_EQ(accumulator.GetAndReset(), AudioGlitchInfo());
 }
 
+TEST(AudioGlitchInfo, ToString) {
+  AudioGlitchInfo info{.duration = base::Milliseconds(123), .count = 456};
+
+  EXPECT_EQ(info.ToString(), "duration (ms): 123, count: 456");
+}
+
 }  // namespace media
diff --git a/media/base/audio_hash_unittest.cc b/media/base/audio_hash_unittest.cc
index 60a6480..a12d52a 100644
--- a/media/base/audio_hash_unittest.cc
+++ b/media/base/audio_hash_unittest.cc
@@ -35,7 +35,7 @@
     // audio data, we need to fill each channel manually.
     for (int ch = 0; ch < audio_bus->channels(); ++ch) {
       wrapped_bus->SetChannelData(0, audio_bus->channel(ch));
-      fake_callback_.Render(base::TimeDelta(), base::TimeTicks::Now(), 0,
+      fake_callback_.Render(base::TimeDelta(), base::TimeTicks::Now(), {},
                             wrapped_bus.get());
     }
   }
diff --git a/media/base/audio_renderer_mixer.cc b/media/base/audio_renderer_mixer.cc
index 5ab2e76..56c7cb3 100644
--- a/media/base/audio_renderer_mixer.cc
+++ b/media/base/audio_renderer_mixer.cc
@@ -117,7 +117,7 @@
 
 int AudioRendererMixer::Render(base::TimeDelta delay,
                                base::TimeTicks delay_timestamp,
-                               int prior_frames_skipped,
+                               const AudioGlitchInfo& glitch_info,
                                AudioBus* audio_bus) {
   TRACE_EVENT0("audio", "AudioRendererMixer::Render");
   base::AutoLock auto_lock(lock_);
@@ -140,7 +140,7 @@
 
   uint32_t frames_delayed =
       AudioTimestampHelper::TimeToFrames(delay, output_params_.sample_rate());
-  aggregate_converter_.ConvertWithInfo(frames_delayed, {}, audio_bus);
+  aggregate_converter_.ConvertWithInfo(frames_delayed, glitch_info, audio_bus);
   return audio_bus->frames();
 }
 
diff --git a/media/base/audio_renderer_mixer.h b/media/base/audio_renderer_mixer.h
index 622a11db..1ff0356f 100644
--- a/media/base/audio_renderer_mixer.h
+++ b/media/base/audio_renderer_mixer.h
@@ -59,7 +59,7 @@
   // AudioRendererSink::RenderCallback implementation.
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const AudioGlitchInfo& glitch_info,
              AudioBus* audio_bus) override;
   void OnRenderError() override;
 
diff --git a/media/base/audio_renderer_mixer_input.cc b/media/base/audio_renderer_mixer_input.cc
index 94109bf8..14519b0 100644
--- a/media/base/audio_renderer_mixer_input.cc
+++ b/media/base/audio_renderer_mixer_input.cc
@@ -227,7 +227,7 @@
       AudioTimestampHelper::FramesToTime(frames_delayed, params_.sample_rate());
 
   int frames_filled =
-      callback_->Render(delay, base::TimeTicks::Now(), 0, audio_bus);
+      callback_->Render(delay, base::TimeTicks::Now(), glitch_info, audio_bus);
 
   // AudioConverter expects unfilled frames to be zeroed.
   if (frames_filled < audio_bus->frames()) {
diff --git a/media/base/audio_renderer_mixer_unittest.cc b/media/base/audio_renderer_mixer_unittest.cc
index 96e9eb6..cda235c 100644
--- a/media/base/audio_renderer_mixer_unittest.cc
+++ b/media/base/audio_renderer_mixer_unittest.cc
@@ -171,12 +171,12 @@
 
     // Render actual audio data.
     int frames = mixer_callback_->Render(
-        base::TimeDelta(), base::TimeTicks::Now(), 0, audio_bus_.get());
+        base::TimeDelta(), base::TimeTicks::Now(), {}, audio_bus_.get());
     if (frames != audio_bus_->frames())
       return false;
 
     // Render expected audio data (without scaling).
-    expected_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(), 0,
+    expected_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(), {},
                                expected_audio_bus_.get());
 
     if (half_fill_) {
@@ -340,6 +340,44 @@
       mixer_inputs_[i]->Stop();
   }
 
+  // Verify that glitch info is being propagated properly.
+  void GlitchInfoTest(int inputs) {
+    InitializeInputs(inputs);
+
+    // Play() all mixer inputs and ensure we get the right values.
+    for (auto& mixer_input : mixer_inputs_) {
+      mixer_input->Start();
+      mixer_input->Play();
+    }
+
+    AudioGlitchInfo glitch_info{.duration = base::Milliseconds(100),
+                                .count = 123};
+    AudioGlitchInfo expected_glitch_info;
+
+    for (int i = 0; i < kMixerCycles; ++i) {
+      expected_glitch_info += glitch_info;
+      mixer_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(),
+                              glitch_info, audio_bus_.get());
+    }
+
+    // If the output buffer duration is not divisible by all the input buffer
+    // durations, all glitch info will not necessarily have been propagated yet.
+    // We call Render with empty glitch info a few more times to flush out any
+    // remaining glitch info.
+    for (int i = 0; i < kMixerCycles; ++i) {
+      mixer_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(), {},
+                              audio_bus_.get());
+    }
+
+    for (auto& callback : fake_callbacks_) {
+      EXPECT_EQ(callback->cumulative_glitch_info(), expected_glitch_info);
+    }
+
+    for (auto& mixer_input : mixer_inputs_) {
+      mixer_input->Stop();
+    }
+  }
+
   scoped_refptr<AudioRendererMixerInput> CreateMixerInput() {
     auto input = base::MakeRefCounted<AudioRendererMixerInput>(
         this, base::UnguessableToken::Create(),
@@ -452,6 +490,11 @@
   MixedStopPlayTest(kOddMixerInputs);
 }
 
+// Check that AudioGlitchInfo is propagated.
+TEST_P(AudioRendererMixerTest, PropagatesAudioGlitchInfo) {
+  GlitchInfoTest(kMixerInputs);
+}
+
 TEST_P(AudioRendererMixerBehavioralTest, OnRenderError) {
   InitializeInputs(kMixerInputs);
   for (size_t i = 0; i < mixer_inputs_.size(); ++i) {
@@ -500,7 +543,7 @@
   const base::TimeDelta kSleepTime = base::Milliseconds(100);
   base::TimeTicks start_time = base::TimeTicks::Now();
   while (!pause_event.IsSignaled()) {
-    mixer_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(), 0,
+    mixer_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(), {},
                             audio_bus_.get());
     base::PlatformThread::Sleep(kSleepTime);
     ASSERT_TRUE(base::TimeTicks::Now() - start_time < kTestTimeout);
@@ -516,7 +559,7 @@
   // Ensure once the input is paused the sink eventually pauses.
   start_time = base::TimeTicks::Now();
   while (!pause_event.IsSignaled()) {
-    mixer_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(), 0,
+    mixer_callback_->Render(base::TimeDelta(), base::TimeTicks::Now(), {},
                             audio_bus_.get());
     base::PlatformThread::Sleep(kSleepTime);
     ASSERT_TRUE(base::TimeTicks::Now() - start_time < kTestTimeout);
diff --git a/media/base/audio_renderer_sink.h b/media/base/audio_renderer_sink.h
index 38e883c..fee63ad 100644
--- a/media/base/audio_renderer_sink.h
+++ b/media/base/audio_renderer_sink.h
@@ -17,6 +17,8 @@
 
 namespace media {
 
+struct AudioGlitchInfo;
+
 // AudioRendererSink is an interface representing the end-point for
 // rendered audio.  An implementation is expected to
 // periodically call Render() on a callback object.
@@ -34,7 +36,7 @@
     // |delay_timestamp| represents the time when |delay| was obtained.
     virtual int Render(base::TimeDelta delay,
                        base::TimeTicks delay_timestamp,
-                       int prior_frames_skipped,
+                       const AudioGlitchInfo& glitch_info,
                        AudioBus* dest) = 0;
     // Signals an error has occurred.
     virtual void OnRenderError() = 0;
diff --git a/media/base/fake_audio_render_callback.cc b/media/base/fake_audio_render_callback.cc
index d20f50b..63ce619 100644
--- a/media/base/fake_audio_render_callback.cc
+++ b/media/base/fake_audio_render_callback.cc
@@ -26,8 +26,9 @@
 
 int FakeAudioRenderCallback::Render(base::TimeDelta delay,
                                     base::TimeTicks delay_timestamp,
-                                    int prior_frames_skipped,
+                                    const AudioGlitchInfo& glitch_info,
                                     AudioBus* audio_bus) {
+  cumulative_glitch_info_ += glitch_info;
   return RenderInternal(audio_bus, delay, volume_);
 }
 
diff --git a/media/base/fake_audio_render_callback.h b/media/base/fake_audio_render_callback.h
index 603c17e..beb6bf3 100644
--- a/media/base/fake_audio_render_callback.h
+++ b/media/base/fake_audio_render_callback.h
@@ -34,7 +34,7 @@
   // is set, will only fill half the buffer.
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const AudioGlitchInfo& glitch_info,
              AudioBus* audio_bus) override;
   MOCK_METHOD0(OnRenderError, void());
 
diff --git a/media/base/fake_audio_renderer_sink.cc b/media/base/fake_audio_renderer_sink.cc
index 34a5632..ba33af0 100644
--- a/media/base/fake_audio_renderer_sink.cc
+++ b/media/base/fake_audio_renderer_sink.cc
@@ -8,6 +8,7 @@
 #include "base/location.h"
 #include "base/logging.h"
 #include "base/task/sequenced_task_runner.h"
+#include "media/base/audio_glitch_info.h"
 
 namespace media {
 
@@ -94,7 +95,7 @@
   if (state_ != kPlaying)
     return false;
 
-  *frames_written = callback_->Render(delay, base::TimeTicks::Now(), 0, dest);
+  *frames_written = callback_->Render(delay, base::TimeTicks::Now(), {}, dest);
   return true;
 }
 
diff --git a/media/base/silent_sink_suspender.cc b/media/base/silent_sink_suspender.cc
index bdc5829..2f74573 100644
--- a/media/base/silent_sink_suspender.cc
+++ b/media/base/silent_sink_suspender.cc
@@ -6,6 +6,7 @@
 
 #include "base/bind.h"
 #include "base/task/single_thread_task_runner.h"
+#include "media/base/audio_glitch_info.h"
 
 namespace media {
 
@@ -37,7 +38,7 @@
 
 int SilentSinkSuspender::Render(base::TimeDelta delay,
                                 base::TimeTicks delay_timestamp,
-                                int prior_frames_skipped,
+                                const AudioGlitchInfo& glitch_info,
                                 AudioBus* dest) {
   // Lock required since AudioRendererSink::Pause() is not synchronous, we need
   // to discard these calls during the transition to the fake sink.
@@ -53,7 +54,6 @@
   // the audio data for a future transition out of silence.
   if (!dest) {
     DCHECK(is_using_fake_sink_);
-    DCHECK_EQ(prior_frames_skipped, 0);
     // |delay_timestamp| contains the value cached at
     // |latest_output_delay_timestamp_|
     // so we simulate the real sink output, promoting |delay_timestamp| with
@@ -80,7 +80,7 @@
   }
 
   // Pass-through to client and request rendering.
-  callback_->Render(delay, delay_timestamp, prior_frames_skipped, dest);
+  callback_->Render(delay, delay_timestamp, glitch_info, dest);
 
   // Check for silence or real audio data and transition if necessary.
   if (!dest->AreFramesZero() || !detect_silence_) {
@@ -195,7 +195,7 @@
           // new timestamps being provided by FakeAudioWorker, in that it's call
           // to base::TimeTicks::Now() can be eliminated (use |now| instead),
           // along with its custom delay timestamp calculations.
-          suspender->Render(frozen_delay, frozen_delay_timestamp, 0, nullptr);
+          suspender->Render(frozen_delay, frozen_delay_timestamp, {}, nullptr);
         },
         this, latest_output_delay_, latest_output_delay_timestamp_));
   } else {
diff --git a/media/base/silent_sink_suspender.h b/media/base/silent_sink_suspender.h
index 969102be..58f78bf1 100644
--- a/media/base/silent_sink_suspender.h
+++ b/media/base/silent_sink_suspender.h
@@ -52,7 +52,7 @@
   // AudioRendererSink::RenderCallback implementation.
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const AudioGlitchInfo& glitch_info,
              AudioBus* dest) override;
   void OnRenderError() override;
 
diff --git a/media/base/silent_sink_suspender_unittest.cc b/media/base/silent_sink_suspender_unittest.cc
index 520cf26..fcd65c5 100644
--- a/media/base/silent_sink_suspender_unittest.cc
+++ b/media/base/silent_sink_suspender_unittest.cc
@@ -50,11 +50,15 @@
 TEST_F(SilentSinkSuspenderTest, BasicPassthough) {
   temp_bus_->Zero();
   auto delay = base::Milliseconds(20);
+  AudioGlitchInfo glitch_info{.duration = base::Milliseconds(100),
+                              .count = 123};
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(delay, base::TimeTicks(), 0, temp_bus_.get()));
+            suspender_.Render(delay, base::TimeTicks(), glitch_info,
+                              temp_bus_.get()));
 
   // Delay should remain.
   EXPECT_EQ(delay, fake_callback_.last_delay());
+  EXPECT_EQ(glitch_info, fake_callback_.cumulative_glitch_info());
   EXPECT_FALSE(temp_bus_->AreFramesZero());
 }
 
@@ -63,7 +67,7 @@
   EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_FALSE(temp_bus_->AreFramesZero());
   base::RunLoop().RunUntilIdle();
@@ -73,7 +77,7 @@
   fake_callback_.set_volume(0);
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_TRUE(temp_bus_->AreFramesZero());
   {
@@ -100,11 +104,11 @@
   // not silent.
   fake_callback_.reset();
   std::unique_ptr<AudioBus> true_bus = AudioBus::Create(params_);
-  fake_callback_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+  fake_callback_.Render(base::TimeDelta(), base::TimeTicks(), {},
                         true_bus.get());
   EXPECT_FALSE(true_bus->AreFramesZero());
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_EQ(memcmp(temp_bus_->channel(0), true_bus->channel(0),
                    temp_bus_->frames() * sizeof(float)),
@@ -116,13 +120,13 @@
   fake_callback_.set_volume(0);
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_TRUE(temp_bus_->AreFramesZero());
 
   // A second render should only result in a single Pause() call.
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
 
   EXPECT_CALL(*mock_sink_, Pause());
@@ -135,7 +139,7 @@
   fake_callback_.set_volume(0);
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_TRUE(temp_bus_->AreFramesZero());
   EXPECT_CALL(*mock_sink_, Pause());
@@ -148,10 +152,10 @@
   // Prepare our equality testers.
   fake_callback_.reset();
   std::unique_ptr<AudioBus> true_bus1 = AudioBus::Create(params_);
-  fake_callback_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+  fake_callback_.Render(base::TimeDelta(), base::TimeTicks(), {},
                         true_bus1.get());
   std::unique_ptr<AudioBus> true_bus2 = AudioBus::Create(params_);
-  fake_callback_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+  fake_callback_.Render(base::TimeDelta(), base::TimeTicks(), {},
                         true_bus2.get());
   EXPECT_NE(memcmp(true_bus1->channel(0), true_bus2->channel(0),
                    true_bus1->frames() * sizeof(float)),
@@ -162,23 +166,23 @@
   fake_callback_.reset();
   EXPECT_EQ(
       temp_bus_->frames(),
-      suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0, nullptr));
+      suspender_.Render(base::TimeDelta(), base::TimeTicks(), {}, nullptr));
   EXPECT_EQ(
       temp_bus_->frames(),
-      suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0, nullptr));
+      suspender_.Render(base::TimeDelta(), base::TimeTicks(), {}, nullptr));
   EXPECT_CALL(*mock_sink_, Play());
   base::RunLoop().RunUntilIdle();
   EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
 
   // Each render after resuming should return one of the non-silent bus.
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_EQ(memcmp(temp_bus_->channel(0), true_bus1->channel(0),
                    temp_bus_->frames() * sizeof(float)),
             0);
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_EQ(memcmp(temp_bus_->channel(0), true_bus2->channel(0),
                    temp_bus_->frames() * sizeof(float)),
@@ -190,7 +194,7 @@
   EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_FALSE(temp_bus_->AreFramesZero());
   base::RunLoop().RunUntilIdle();
@@ -200,7 +204,7 @@
   fake_callback_.set_volume(0);
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_TRUE(temp_bus_->AreFramesZero());
   {
@@ -230,7 +234,7 @@
   // audio.
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
 
   base::RunLoop().RunUntilIdle();
@@ -241,7 +245,7 @@
   suspender_.SetDetectSilence(true);
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_TRUE(temp_bus_->AreFramesZero());
   {
@@ -258,7 +262,7 @@
   EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_FALSE(temp_bus_->AreFramesZero());
   base::RunLoop().RunUntilIdle();
@@ -271,7 +275,7 @@
   fake_callback_.set_volume(0);
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
   EXPECT_TRUE(temp_bus_->AreFramesZero());
   {
@@ -290,7 +294,7 @@
   // Render silence again, which should attempt to transition to the fake sink.
   temp_bus_->Zero();
   EXPECT_EQ(temp_bus_->frames(),
-            suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+            suspender_.Render(base::TimeDelta(), base::TimeTicks(), {},
                               temp_bus_.get()));
 
   // OnPaused() should cancel any pending transitions.
diff --git a/media/renderers/audio_renderer_impl.cc b/media/renderers/audio_renderer_impl.cc
index f601f26..6c80abe 100644
--- a/media/renderers/audio_renderer_impl.cc
+++ b/media/renderers/audio_renderer_impl.cc
@@ -1166,12 +1166,12 @@
 
 int AudioRendererImpl::Render(base::TimeDelta delay,
                               base::TimeTicks delay_timestamp,
-                              int prior_frames_skipped,
+                              const AudioGlitchInfo& glitch_info,
                               AudioBus* audio_bus) {
   TRACE_EVENT1("media", "AudioRendererImpl::Render", "id", player_id_);
   int frames_requested = audio_bus->frames();
-  DVLOG(4) << __func__ << " delay:" << delay
-           << " prior_frames_skipped:" << prior_frames_skipped
+  DVLOG(4) << __func__ << " delay:" << delay << " glitch_info:["
+           << glitch_info.ToString() << "]"
            << " frames_requested:" << frames_requested;
 
   // Since this information is coming from the OS or potentially a fake stream,
diff --git a/media/renderers/audio_renderer_impl.h b/media/renderers/audio_renderer_impl.h
index e9a88411..2bab087 100644
--- a/media/renderers/audio_renderer_impl.h
+++ b/media/renderers/audio_renderer_impl.h
@@ -187,7 +187,7 @@
   // should the filled buffer be played.
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const AudioGlitchInfo& glitch_info,
              AudioBus* dest) override;
   void OnRenderError() override;
 
diff --git a/mojo/public/cpp/bindings/BUILD.gn b/mojo/public/cpp/bindings/BUILD.gn
index c080f1a..aa7a4e4 100644
--- a/mojo/public/cpp/bindings/BUILD.gn
+++ b/mojo/public/cpp/bindings/BUILD.gn
@@ -169,8 +169,6 @@
     "lib/interface_ptr_state.h",
     "lib/interface_serialization.h",
     "lib/message_dispatcher.cc",
-    "lib/message_quota_checker.cc",
-    "lib/message_quota_checker.h",
     "lib/multiplex_router.cc",
     "lib/multiplex_router.h",
     "lib/native_enum_data.h",
diff --git a/mojo/public/cpp/bindings/connector.h b/mojo/public/cpp/bindings/connector.h
index 4bc9f721..a35ba18 100644
--- a/mojo/public/cpp/bindings/connector.h
+++ b/mojo/public/cpp/bindings/connector.h
@@ -30,9 +30,6 @@
 }
 
 namespace mojo {
-namespace internal {
-class MessageQuotaChecker;
-}
 
 class SyncHandleWatcher;
 
@@ -224,10 +221,6 @@
 
   base::SequencedTaskRunner* task_runner() const { return task_runner_.get(); }
 
-  // Sets the quota checker.
-  void SetMessageQuotaChecker(
-      scoped_refptr<internal::MessageQuotaChecker> checker);
-
   // Allows testing environments to override the default serialization behavior
   // of newly constructed Connector instances. Must be called before any
   // Connector instances are constructed.
@@ -334,9 +327,6 @@
 
   SEQUENCE_CHECKER(sequence_checker_);
 
-  // The quota checker associate with this connector, if any.
-  scoped_refptr<internal::MessageQuotaChecker> quota_checker_;
-
   // Indicates whether the Connector is configured to actively read from its
   // message pipe. As long as this is true, the Connector is only safe to
   // destroy in sequence with `task_runner_` tasks.
diff --git a/mojo/public/cpp/bindings/lib/connector.cc b/mojo/public/cpp/bindings/lib/connector.cc
index b69c53d5..5835675 100644
--- a/mojo/public/cpp/bindings/lib/connector.cc
+++ b/mojo/public/cpp/bindings/lib/connector.cc
@@ -28,7 +28,6 @@
 #include "mojo/public/c/system/quota.h"
 #include "mojo/public/cpp/bindings/features.h"
 #include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/mojo_buildflags.h"
 #include "mojo/public/cpp/bindings/sync_handle_watcher.h"
 #include "mojo/public/cpp/bindings/tracing_helpers.h"
@@ -182,13 +181,6 @@
 }
 
 Connector::~Connector() {
-  if (quota_checker_) {
-    // Clear the message pipe handle in the checker.
-    quota_checker_->SetMessagePipe(MessagePipeHandle());
-    UMA_HISTOGRAM_COUNTS_1M("Mojo.Connector.MaxUnreadMessageQuotaUsed",
-                            quota_checker_->GetMaxQuotaUsage());
-  }
-
   if (is_receiving_) {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
     CancelWait();
@@ -351,9 +343,6 @@
     }
   }
 
-  if (quota_checker_)
-    quota_checker_->BeforeWrite();
-
   MojoResult rv =
       WriteMessageNew(message_pipe_.get(), message->TakeMojoMessage(),
                       MOJO_WRITE_MESSAGE_FLAG_NONE);
@@ -398,14 +387,6 @@
   sync_watcher_->AllowWokenUpBySyncWatchOnSameThread();
 }
 
-void Connector::SetMessageQuotaChecker(
-    scoped_refptr<internal::MessageQuotaChecker> checker) {
-  DCHECK(checker && !quota_checker_);
-
-  quota_checker_ = std::move(checker);
-  quota_checker_->SetMessagePipe(message_pipe_.get());
-}
-
 // static
 void Connector::OverrideDefaultSerializationBehaviorForTesting(
     OutgoingSerializationMode outgoing_mode,
diff --git a/mojo/public/cpp/bindings/lib/message_quota_checker.cc b/mojo/public/cpp/bindings/lib/message_quota_checker.cc
deleted file mode 100644
index 8921b41..0000000
--- a/mojo/public/cpp/bindings/lib/message_quota_checker.cc
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
-
-#include <algorithm>
-
-#include "base/check_op.h"
-#include "base/debug/activity_tracker.h"
-#include "base/debug/alias.h"
-#include "base/debug/dump_without_crashing.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/rand_util.h"
-#include "base/synchronization/lock.h"
-#include "base/types/pass_key.h"
-#include "mojo/public/c/system/quota.h"
-#include "mojo/public/cpp/bindings/features.h"
-#include "mojo/public/cpp/bindings/mojo_buildflags.h"
-
-namespace mojo {
-namespace internal {
-namespace {
-
-const base::FeatureParam<int> kMojoRecordUnreadMessageCountSampleRate = {
-    &features::kMojoRecordUnreadMessageCount, "SampleRate",
-    100  // Sample 1% of Connectors by default. */
-};
-
-const base::FeatureParam<int> kMojoRecordUnreadMessageCountQuotaValue = {
-    &features::kMojoRecordUnreadMessageCount, "QuotaValue",
-    100  // Use a 100 message quote by default.
-};
-
-const base::FeatureParam<int> kMojoRecordUnreadMessageCountCrashThreshold = {
-    &features::kMojoRecordUnreadMessageCount, "CrashThreshold",
-    0  // Set to zero to disable crash dumps by default.
-};
-
-NOINLINE void MaybeDumpWithoutCrashing(
-    size_t total_quota_used,
-    absl::optional<size_t> message_pipe_quota_used,
-    int64_t seconds_since_construction,
-    double average_write_rate,
-    uint64_t messages_enqueued,
-    uint64_t messages_dequeued,
-    uint64_t messages_written) {
-  static bool have_crashed = false;
-  if (have_crashed)
-    return;
-
-  // Only crash once per process/per run. Note that this is slightly racy
-  // against concurrent quota overruns on multiple threads, but that's fine.
-  have_crashed = true;
-
-  size_t local_quota_used = total_quota_used;
-  bool had_message_pipe = false;
-  if (message_pipe_quota_used.has_value()) {
-    had_message_pipe = true;
-    local_quota_used -= message_pipe_quota_used.value();
-  }
-
-  // Normalize the write rate to writes/second.
-  double average_write_rate_per_second =
-      average_write_rate /
-      MessageQuotaChecker::DecayingRateAverage::kSamplingInterval.InSecondsF();
-  base::debug::Alias(&total_quota_used);
-  base::debug::Alias(&local_quota_used);
-  base::debug::Alias(&had_message_pipe);
-  base::debug::Alias(&seconds_since_construction);
-  base::debug::Alias(&average_write_rate_per_second);
-
-  // Note that these values are acquired non-atomically with respect to the
-  // variables above, and so may have increased since the quota overflow
-  // occurred. They will still give a good indication of the traffic and the
-  // traffic mix on this MessageQuotaChecker.
-  base::debug::Alias(&messages_enqueued);
-  base::debug::Alias(&messages_dequeued);
-  base::debug::Alias(&messages_written);
-
-  // Also record the data for extended crash reporting.
-  base::debug::ScopedActivity scoped_activity;
-  auto& user_data = scoped_activity.user_data();
-  user_data.SetUint("total_quota_used", total_quota_used);
-  user_data.SetUint("local_quota_used", local_quota_used);
-  user_data.SetBool("had_message_pipe", had_message_pipe);
-  user_data.SetUint("seconds_since_construction", seconds_since_construction);
-  user_data.SetUint("average_write_rate_per_second",
-                    static_cast<uint64_t>(average_write_rate_per_second));
-  user_data.SetUint("messages_enqueued", messages_enqueued);
-  user_data.SetUint("messages_dequeued", messages_dequeued);
-  user_data.SetUint("messages_enqueued", messages_enqueued);
-  user_data.SetUint("messages_written", messages_written);
-
-  // This is happening because the user of the interface implicated on the crash
-  // stack has queued up an unreasonable number of messages, namely
-  // |total_quota_used|.
-  base::debug::DumpWithoutCrashing();
-}
-
-int64_t ToSamplingInterval(base::TimeTicks when) {
-  return (when - base::TimeTicks::UnixEpoch())
-      .IntDiv(MessageQuotaChecker::DecayingRateAverage::kSamplingInterval);
-}
-
-base::TimeTicks FromSamplingInterval(int64_t sampling_interval) {
-  return MessageQuotaChecker::DecayingRateAverage::kSamplingInterval *
-             sampling_interval +
-         base::TimeTicks::UnixEpoch();
-}
-
-}  // namespace
-
-constexpr base::TimeDelta
-    MessageQuotaChecker::DecayingRateAverage::kSamplingInterval;
-constexpr double MessageQuotaChecker::DecayingRateAverage::kSampleWeight;
-
-// static
-scoped_refptr<MessageQuotaChecker> MessageQuotaChecker::MaybeCreate() {
-  static const Configuration config = GetConfiguration();
-  return MaybeCreateImpl(config);
-}
-
-void MessageQuotaChecker::BeforeWrite() {
-  ++messages_written_;
-  QuotaCheckImpl(0u);
-}
-
-void MessageQuotaChecker::BeforeMessagesEnqueued(size_t num) {
-  DCHECK_NE(num, 0u);
-  messages_enqueued_ += num;
-  QuotaCheckImpl(num);
-}
-
-void MessageQuotaChecker::AfterMessagesDequeued(size_t num) {
-  base::AutoLock hold(lock_);
-  DCHECK_LE(num, consumed_quota_);
-  DCHECK_NE(num, 0u);
-  messages_dequeued_ += num;
-  consumed_quota_ -= num;
-}
-
-size_t MessageQuotaChecker::GetMaxQuotaUsage() {
-  base::AutoLock hold(lock_);
-  return max_consumed_quota_;
-}
-
-void MessageQuotaChecker::SetMessagePipe(MessagePipeHandle message_pipe) {
-  base::AutoLock hold(lock_);
-  message_pipe_ = message_pipe;
-  if (!message_pipe_)
-    return;
-
-  MojoResult rv =
-      MojoSetQuota(message_pipe.value(), MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT,
-                   config_->unread_message_count_quota, nullptr);
-  DCHECK_EQ(MOJO_RESULT_OK, rv);
-}
-
-size_t MessageQuotaChecker::GetCurrentQuotaStatusForTesting() {
-  base::AutoLock hold(lock_);
-  size_t quota_used = consumed_quota_;
-  absl::optional<size_t> message_pipe_quota_used = GetCurrentMessagePipeQuota();
-  if (message_pipe_quota_used.has_value())
-    quota_used += message_pipe_quota_used.value();
-
-  return quota_used;
-}
-
-// static
-MessageQuotaChecker::Configuration
-MessageQuotaChecker::GetConfigurationForTesting() {
-  return GetConfiguration();
-}
-
-// static
-scoped_refptr<MessageQuotaChecker> MessageQuotaChecker::MaybeCreateForTesting(
-    const Configuration& config) {
-  return MaybeCreateImpl(config);
-}
-
-MessageQuotaChecker::MessageQuotaChecker(const Configuration* config,
-                                         base::PassKey<MessageQuotaChecker>)
-    : config_(config), creation_time_(base::TimeTicks::Now()) {}
-MessageQuotaChecker::~MessageQuotaChecker() = default;
-
-// static
-MessageQuotaChecker::Configuration MessageQuotaChecker::GetConfiguration() {
-  // Const since this may be called from any thread. Initialization is
-  // thread-safe. This is a workaround since some consumers of Mojo (e.g. many
-  // browser tests) use base::FeatureList incorrectly and thus cause data races
-  // when features are queried from arbitrary threads.
-  static const bool is_enabled =
-      base::FeatureList::IsEnabled(features::kMojoRecordUnreadMessageCount);
-
-  Configuration ret;
-  ret.is_enabled = is_enabled;
-  ret.sample_rate = kMojoRecordUnreadMessageCountSampleRate.Get();
-
-  // Lower-bound the quota value to 100, which implies roughly 2% message
-  // overhead for sampled pipes.
-  constexpr int kMinQuotaValue = 100;
-  ret.unread_message_count_quota =
-      std::max(kMinQuotaValue, kMojoRecordUnreadMessageCountQuotaValue.Get());
-  ret.crash_threshold = kMojoRecordUnreadMessageCountCrashThreshold.Get();
-  ret.maybe_crash_function = &MaybeDumpWithoutCrashing;
-  return ret;
-}
-
-// static
-scoped_refptr<MessageQuotaChecker> MessageQuotaChecker::MaybeCreateImpl(
-    const Configuration& config) {
-  if (!config.is_enabled)
-    return nullptr;
-
-  if (base::RandInt(0, config.sample_rate - 1) != 0)
-    return nullptr;
-
-  return base::MakeRefCounted<MessageQuotaChecker>(
-      &config, base::PassKey<MessageQuotaChecker>());
-}
-
-absl::optional<size_t> MessageQuotaChecker::GetCurrentMessagePipeQuota() {
-  lock_.AssertAcquired();
-
-  if (!message_pipe_)
-    return absl::nullopt;
-
-  uint64_t limit = 0;
-  uint64_t usage = 0;
-  MojoResult rv = MojoQueryQuota(message_pipe_.value(),
-                                 MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr,
-                                 &limit, &usage);
-  DCHECK_NE(MOJO_QUOTA_LIMIT_NONE, limit);
-  return rv == MOJO_RESULT_OK ? usage : 0u;
-}
-
-void MessageQuotaChecker::QuotaCheckImpl(size_t num_enqueued) {
-  bool new_max = false;
-
-  // By the time a crash is reported, another thread might have consumed some of
-  // the locally queued messages, and/or the message pipe might have been unset.
-  // To make the crash reports as useful as possible, grab the state of the
-  // local and the message pipe queues into individual variables, then pass them
-  // into the crashing function.
-  size_t total_quota_used = 0u;
-  base::TimeTicks now;
-  double average_write_rate = 0.0;
-  absl::optional<size_t> message_pipe_quota_used;
-  {
-    base::AutoLock hold(lock_);
-
-    message_pipe_quota_used = GetCurrentMessagePipeQuota();
-    now = base::TimeTicks::Now();
-    if (num_enqueued) {
-      consumed_quota_ += num_enqueued;
-    } else {
-      // BeforeWrite passes num_enqueued zero, as the message won't be locally
-      // enqueued. The assumption is that there's already a message pipe in
-      // play, and that the caller is keeping it alive somehow.
-      DCHECK(message_pipe_);
-      DCHECK(message_pipe_quota_used.has_value());
-
-      // Accrue this write event to the write rate average.
-      write_rate_average_.AccrueEvent(now);
-
-      // Account for the message about to be written to the message pipe in the
-      // the full tally.
-      ++message_pipe_quota_used.value();
-    }
-
-    total_quota_used += consumed_quota_;
-    if (message_pipe_quota_used.has_value())
-      total_quota_used += message_pipe_quota_used.value();
-
-    if (total_quota_used > max_consumed_quota_) {
-      max_consumed_quota_ = total_quota_used;
-      new_max = true;
-      // Retrieve the average rate, in the case that a crash is imminent.
-      average_write_rate = write_rate_average_.GetDecayedRateAverage(now);
-    }
-  }
-
-  if (new_max && config_->crash_threshold != 0 &&
-      total_quota_used >= config_->crash_threshold) {
-    DCHECK(!now.is_null());
-    int64_t seconds_since_construction = (now - creation_time_).InSeconds();
-    config_->maybe_crash_function(
-        total_quota_used, message_pipe_quota_used, seconds_since_construction,
-        average_write_rate, messages_enqueued_.load(),
-        messages_dequeued_.load(), messages_written_.load());
-  }
-}
-
-MessageQuotaChecker::DecayingRateAverage::DecayingRateAverage() {
-  events_sampling_interval_ = ToSamplingInterval(base::TimeTicks::Now());
-}
-
-void MessageQuotaChecker::DecayingRateAverage::AccrueEvent(
-    base::TimeTicks when) {
-  const int64_t sampling_interval = ToSamplingInterval(when);
-  DCHECK_GE(sampling_interval, events_sampling_interval_);
-  if (sampling_interval == events_sampling_interval_) {
-    // The time is still in the sampling interval, just add the event.
-    ++events_;
-    return;
-  }
-  DCHECK_GT(sampling_interval, events_sampling_interval_);
-
-  // Add the new sample and decay it to the previous event sampling interval.
-  decayed_average_ = events_ * kSampleWeight + decayed_average_ * kDecayFactor;
-
-  // Decay the average to the current sampling interval - 1.
-  const int64_t decayed_average_age =
-      sampling_interval - events_sampling_interval_ - 1;
-  if (decayed_average_age)
-    decayed_average_ *= pow(kDecayFactor, decayed_average_age);
-
-  // Start a new event sampling interval.
-  events_ = 1;
-  events_sampling_interval_ = sampling_interval;
-}
-
-double MessageQuotaChecker::DecayingRateAverage::GetDecayedRateAverage(
-    base::TimeTicks when) const {
-  // Three cases:
-  // - |when| is exactly at the start of a sampling interval.
-  // - |when| is within the current sampling interval.
-  // - |when| is beyond the end of the current sampling interval.
-  const int64_t sampling_interval = ToSamplingInterval(when);
-  double age_in_sampling_intervals =
-      (when - FromSamplingInterval(events_sampling_interval_)) /
-      kSamplingInterval;
-  DCHECK_LE(0.0, age_in_sampling_intervals);
-  if (when == FromSamplingInterval(events_sampling_interval_)) {
-    DCHECK_EQ(0.0, age_in_sampling_intervals);
-    // It is possible that an event has been accrued to the very start of a
-    // sampling interval. Technically this converges like so:
-    //
-    // lim when t -> 0 = - events_ * log(kDecayFactor) / kSamplingInterval
-    //
-    // For simplicity's sake, this is treated as synonymous with the decayed
-    // average at the end of the previous sampling interval here.
-    return decayed_average_;
-  } else if (sampling_interval == events_sampling_interval_) {
-    DCHECK_GT(1.0, age_in_sampling_intervals);
-
-    // Use a decay factor that's exponential in the age |when|, relative to
-    // the sampling interval. This yields a stabler estimate than straight up
-    // extrapolating the rate to the end of the sampling interval, as that
-    // method is very sensitive to noise in sample timing near zero age.
-    double decay_factor = pow(kDecayFactor, age_in_sampling_intervals);
-    // Scale up the events collected so far to the rate.
-    double event_rate = events_ / age_in_sampling_intervals;
-
-    return event_rate * (1.0 - decay_factor) + decayed_average_ * decay_factor;
-  } else {
-    DCHECK_LE(1.0, age_in_sampling_intervals);
-    // Compute the decayed average to the start of
-    // events_sampling_interval_ + 1.
-    double average = events_ * kSampleWeight + decayed_average_ * kDecayFactor;
-
-    // And age it to |when|.
-    return average * pow(kDecayFactor, age_in_sampling_intervals - 1.0);
-  }
-}
-
-// static
-base::TimeTicks
-MessageQuotaChecker::DecayingRateAverage::GetNextSamplingIntervalForTesting(
-    base::TimeTicks when) {
-  return FromSamplingInterval(ToSamplingInterval(when) + 1);
-}
-
-}  // namespace internal
-}  // namespace mojo
diff --git a/mojo/public/cpp/bindings/lib/message_quota_checker.h b/mojo/public/cpp/bindings/lib/message_quota_checker.h
deleted file mode 100644
index 3e1752c..0000000
--- a/mojo/public/cpp/bindings/lib/message_quota_checker.h
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUOTA_CHECKER_H_
-#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUOTA_CHECKER_H_
-
-#include <stdint.h>
-#include <memory>
-
-#include "base/component_export.h"
-#include "base/memory/raw_ptr.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/lock.h"
-#include "base/thread_annotations.h"
-#include "base/time/time.h"
-#include "base/types/pass_key.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace mojo {
-namespace internal {
-
-// This class keeps track of how many messages are in-flight for a message pipe,
-// including messages that are posted or locally queued.
-//
-// Message pipe owners may have reason to implement their own mechanism for
-// queuing outgoing messages before writing them to a pipe. This class helps
-// with unread message quota monitoring in such cases, since Mojo's own
-// quota monitoring on the pipe cannot account for such external queues.
-// Callers are responsible for invoking  |BeforeMessagesEnqueued()| and
-// |AfterMessagesDequeued()| when making respective changes to their local
-// outgoing queue. Additionally, |BeforeWrite()| should be called immediately
-// before writing each message to the corresponding message pipe.
-//
-// Also note that messages posted to a different sequence with base::ThreadPool
-// and the like, need to be treated as locally queued. Task queues can grow
-// arbitrarily long, and it's ideal to perform unread quota checks before
-// posting.
-//
-// Either |BeforeMessagesEnqueued()| or |BeforeWrite()| may cause the quota
-// to be exceeded, thus invoking the |maybe_crash_function| set in this
-// object's Configuration.
-class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) MessageQuotaChecker
-    : public base::RefCountedThreadSafe<MessageQuotaChecker> {
- public:
-  // A helper class to maintain a decaying average for the rate of events per
-  // sampling interval over time.
-  class COMPONENT_EXPORT(MOJO_CPP_BINDINGS) DecayingRateAverage {
-   public:
-    DecayingRateAverage();
-
-    // Accrues one event at time |when|. Note that |when| must increase
-    // monotonically from one event to the next.
-    void AccrueEvent(base::TimeTicks when);
-    // Retrieves the current rate average, decayed to |when|.
-    double GetDecayedRateAverage(base::TimeTicks when) const;
-
-    // The length of a sampling interval in seconds.
-    static constexpr base::TimeDelta kSamplingInterval = base::Seconds(5);
-
-    // Returns the start of the sampling interval after the interval that
-    // |when| falls into.
-    static base::TimeTicks GetNextSamplingIntervalForTesting(
-        base::TimeTicks when);
-
-   private:
-    // A new sample is weighed at this rate into the average, whereas the old
-    // average is weighed at kDecayFactor^age; Note that
-    // (kSampleWeight + kDecayFactor) == 1.0.
-    static constexpr double kSampleWeight = 0.5;
-    static constexpr double kDecayFactor = (1 - kSampleWeight);
-
-    // The event count for the current or most recent sampling interval and
-    // the ordinal sampling interval they correspond to.
-    size_t events_ = 0;
-    int64_t events_sampling_interval_;
-
-    // The so-far accrued average to |events_sampling_interval_|.
-    double decayed_average_ = 0.0;
-  };
-
-  // Exposed for use in tests.
-  struct Configuration;
-
-  // Returns a new instance if this invocation has been sampled for quota
-  // checking.
-  static scoped_refptr<MessageQuotaChecker> MaybeCreate();
-
-  // Public for base::MakeRefCounted(). Use MaybeCreate().
-  MessageQuotaChecker(const Configuration* config,
-                      base::PassKey<MessageQuotaChecker>);
-
-  // Call before writing a message to |message_pipe_|.
-  void BeforeWrite();
-
-  // Call before queueing |num| messages.
-  void BeforeMessagesEnqueued(size_t num);
-  // Call after de-queueing |num| messages.
-  void AfterMessagesDequeued(size_t num);
-
-  // Returns the high watermark of quota usage observed by this instance.
-  size_t GetMaxQuotaUsage();
-
-  // Set or unset the message pipe associated with this quota checker.
-  void SetMessagePipe(MessagePipeHandle message_pipe);
-
-  // Test support.
-  size_t GetCurrentQuotaStatusForTesting();
-  static Configuration GetConfigurationForTesting();
-  static scoped_refptr<MessageQuotaChecker> MaybeCreateForTesting(
-      const Configuration& config);
-
- private:
-  friend class base::RefCountedThreadSafe<MessageQuotaChecker>;
-  ~MessageQuotaChecker();
-  static Configuration GetConfiguration();
-  static scoped_refptr<MessageQuotaChecker> MaybeCreateImpl(
-      const Configuration& config);
-
-  // Returns the amount of unread message quota currently used if there is
-  // an associated message pipe.
-  absl::optional<size_t> GetCurrentMessagePipeQuota();
-  void QuotaCheckImpl(size_t num_enqueued);
-
-  raw_ptr<const Configuration> config_;
-
-  // The time ticks when this instance was created.
-  const base::TimeTicks creation_time_;
-
-
-  // Cumulative counts for the number of messages enqueued with
-  // |BeforeMessagesEnqueued()| and dequeued with |BeforeMessagesDequeued()|.
-  std::atomic<uint64_t> messages_enqueued_{0};
-  std::atomic<uint64_t> messages_dequeued_{0};
-  std::atomic<uint64_t> messages_written_{0};
-
-  // Guards all state below here.
-  base::Lock lock_;
-
-  // A decaying average of the rate of call to BeforeWrite per second.
-  DecayingRateAverage write_rate_average_ GUARDED_BY(lock_);
-
-  // The locally consumed quota, e.g. the difference between the counts passed
-  // to |BeforeMessagesEnqueued()| and |BeforeMessagesDequeued()|.
-  size_t consumed_quota_ GUARDED_BY(lock_) = 0u;
-  // The high watermark consumed quota observed.
-  size_t max_consumed_quota_ GUARDED_BY(lock_) = 0u;
-  // The message pipe this instance observes, if any.
-  MessagePipeHandle message_pipe_ GUARDED_BY(lock_);
-};
-
-struct MessageQuotaChecker::Configuration {
-  bool is_enabled = false;
-  size_t sample_rate = 0u;
-  size_t unread_message_count_quota = 0u;
-  size_t crash_threshold = 0u;
-  void (*maybe_crash_function)(size_t quota_used,
-                               absl::optional<size_t> message_pipe_quota_used,
-                               int64_t seconds_since_construction,
-                               double average_write_rate,
-                               uint64_t messages_enqueued,
-                               uint64_t messages_dequeued,
-                               uint64_t messages_written);
-};
-
-}  // namespace internal
-}  // namespace mojo
-
-#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MESSAGE_QUOTA_CHECKER_H_
diff --git a/mojo/public/cpp/bindings/lib/multiplex_router.cc b/mojo/public/cpp/bindings/lib/multiplex_router.cc
index d8dd91b..1ca2bbd0 100644
--- a/mojo/public/cpp/bindings/lib/multiplex_router.cc
+++ b/mojo/public/cpp/bindings/lib/multiplex_router.cc
@@ -22,7 +22,6 @@
 #include "mojo/public/cpp/bindings/interface_endpoint_client.h"
 #include "mojo/public/cpp/bindings/interface_endpoint_controller.h"
 #include "mojo/public/cpp/bindings/lib/may_auto_lock.h"
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
 #include "mojo/public/cpp/bindings/sequence_local_sync_event_watcher.h"
 
 namespace mojo {
@@ -388,11 +387,6 @@
 
   connector_.set_incoming_receiver(&dispatcher_);
 
-  scoped_refptr<internal::MessageQuotaChecker> quota_checker =
-      internal::MessageQuotaChecker::MaybeCreate();
-  if (quota_checker)
-    connector_.SetMessageQuotaChecker(std::move(quota_checker));
-
   if (primary_interface_name) {
     control_message_handler_.SetDescription(base::JoinString(
         {primary_interface_name, "[primary] PipeControlMessageHandler"}, " "));
diff --git a/mojo/public/cpp/bindings/tests/BUILD.gn b/mojo/public/cpp/bindings/tests/BUILD.gn
index e7f1bfa..160fb60 100644
--- a/mojo/public/cpp/bindings/tests/BUILD.gn
+++ b/mojo/public/cpp/bindings/tests/BUILD.gn
@@ -34,7 +34,6 @@
     "map_unittest.cc",
     "message_queue.cc",
     "message_queue.h",
-    "message_quota_checker_unittest.cc",
     "message_unittest.cc",
     "multiplex_router_unittest.cc",
     "native_struct_unittest.cc",
diff --git a/mojo/public/cpp/bindings/tests/message_quota_checker_unittest.cc b/mojo/public/cpp/bindings/tests/message_quota_checker_unittest.cc
deleted file mode 100644
index 4b22984..0000000
--- a/mojo/public/cpp/bindings/tests/message_quota_checker_unittest.cc
+++ /dev/null
@@ -1,356 +0,0 @@
-// Copyright 2019 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "mojo/public/cpp/bindings/lib/message_quota_checker.h"
-
-#include "base/test/scoped_feature_list.h"
-#include "base/test/task_environment.h"
-#include "base/time/time.h"
-#include "mojo/core/embedder/embedder.h"
-#include "mojo/public/c/system/quota.h"
-#include "mojo/public/cpp/bindings/features.h"
-#include "mojo/public/cpp/system/message_pipe.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/abseil-cpp/absl/types/optional.h"
-
-namespace mojo {
-namespace test {
-namespace {
-
-class MessageQuotaCheckerTest : public testing::Test {
- public:
-  MessageQuotaCheckerTest() {
-    EXPECT_EQ(nullptr, instance_);
-    instance_ = this;
-  }
-  ~MessageQuotaCheckerTest() override {
-    EXPECT_EQ(this, instance_);
-    instance_ = nullptr;
-  }
-
- protected:
-  using MessageQuotaChecker = internal::MessageQuotaChecker;
-  using Configuration = MessageQuotaChecker::Configuration;
-
-  static constexpr base::TimeDelta kOneSamplingInterval =
-      MessageQuotaChecker::DecayingRateAverage::kSamplingInterval;
-
-  void Advance(base::TimeDelta delta) {
-    task_environment_.FastForwardBy(delta);
-  }
-
-  static void RecordDumpAttempt(size_t total_quota_used,
-                                absl::optional<size_t> message_pipe_quota_used,
-                                int64_t seconds_since_construction,
-                                double average_write_rate,
-                                uint64_t messages_enqueued,
-                                uint64_t messages_dequeued,
-                                uint64_t messages_written) {
-    ++instance_->num_dumps_;
-    instance_->last_dump_total_quota_used_ = total_quota_used;
-    instance_->last_dump_message_pipe_quota_used_ = message_pipe_quota_used;
-    instance_->last_seconds_since_construction_ = seconds_since_construction;
-    instance_->last_average_write_rate_ = average_write_rate;
-    instance_->last_messages_enqueued_ = messages_enqueued;
-    instance_->last_messages_dequeued_ = messages_dequeued;
-    instance_->last_messages_written_ = messages_written;
-  }
-
-  // Mock time to allow testing
-  base::test::TaskEnvironment task_environment_{
-      base::test::TaskEnvironment::TimeSource::MOCK_TIME};
-
-  size_t num_dumps_ = false;
-  size_t last_dump_total_quota_used_ = 0u;
-  absl::optional<size_t> last_dump_message_pipe_quota_used_;
-  int64_t last_seconds_since_construction_ = 0;
-  double last_average_write_rate_ = 0.0;
-  uint64_t last_messages_enqueued_ = 0u;
-  uint64_t last_messages_dequeued_ = 0u;
-  uint64_t last_messages_written_ = 0u;
-
-  static const Configuration enabled_config_;
-
-  static MessageQuotaCheckerTest* instance_;
-};
-
-constexpr base::TimeDelta MessageQuotaCheckerTest::kOneSamplingInterval;
-const MessageQuotaCheckerTest::Configuration
-    MessageQuotaCheckerTest::enabled_config_ = {true, 1, 100, 200,
-                                                &RecordDumpAttempt};
-MessageQuotaCheckerTest* MessageQuotaCheckerTest::instance_ = nullptr;
-
-TEST_F(MessageQuotaCheckerTest, ReadsConfigurationFromFeatures) {
-  if (mojo::core::IsMojoIpczEnabled()) {
-    GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz.";
-  }
-
-  base::FieldTrialParams params;
-  params["SampleRate"] = "19";
-  // Quota value parameter below the minimum the checker will allow.
-  params["QuotaValue"] = "57";
-  params["CrashThreshold"] = "225";
-
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(
-      features::kMojoRecordUnreadMessageCount, params);
-
-  // Validate that the configuration reads from the feature configuration.
-  const MessageQuotaChecker::Configuration config =
-      MessageQuotaChecker::GetConfigurationForTesting();
-
-  EXPECT_TRUE(config.is_enabled);
-  EXPECT_EQ(19u, config.sample_rate);
-  EXPECT_EQ(100u, config.unread_message_count_quota);
-  EXPECT_EQ(225u, config.crash_threshold);
-  EXPECT_NE(nullptr, config.maybe_crash_function);
-}
-
-TEST_F(MessageQuotaCheckerTest, DisabledByDefault) {
-  if (mojo::core::IsMojoIpczEnabled()) {
-    GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz.";
-  }
-
-  const MessageQuotaChecker::Configuration config =
-      MessageQuotaChecker::GetConfigurationForTesting();
-  EXPECT_FALSE(config.is_enabled);
-
-  // Validate that no MessageQuoteCheckers are created in the default feature
-  // configuration. Run a bunch of iterations, as this function returns an
-  // instance randomly.
-  for (size_t i = 0; i < 1000; ++i)
-    ASSERT_EQ(nullptr, MessageQuotaChecker::MaybeCreate());
-}
-
-TEST_F(MessageQuotaCheckerTest, CreatesWhenEnabled) {
-  if (mojo::core::IsMojoIpczEnabled()) {
-    GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz.";
-  }
-
-  // Run a bunch of iterations, as this function returns an instance randomly.
-  for (size_t i = 0; i < 1000; ++i)
-    EXPECT_NE(nullptr,
-              MessageQuotaChecker::MaybeCreateForTesting(enabled_config_));
-}
-
-TEST_F(MessageQuotaCheckerTest, CountsRight) {
-  if (mojo::core::IsMojoIpczEnabled()) {
-    GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz.";
-  }
-
-  scoped_refptr<MessageQuotaChecker> checker =
-      MessageQuotaChecker::MaybeCreateForTesting(enabled_config_);
-
-  ASSERT_EQ(0u, checker->GetCurrentQuotaStatusForTesting());
-  ASSERT_EQ(0u, checker->GetMaxQuotaUsage());
-
-  checker->BeforeMessagesEnqueued(10);
-  ASSERT_EQ(10u, checker->GetCurrentQuotaStatusForTesting());
-  ASSERT_EQ(10u, checker->GetMaxQuotaUsage());
-
-  checker->AfterMessagesDequeued(5);
-  ASSERT_EQ(5u, checker->GetCurrentQuotaStatusForTesting());
-  ASSERT_EQ(10u, checker->GetMaxQuotaUsage());
-
-  ASSERT_EQ(0u, num_dumps_);
-}
-
-TEST_F(MessageQuotaCheckerTest, CountsMessagePipeAlso) {
-  if (mojo::core::IsMojoIpczEnabled()) {
-    GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz.";
-  }
-
-  MessagePipe pipe;
-  scoped_refptr<MessageQuotaChecker> checker =
-      MessageQuotaChecker::MaybeCreateForTesting(enabled_config_);
-
-  uint64_t limit = 0;
-  uint64_t usage = 0;
-  MojoResult rv = MojoQueryQuota(pipe.handle0.get().value(),
-                                 MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr,
-                                 &limit, &usage);
-  ASSERT_EQ(MOJO_RESULT_OK, rv);
-  ASSERT_EQ(MOJO_QUOTA_LIMIT_NONE, limit);
-
-  checker->SetMessagePipe(pipe.handle0.get());
-
-  // Validate that the checker sets an unread message quota on the pipe, and
-  // that it clamps to the minimum of 100.
-  rv = MojoQueryQuota(pipe.handle0.get().value(),
-                      MOJO_QUOTA_TYPE_UNREAD_MESSAGE_COUNT, nullptr, &limit,
-                      &usage);
-  ASSERT_EQ(MOJO_RESULT_OK, rv);
-  ASSERT_EQ(100u, limit);
-
-  ASSERT_EQ(0u, checker->GetCurrentQuotaStatusForTesting());
-
-  const char kMessage[] = "hello";
-  for (size_t i = 0; i < 10; ++i) {
-    checker->BeforeWrite();
-    ASSERT_EQ(MOJO_RESULT_OK,
-              WriteMessageRaw(pipe.handle0.get(), kMessage, sizeof(kMessage),
-                              nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
-  }
-
-  ASSERT_EQ(10u, checker->GetMaxQuotaUsage());
-  ASSERT_EQ(10u, checker->GetCurrentQuotaStatusForTesting());
-
-  checker->BeforeMessagesEnqueued(10);
-  ASSERT_EQ(20u, checker->GetMaxQuotaUsage());
-  ASSERT_EQ(20u, checker->GetCurrentQuotaStatusForTesting());
-
-  ASSERT_EQ(0u, num_dumps_);
-}
-
-TEST_F(MessageQuotaCheckerTest, DumpsCoreOnOverrun) {
-  if (mojo::core::IsMojoIpczEnabled()) {
-    GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz.";
-  }
-
-  // Make sure to start the test on an even sampling interval to get consistent
-  // average computations below.
-  base::TimeTicks t0 = MessageQuotaChecker::DecayingRateAverage::
-      GetNextSamplingIntervalForTesting(base::TimeTicks::Now());
-  task_environment_.AdvanceClock(t0 - base::TimeTicks::Now());
-
-  MessagePipe pipe;
-  scoped_refptr<MessageQuotaChecker> checker =
-      MessageQuotaChecker::MaybeCreateForTesting(enabled_config_);
-
-  // Fast forward time by a few sampling intervals.
-  Advance(10 * kOneSamplingInterval);
-
-  // Queue up 100 messages.
-  checker->SetMessagePipe(pipe.handle0.get());
-  const char kMessage[] = "hello";
-  for (size_t i = 0; i < 100; ++i) {
-    checker->BeforeWrite();
-    ASSERT_EQ(MOJO_RESULT_OK,
-              WriteMessageRaw(pipe.handle0.get(), kMessage, sizeof(kMessage),
-                              nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
-  }
-
-  // The crash threshold is at 200 per the config, so shouldn't have attempted
-  // a core dump yet.
-  ASSERT_EQ(0u, num_dumps_);
-
-  checker->BeforeMessagesEnqueued(99);
-  ASSERT_EQ(0u, num_dumps_);
-
-  Advance(kOneSamplingInterval);
-  checker->BeforeMessagesEnqueued(1);
-  EXPECT_EQ(1u, num_dumps_);
-  EXPECT_EQ(200u, last_dump_total_quota_used_);
-  EXPECT_EQ(100u, last_dump_message_pipe_quota_used_);
-  EXPECT_EQ((11 * kOneSamplingInterval).InSeconds(),
-            last_seconds_since_construction_);
-  EXPECT_EQ(50, last_average_write_rate_);
-
-  checker->BeforeWrite();
-  ASSERT_EQ(MOJO_RESULT_OK,
-            WriteMessageRaw(pipe.handle0.get(), kMessage, sizeof(kMessage),
-                            nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE));
-
-  EXPECT_EQ(2u, num_dumps_);
-  EXPECT_EQ(201u, last_dump_total_quota_used_);
-  EXPECT_EQ(101u, last_dump_message_pipe_quota_used_);
-  EXPECT_EQ((11 * kOneSamplingInterval).InSeconds(),
-            last_seconds_since_construction_);
-  EXPECT_EQ(50, last_average_write_rate_);
-
-  Advance(kOneSamplingInterval);
-  checker->SetMessagePipe(mojo::MessagePipeHandle());
-  checker->AfterMessagesDequeued(50);
-  checker->BeforeMessagesEnqueued(300);
-  EXPECT_EQ(3u, num_dumps_);
-  EXPECT_EQ(350u, last_dump_total_quota_used_);
-  EXPECT_FALSE(last_dump_message_pipe_quota_used_.has_value());
-  EXPECT_EQ((12 * kOneSamplingInterval).InSeconds(),
-            last_seconds_since_construction_);
-  EXPECT_EQ(25.5, last_average_write_rate_);
-  EXPECT_EQ(400u, last_messages_enqueued_);
-  EXPECT_EQ(50u, last_messages_dequeued_);
-  EXPECT_EQ(101u, last_messages_written_);
-}
-
-TEST_F(MessageQuotaCheckerTest, DecayingRateAverage) {
-  if (mojo::core::IsMojoIpczEnabled()) {
-    GTEST_SKIP() << "Mojo quota APIs are not supported by MojoIpcz.";
-  }
-
-  // Make sure to start the test on an even sampling interval to get consistent
-  // average computations below.
-  base::TimeTicks t0 = MessageQuotaChecker::DecayingRateAverage::
-      GetNextSamplingIntervalForTesting(base::TimeTicks::Now());
-  task_environment_.AdvanceClock(t0 - base::TimeTicks::Now());
-
-  // Walk time forward a bit from the epoch.
-  t0 += 101 * kOneSamplingInterval;
-
-  MessageQuotaChecker::DecayingRateAverage avg;
-  EXPECT_EQ(0.0, avg.GetDecayedRateAverage(t0));
-
-  // Tally two events in the same sampling interval.
-  avg.AccrueEvent(t0);
-  avg.AccrueEvent(t0);
-
-  // The decayed average rate is half of the rate this sampling interval.
-  t0 += kOneSamplingInterval;
-  EXPECT_EQ(1.0, avg.GetDecayedRateAverage(t0));
-
-  // Tally another two events in a subsequent sampling interval.
-  avg.AccrueEvent(t0);
-  avg.AccrueEvent(t0);
-
-  t0 += kOneSamplingInterval;
-  EXPECT_EQ(1.5, avg.GetDecayedRateAverage(t0));
-
-  // Tally another two events in a subsequent sampling interval.
-  avg.AccrueEvent(t0);
-  avg.AccrueEvent(t0);
-  EXPECT_EQ(1.75, avg.GetDecayedRateAverage(t0 + kOneSamplingInterval));
-
-  // Make sure the average is decayed with time, including within a sampling
-  // interval.
-  EXPECT_EQ(0.875, avg.GetDecayedRateAverage(t0 + 2 * kOneSamplingInterval));
-  EXPECT_NEAR(0.619, avg.GetDecayedRateAverage(t0 + 2.5 * kOneSamplingInterval),
-              0.001);
-  EXPECT_EQ(0.4375, avg.GetDecayedRateAverage(t0 + 3 * kOneSamplingInterval));
-
-  t0 += 10 * kOneSamplingInterval;
-  avg.AccrueEvent(t0);
-  avg.AccrueEvent(t0);
-  // The previous average should have decayed by 1/1024.
-  EXPECT_EQ(1.0 + 1.75 / 1024.0,
-            avg.GetDecayedRateAverage(t0 + kOneSamplingInterval));
-
-  // Explicitly test simple interpolation in a sampling interval by setting
-  // up an average that's conveniently converged to 4.0.
-  MessageQuotaChecker::DecayingRateAverage avg2;
-  for (size_t i = 0; i < 16; ++i)
-    avg2.AccrueEvent(t0);
-  t0 += 2 * kOneSamplingInterval;
-  EXPECT_EQ(4.0, avg2.GetDecayedRateAverage(t0));
-
-  // Now test 0/8, 1/8, 1/4, 1/2 and 3/4-way interpolation.
-  avg2.AccrueEvent(t0);
-  // The special case of adding an event and requesting the average at the
-  // start of sampling interval explicitly excludes that event.
-  EXPECT_EQ(4.0, avg2.GetDecayedRateAverage(t0));
-  EXPECT_NEAR(4.332,
-              avg2.GetDecayedRateAverage(t0 + 1.0 / 8.0 * kOneSamplingInterval),
-              0.001);
-  EXPECT_EQ(4.0,
-            avg2.GetDecayedRateAverage(t0 + 1.0 / 4.0 * kOneSamplingInterval));
-  avg2.AccrueEvent(t0);
-  EXPECT_EQ(4.0,
-            avg2.GetDecayedRateAverage(t0 + 2.0 / 4.0 * kOneSamplingInterval));
-  avg2.AccrueEvent(t0);
-  EXPECT_EQ(4.0,
-            avg2.GetDecayedRateAverage(t0 + 3.0 / 4.0 * kOneSamplingInterval));
-}
-
-}  // namespace
-}  // namespace test
-}  // namespace mojo
diff --git a/services/audio/public/cpp/output_device_unittest.cc b/services/audio/public/cpp/output_device_unittest.cc
index ce9e0527..c574baf 100644
--- a/services/audio/public/cpp/output_device_unittest.cc
+++ b/services/audio/public/cpp/output_device_unittest.cc
@@ -56,7 +56,7 @@
   MOCK_METHOD4(Render,
                int(base::TimeDelta delay,
                    base::TimeTicks timestamp,
-                   int prior_frames_skipped,
+                   const media::AudioGlitchInfo& glitch_info,
                    media::AudioBus* dest));
   void OnRenderError() override {}
 };
@@ -234,8 +234,10 @@
   auto test_bus = media::AudioBus::Create(params);
   for (int i = 0; i < 10; ++i) {
     test_bus->Zero();
+    media::AudioGlitchInfo glitch_info{.duration = base::Milliseconds(100),
+                                       .count = 123};
     EXPECT_CALL(env.render_callback,
-                Render(kDelay, env.time_stamp, 0, NotNull()))
+                Render(kDelay, env.time_stamp, glitch_info, NotNull()))
         .WillOnce(WithArg<3>(Invoke([](media::AudioBus* client_bus) -> int {
           // Place some test data in the bus so that we can check that it was
           // copied to the audio service side.
@@ -243,7 +245,7 @@
           std::fill_n(client_bus->channel(1), client_bus->frames(), kAudioData);
           return client_bus->frames();
         })));
-    env.reader->RequestMoreData(kDelay, env.time_stamp, {});
+    env.reader->RequestMoreData(kDelay, env.time_stamp, glitch_info);
     env.reader->Read(test_bus.get(), false);
 
     Mock::VerifyAndClear(&env.render_callback);
@@ -290,8 +292,10 @@
   auto test_bus = media::AudioBus::Create(params);
   for (int i = 0; i < 10; ++i) {
     test_bus->Zero();
+    media::AudioGlitchInfo glitch_info{.duration = base::Milliseconds(100),
+                                       .count = 123};
     EXPECT_CALL(env.render_callback,
-                Render(kDelay, env.time_stamp, 0, NotNull()))
+                Render(kDelay, env.time_stamp, glitch_info, NotNull()))
         .WillOnce(WithArg<3>(Invoke([](media::AudioBus* renderer_bus) -> int {
           EXPECT_TRUE(renderer_bus->is_bitstream_format());
           // Place some test data in the bus so that we can check that it was
@@ -302,7 +306,7 @@
           renderer_bus->SetBitstreamDataSize(kBitstreamDataSize);
           return renderer_bus->frames();
         })));
-    env.reader->RequestMoreData(kDelay, env.time_stamp, {});
+    env.reader->RequestMoreData(kDelay, env.time_stamp, glitch_info);
     env.reader->Read(test_bus.get(), false);
 
     Mock::VerifyAndClear(&env.render_callback);
diff --git a/services/audio/public/cpp/sounds/audio_stream_handler.cc b/services/audio/public/cpp/sounds/audio_stream_handler.cc
index 1670523..646bc96 100644
--- a/services/audio/public/cpp/sounds/audio_stream_handler.cc
+++ b/services/audio/public/cpp/sounds/audio_stream_handler.cc
@@ -119,7 +119,7 @@
   // Following methods could be called from *ANY* thread.
   int Render(base::TimeDelta /* delay */,
              base::TimeTicks /* delay_timestamp */,
-             int /* prior_frames_skipped */,
+             const media::AudioGlitchInfo& /* glitch_info */,
              media::AudioBus* dest) override {
     base::AutoLock al(state_lock_);
     size_t bytes_written = 0;
diff --git a/services/audio/public/cpp/sounds/test_data.cc b/services/audio/public/cpp/sounds/test_data.cc
index 05086a1..b7f751c 100644
--- a/services/audio/public/cpp/sounds/test_data.cc
+++ b/services/audio/public/cpp/sounds/test_data.cc
@@ -33,7 +33,7 @@
 void TestObserver::Render() {
   if (!is_playing)
     return;
-  if (callback_->Render(base::Seconds(0), base::TimeTicks::Now(), 0,
+  if (callback_->Render(base::Seconds(0), base::TimeTicks::Now(), {},
                         bus_.get())) {
     task_runner_->PostTask(FROM_HERE, base::BindOnce(&TestObserver::Render,
                                                      base::Unretained(this)));
diff --git a/services/preferences/tracked/pref_hash_filter.cc b/services/preferences/tracked/pref_hash_filter.cc
index e6fc68d4..bcecae4d 100644
--- a/services/preferences/tracked/pref_hash_filter.cc
+++ b/services/preferences/tracked/pref_hash_filter.cc
@@ -298,8 +298,9 @@
   for (const auto item : *changed_paths_and_macs) {
     const std::string& changed_path = item.first;
 
-    if (const base::Value::Dict* split_values = item.second.GetIfDict()) {
-      for (const auto inner_item : *split_values) {
+    if (item.second.is_dict()) {
+      const base::Value::Dict& split_values = item.second.GetDict();
+      for (const auto inner_item : split_values) {
         const std::string* mac = inner_item.second.GetIfString();
         bool is_string = !!mac;
         DCHECK(is_string);
diff --git a/services/preferences/tracked/pref_hash_filter_unittest.cc b/services/preferences/tracked/pref_hash_filter_unittest.cc
index 5bd0c83..26eba40 100644
--- a/services/preferences/tracked/pref_hash_filter_unittest.cc
+++ b/services/preferences/tracked/pref_hash_filter_unittest.cc
@@ -402,7 +402,7 @@
   void SetSuperMac(const std::string& super_mac) override;
 
  private:
-  MockHashStoreContents(MockHashStoreContents* origin_mock);
+  explicit MockHashStoreContents(MockHashStoreContents* origin_mock);
 
   // Records calls to this mock's SetMac/SetSplitMac methods.
   void RecordSetMac(const std::string& path, const std::string& mac) {
@@ -447,8 +447,7 @@
     const std::string& split_path) const {
   const base::Value* out_value = dictionary_.FindKey(path);
   if (out_value) {
-    const base::DictionaryValue* value_as_dict;
-    EXPECT_TRUE(out_value->GetAsDictionary(&value_as_dict));
+    EXPECT_TRUE(out_value->is_dict());
 
     out_value = dictionary_.FindKey(split_path);
     if (out_value) {
diff --git a/services/preferences/tracked/tracked_split_preference.cc b/services/preferences/tracked/tracked_split_preference.cc
index ba5e45b..3340b53 100644
--- a/services/preferences/tracked/tracked_split_preference.cc
+++ b/services/preferences/tracked/tracked_split_preference.cc
@@ -37,11 +37,12 @@
 void TrackedSplitPreference::OnNewValue(
     const base::Value* value,
     PrefHashStoreTransaction* transaction) const {
-  const base::DictionaryValue* dict_value = NULL;
-  if (value && !value->GetAsDictionary(&dict_value)) {
+  if (value && !value->is_dict()) {
     NOTREACHED();
     return;
   }
+  const base::DictionaryValue* dict_value =
+      value ? &base::Value::AsDictionaryValue(*value) : nullptr;
   transaction->StoreSplitHash(pref_path_, dict_value);
 }
 
diff --git a/services/viz/public/cpp/compositing/filter_operation_mojom_traits.cc b/services/viz/public/cpp/compositing/filter_operation_mojom_traits.cc
index a82dc63..4a3bd8f 100644
--- a/services/viz/public/cpp/compositing/filter_operation_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/filter_operation_mojom_traits.cc
@@ -46,6 +46,8 @@
       return viz::mojom::FilterType::SATURATING_BRIGHTNESS;
     case cc::FilterOperation::ALPHA_THRESHOLD:
       return viz::mojom::FilterType::ALPHA_THRESHOLD;
+    case cc::FilterOperation::OFFSET:
+      return viz::mojom::FilterType::OFFSET;
   }
   NOTREACHED();
   return viz::mojom::FilterType::FILTER_TYPE_LAST;
@@ -84,6 +86,8 @@
       return cc::FilterOperation::SATURATING_BRIGHTNESS;
     case viz::mojom::FilterType::ALPHA_THRESHOLD:
       return cc::FilterOperation::ALPHA_THRESHOLD;
+    case viz::mojom::FilterType::OFFSET:
+      return cc::FilterOperation::OFFSET;
   }
   NOTREACHED();
   return cc::FilterOperation::FILTER_TYPE_LAST;
@@ -125,10 +129,10 @@
       out->set_amount(data.amount());
       gfx::Point offset;
       SkColor4f drop_shadow_color;
-      if (!data.ReadDropShadowOffset(&offset) ||
+      if (!data.ReadOffset(&offset) ||
           !data.ReadDropShadowColor(&drop_shadow_color))
         return false;
-      out->set_drop_shadow_offset(offset);
+      out->set_offset(offset);
       out->set_drop_shadow_color(drop_shadow_color);
       return true;
     }
@@ -166,6 +170,13 @@
       out->set_shape(shape);
       return true;
     }
+    case cc::FilterOperation::OFFSET: {
+      gfx::Point offset;
+      if (!data.ReadOffset(&offset))
+        return false;
+      out->set_offset(offset);
+      return true;
+    }
   }
   return false;
 }
diff --git a/services/viz/public/cpp/compositing/filter_operation_mojom_traits.h b/services/viz/public/cpp/compositing/filter_operation_mojom_traits.h
index 82989a8..8e94983 100644
--- a/services/viz/public/cpp/compositing/filter_operation_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/filter_operation_mojom_traits.h
@@ -36,10 +36,11 @@
     return operation.outer_threshold();
   }
 
-  static gfx::Point drop_shadow_offset(const cc::FilterOperation& operation) {
-    if (operation.type() != cc::FilterOperation::DROP_SHADOW)
+  static gfx::Point offset(const cc::FilterOperation& operation) {
+    if (operation.type() != cc::FilterOperation::DROP_SHADOW &&
+        operation.type() != cc::FilterOperation::OFFSET)
       return gfx::Point();
-    return operation.drop_shadow_offset();
+    return operation.offset();
   }
 
   static SkColor4f drop_shadow_color(const cc::FilterOperation& operation) {
diff --git a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
index d30c96b..acc8a99 100644
--- a/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
+++ b/services/viz/public/cpp/compositing/mojom_traits_unittest.cc
@@ -151,7 +151,7 @@
       break;
     case cc::FilterOperation::DROP_SHADOW:
       EXPECT_EQ(input.amount(), output.amount());
-      EXPECT_EQ(input.drop_shadow_offset(), output.drop_shadow_offset());
+      EXPECT_EQ(input.offset(), output.offset());
       EXPECT_EQ(input.drop_shadow_color(), output.drop_shadow_color());
       break;
     case cc::FilterOperation::COLOR_MATRIX:
@@ -171,6 +171,9 @@
     case cc::FilterOperation::ALPHA_THRESHOLD:
       NOTREACHED();
       break;
+    case cc::FilterOperation::OFFSET:
+      EXPECT_EQ(input.offset(), output.offset());
+      break;
   }
 }
 
diff --git a/services/viz/public/mojom/compositing/filter_operation.mojom b/services/viz/public/mojom/compositing/filter_operation.mojom
index cfcae13..4e3a90d4 100644
--- a/services/viz/public/mojom/compositing/filter_operation.mojom
+++ b/services/viz/public/mojom/compositing/filter_operation.mojom
@@ -25,7 +25,8 @@
   REFERENCE,
   SATURATING_BRIGHTNESS,
   ALPHA_THRESHOLD,
-  FILTER_TYPE_LAST = ALPHA_THRESHOLD
+  OFFSET,
+  FILTER_TYPE_LAST = OFFSET
 };
 
 // See cc/paint/filter_operation.h.
@@ -33,7 +34,7 @@
   FilterType type;
   float amount;
   float outer_threshold;
-  gfx.mojom.Point drop_shadow_offset;
+  gfx.mojom.Point offset;
   skia.mojom.SkColor4f drop_shadow_color;
   PaintFilter image_filter;
   array<float, 20>? matrix;
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 2a8eb26..5686e3b 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -5774,9 +5774,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5788,8 +5788,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -5945,9 +5945,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -5959,8 +5959,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -6097,9 +6097,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -6111,8 +6111,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 08c4f59..14fff1be 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -85321,9 +85321,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -85335,8 +85335,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -85462,9 +85462,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -85476,8 +85476,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
@@ -85589,9 +85589,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -85603,8 +85603,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
@@ -86937,9 +86937,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -86950,8 +86950,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -87108,9 +87108,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -87121,8 +87121,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -87260,9 +87260,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -87273,8 +87273,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -88798,9 +88798,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -88811,8 +88811,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -88969,9 +88969,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -88982,8 +88982,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -89121,9 +89121,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -89134,8 +89134,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -89907,9 +89907,9 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome"
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "merge": {
           "args": [],
           "script": "//testing/merge_scripts/standard_gtest_merge.py"
@@ -89920,8 +89920,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 5e2186d..d7020fd5 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -18656,12 +18656,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.filter;../../testing/buildbot/filters/linux-lacros.interactive_ui_tests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18673,8 +18673,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -18847,12 +18847,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -18864,8 +18864,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
@@ -19014,12 +19014,12 @@
       {
         "args": [
           "--test-launcher-filter-file=../../testing/buildbot/filters/linux-lacros.lacros_chrome_browsertests.skew.filter",
-          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome",
+          "--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome",
           "--test-launcher-print-test-stdio=always",
           "--combine-ash-logs-on-bots",
           "--asan-symbolize-output"
         ],
-        "description": "Run with ash-chrome version 110.0.5474.0",
+        "description": "Run with ash-chrome version 110.0.5475.0",
         "isolate_profile_data": true,
         "merge": {
           "args": [],
@@ -19031,8 +19031,8 @@
           "cipd_packages": [
             {
               "cipd_package": "chromium/testing/linux-ash-chromium/x86_64/ash.zip",
-              "location": "lacros_version_skew_tests_v110.0.5474.0",
-              "revision": "version:110.0.5474.0"
+              "location": "lacros_version_skew_tests_v110.0.5475.0",
+              "revision": "version:110.0.5475.0"
             }
           ],
           "dimension_sets": [
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl
index 5e6ba109..55d7ab1 100644
--- a/testing/buildbot/variants.pyl
+++ b/testing/buildbot/variants.pyl
@@ -22,16 +22,16 @@
   },
   'LACROS_VERSION_SKEW_CANARY': {
     'args': [
-      '--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5474.0/test_ash_chrome',
+      '--ash-chrome-path-override=../../lacros_version_skew_tests_v110.0.5475.0/test_ash_chrome',
     ],
-    'description': 'Run with ash-chrome version 110.0.5474.0',
+    'description': 'Run with ash-chrome version 110.0.5475.0',
     'identifier': 'Lacros version skew testing ash canary',
     'swarming': {
       'cipd_packages': [
         {
           'cipd_package': 'chromium/testing/linux-ash-chromium/x86_64/ash.zip',
-          'location': 'lacros_version_skew_tests_v110.0.5474.0',
-          'revision': 'version:110.0.5474.0',
+          'location': 'lacros_version_skew_tests_v110.0.5475.0',
+          'revision': 'version:110.0.5475.0',
         },
       ],
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 1a79b3a..3e1b6c5 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -2530,6 +2530,21 @@
             ]
         }
     ],
+    "ChromeOSRobustAudioDeviceSelectLogic": [
+        {
+            "platforms": [
+                "chromeos"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20221019",
+                    "enable_features": [
+                        "RobustAudioDeviceSelectLogic"
+                    ]
+                }
+            ]
+        }
+    ],
     "ChromeOSSharingHub": [
         {
             "platforms": [
@@ -7838,6 +7853,22 @@
             ]
         }
     ],
+    "OsFeedback": [
+        {
+            "platforms": [
+                "chromeos",
+                "chromeos_lacros"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_20221130",
+                    "enable_features": [
+                        "OsFeedback"
+                    ]
+                }
+            ]
+        }
+    ],
     "OutOfProcessSystemDnsResolution": [
         {
             "platforms": [
diff --git a/third_party/bidimapper/README.chromium b/third_party/bidimapper/README.chromium
index d7ffb56..49a1e1c 100644
--- a/third_party/bidimapper/README.chromium
+++ b/third_party/bidimapper/README.chromium
@@ -1,10 +1,10 @@
 Name: Implementation of WebDriver BiDi standard
 Short Name: chromium-bidi
-URL: https://github.com/GoogleChromeLabs/chromium-bidi/archive/70c17448776285b96363bc679c54f6b9550ec8fd.zip
+URL: https://github.com/GoogleChromeLabs/chromium-bidi/archive/f8d953fa7313c562f88471865a4fd18dc81f1715.zip
 Version: 0
-Date: 2022-12-05
-Revision: 70c17448776285b96363bc679c54f6b9550ec8fd
-SHA-512: 62b0f6f5e193b062b42ebd27009d86c6b834e0261646806973f4fc8fedec485d5405f9ed3fb46b8f66c56b9ad38eed36b18b4d4be5c3d32a6b00f1e36dbb8af8
+Date: 2022-12-13
+Revision: f8d953fa7313c562f88471865a4fd18dc81f1715
+SHA-512: 349bc196e97cef7d53d8817000ee898e8f6565d2602cbbacac0b2d15565d08536a4ff5bace2fd8ca03943ee2d1c25cba6b1ed357869ed0599a79db19e1208c6c
 License: Apache 2.0
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/bidimapper/mapper.js b/third_party/bidimapper/mapper.js
index e9b3bdab..b3b226a 100644
--- a/third_party/bidimapper/mapper.js
+++ b/third_party/bidimapper/mapper.js
@@ -1,4 +1,4 @@
-!function(){"use strict";var e,t,s="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n={},r={},a={},i={};e=i,Object.defineProperty(e,"__esModule",{value:!0}),e.log=e.LogType=void 0,(t=e.LogType||(e.LogType={})).system="System",t.bidi="BiDi Messages",t.browsingContexts="Browsing Contexts",t.cdp="CDP",t.commandParser="Command parser",e.log=function(e){return(...t)=>{console.log(e,...t),"MapperTabPage"in globalThis&&globalThis.MapperTabPage.log(e,...t)}};var o={};Object.defineProperty(o,"__esModule",{value:!0}),o.NoSuchFrameException=o.InvalidArgumentException=o.UnknownCommandException=o.UnknownException=o.ErrorResponseClass=void 0;class d{constructor(e,t,s){this.error=e,this.message=t,this.stacktrace=s}error;message;stacktrace;toErrorResponse(e){return{id:e,error:this.error,message:this.message,stacktrace:this.stacktrace}}}o.ErrorResponseClass=d;o.UnknownException=class extends d{constructor(e,t){super("unknown error",e,t)}};o.UnknownCommandException=class extends d{constructor(e,t){super("unknown command",e,t)}};o.InvalidArgumentException=class extends d{constructor(e,t){super("invalid argument",e,t)}};o.NoSuchFrameException=class extends d{constructor(e){super("no such frame",e)}};var c={},u={},l={};Object.defineProperty(l,"__esModule",{value:!0}),l.EventResponseClass=void 0;l.EventResponseClass=class{method;params;constructor(e,t){this.method=e,this.params=t}};var p={},h={},m={},g={},f={};!function(e){var t;Object.defineProperty(e,"__esModule",{value:!0}),e.getParsedType=e.ZodParsedType=e.util=void 0,function(e){e.assertEqual=e=>e,e.assertIs=function(e){},e.assertNever=function(e){throw new Error},e.arrayToEnum=e=>{const t={};for(const s of e)t[s]=s;return t},e.getValidEnumValues=t=>{const s=e.objectKeys(t).filter((e=>"number"!=typeof t[t[e]])),n={};for(const e of s)n[e]=t[e];return e.objectValues(n)},e.objectValues=t=>e.objectKeys(t).map((function(e){return t[e]})),e.objectKeys="function"==typeof Object.keys?e=>Object.keys(e):e=>{const t=[];for(const s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.push(s);return t},e.find=(e,t)=>{for(const s of e)if(t(s))return s},e.isInteger="function"==typeof Number.isInteger?e=>Number.isInteger(e):e=>"number"==typeof e&&isFinite(e)&&Math.floor(e)===e,e.joinValues=function(e,t=" | "){return e.map((e=>"string"==typeof e?`'${e}'`:e)).join(t)},e.jsonStringifyReplacer=(e,t)=>"bigint"==typeof t?t.toString():t}(t=e.util||(e.util={})),e.ZodParsedType=t.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]);e.getParsedType=t=>{switch(typeof t){case"undefined":return e.ZodParsedType.undefined;case"string":return e.ZodParsedType.string;case"number":return isNaN(t)?e.ZodParsedType.nan:e.ZodParsedType.number;case"boolean":return e.ZodParsedType.boolean;case"function":return e.ZodParsedType.function;case"bigint":return e.ZodParsedType.bigint;case"object":return Array.isArray(t)?e.ZodParsedType.array:null===t?e.ZodParsedType.null:t.then&&"function"==typeof t.then&&t.catch&&"function"==typeof t.catch?e.ZodParsedType.promise:"undefined"!=typeof Map&&t instanceof Map?e.ZodParsedType.map:"undefined"!=typeof Set&&t instanceof Set?e.ZodParsedType.set:"undefined"!=typeof Date&&t instanceof Date?e.ZodParsedType.date:e.ZodParsedType.object;default:return e.ZodParsedType.unknown}}}(f);var y={};Object.defineProperty(y,"__esModule",{value:!0}),y.ZodError=y.quotelessJson=y.ZodIssueCode=void 0;const v=f;y.ZodIssueCode=v.util.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of"]);y.quotelessJson=e=>JSON.stringify(e,null,2).replace(/"([^"]+)":/g,"$1:");class x extends Error{constructor(e){super(),this.issues=[],this.addIssue=e=>{this.issues=[...this.issues,e]},this.addIssues=(e=[])=>{this.issues=[...this.issues,...e]};const t=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,t):this.__proto__=t,this.name="ZodError",this.issues=e}get errors(){return this.issues}format(e){const t=e||function(e){return e.message},s={_errors:[]},n=e=>{for(const r of e.issues)if("invalid_union"===r.code)r.unionErrors.map(n);else if("invalid_return_type"===r.code)n(r.returnTypeError);else if("invalid_arguments"===r.code)n(r.argumentsError);else if(0===r.path.length)s._errors.push(t(r));else{let e=s,n=0;for(;n<r.path.length;){const s=r.path[n];n===r.path.length-1?(e[s]=e[s]||{_errors:[]},e[s]._errors.push(t(r))):e[s]=e[s]||{_errors:[]},e=e[s],n++}}};return n(this),s}toString(){return this.message}get message(){return JSON.stringify(this.issues,v.util.jsonStringifyReplacer,2)}get isEmpty(){return 0===this.issues.length}flatten(e=(e=>e.message)){const t={},s=[];for(const n of this.issues)n.path.length>0?(t[n.path[0]]=t[n.path[0]]||[],t[n.path[0]].push(e(n))):s.push(e(n));return{formErrors:s,fieldErrors:t}}get formErrors(){return this.flatten()}}y.ZodError=x,x.create=e=>new x(e),Object.defineProperty(g,"__esModule",{value:!0});const w=f,C=y;g.default=(e,t)=>{let s;switch(e.code){case C.ZodIssueCode.invalid_type:s=e.received===w.ZodParsedType.undefined?"Required":`Expected ${e.expected}, received ${e.received}`;break;case C.ZodIssueCode.invalid_literal:s=`Invalid literal value, expected ${JSON.stringify(e.expected,w.util.jsonStringifyReplacer)}`;break;case C.ZodIssueCode.unrecognized_keys:s=`Unrecognized key(s) in object: ${w.util.joinValues(e.keys,", ")}`;break;case C.ZodIssueCode.invalid_union:s="Invalid input";break;case C.ZodIssueCode.invalid_union_discriminator:s=`Invalid discriminator value. Expected ${w.util.joinValues(e.options)}`;break;case C.ZodIssueCode.invalid_enum_value:s=`Invalid enum value. Expected ${w.util.joinValues(e.options)}, received '${e.received}'`;break;case C.ZodIssueCode.invalid_arguments:s="Invalid function arguments";break;case C.ZodIssueCode.invalid_return_type:s="Invalid function return type";break;case C.ZodIssueCode.invalid_date:s="Invalid date";break;case C.ZodIssueCode.invalid_string:"object"==typeof e.validation?"startsWith"in e.validation?s=`Invalid input: must start with "${e.validation.startsWith}"`:"endsWith"in e.validation?s=`Invalid input: must end with "${e.validation.endsWith}"`:w.util.assertNever(e.validation):s="regex"!==e.validation?`Invalid ${e.validation}`:"Invalid";break;case C.ZodIssueCode.too_small:s="array"===e.type?`Array must contain ${e.inclusive?"at least":"more than"} ${e.minimum} element(s)`:"string"===e.type?`String must contain ${e.inclusive?"at least":"over"} ${e.minimum} character(s)`:"number"===e.type?`Number must be greater than ${e.inclusive?"or equal to ":""}${e.minimum}`:"date"===e.type?`Date must be greater than ${e.inclusive?"or equal to ":""}${new Date(e.minimum)}`:"Invalid input";break;case C.ZodIssueCode.too_big:s="array"===e.type?`Array must contain ${e.inclusive?"at most":"less than"} ${e.maximum} element(s)`:"string"===e.type?`String must contain ${e.inclusive?"at most":"under"} ${e.maximum} character(s)`:"number"===e.type?`Number must be less than ${e.inclusive?"or equal to ":""}${e.maximum}`:"date"===e.type?`Date must be smaller than ${e.inclusive?"or equal to ":""}${new Date(e.maximum)}`:"Invalid input";break;case C.ZodIssueCode.custom:s="Invalid input";break;case C.ZodIssueCode.invalid_intersection_types:s="Intersection results could not be merged";break;case C.ZodIssueCode.not_multiple_of:s=`Number must be a multiple of ${e.multipleOf}`;break;default:s=t.defaultError,w.util.assertNever(e)}return{message:s}};var b=s&&s.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(m,"__esModule",{value:!0}),m.getErrorMap=m.setErrorMap=m.defaultErrorMap=void 0;const _=b(g);m.defaultErrorMap=_.default;let I=_.default;m.setErrorMap=function(e){I=e},m.getErrorMap=function(){return I};var T={};!function(e){var t=s&&s.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(e,"__esModule",{value:!0}),e.isAsync=e.isValid=e.isDirty=e.isAborted=e.OK=e.DIRTY=e.INVALID=e.ParseStatus=e.addIssueToContext=e.EMPTY_PATH=e.makeIssue=void 0;const n=m,r=t(g);e.makeIssue=e=>{const{data:t,path:s,errorMaps:n,issueData:r}=e,a=[...s,...r.path||[]],i={...r,path:a};let o="";const d=n.filter((e=>!!e)).slice().reverse();for(const e of d)o=e(i,{data:t,defaultError:o}).message;return{...r,path:a,message:r.message||o}},e.EMPTY_PATH=[],e.addIssueToContext=function(t,s){const a=e.makeIssue({issueData:s,data:t.data,path:t.path,errorMaps:[t.common.contextualErrorMap,t.schemaErrorMap,n.getErrorMap(),r.default].filter((e=>!!e))});t.common.issues.push(a)};class a{constructor(){this.value="valid"}dirty(){"valid"===this.value&&(this.value="dirty")}abort(){"aborted"!==this.value&&(this.value="aborted")}static mergeArray(t,s){const n=[];for(const r of s){if("aborted"===r.status)return e.INVALID;"dirty"===r.status&&t.dirty(),n.push(r.value)}return{status:t.value,value:n}}static async mergeObjectAsync(e,t){const s=[];for(const e of t)s.push({key:await e.key,value:await e.value});return a.mergeObjectSync(e,s)}static mergeObjectSync(t,s){const n={};for(const r of s){const{key:s,value:a}=r;if("aborted"===s.status)return e.INVALID;if("aborted"===a.status)return e.INVALID;"dirty"===s.status&&t.dirty(),"dirty"===a.status&&t.dirty(),(void 0!==a.value||r.alwaysSet)&&(n[s.value]=a.value)}return{status:t.value,value:n}}}e.ParseStatus=a,e.INVALID=Object.freeze({status:"aborted"});e.DIRTY=e=>({status:"dirty",value:e});e.OK=e=>({status:"valid",value:e});e.isAborted=e=>"aborted"===e.status;e.isDirty=e=>"dirty"===e.status;e.isValid=e=>"valid"===e.status;e.isAsync=e=>void 0!==typeof Promise&&e instanceof Promise}(T);var P={};Object.defineProperty(P,"__esModule",{value:!0});var Z={},E={};!function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.errorUtil=void 0,function(e){e.errToObj=e=>"string"==typeof e?{message:e}:e||{},e.toString=e=>"string"==typeof e?e:null==e?void 0:e.message}(e.errorUtil||(e.errorUtil={}))}(E),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.function=e.enum=e.effect=e.discriminatedUnion=e.date=e.boolean=e.bigint=e.array=e.any=e.ZodFirstPartyTypeKind=e.late=e.ZodSchema=e.Schema=e.custom=e.ZodBranded=e.BRAND=e.ZodNaN=e.ZodDefault=e.ZodNullable=e.ZodOptional=e.ZodTransformer=e.ZodEffects=e.ZodPromise=e.ZodNativeEnum=e.ZodEnum=e.ZodLiteral=e.ZodLazy=e.ZodFunction=e.ZodSet=e.ZodMap=e.ZodRecord=e.ZodTuple=e.ZodIntersection=e.ZodDiscriminatedUnion=e.ZodUnion=e.ZodObject=e.objectUtil=e.ZodArray=e.ZodVoid=e.ZodNever=e.ZodUnknown=e.ZodAny=e.ZodNull=e.ZodUndefined=e.ZodDate=e.ZodBoolean=e.ZodBigInt=e.ZodNumber=e.ZodString=e.ZodType=void 0,e.NEVER=e.void=e.unknown=e.union=e.undefined=e.tuple=e.transformer=e.string=e.strictObject=e.set=e.record=e.promise=e.preprocess=e.ostring=e.optional=e.onumber=e.oboolean=e.object=e.number=e.nullable=e.null=e.never=e.nativeEnum=e.nan=e.map=e.literal=e.lazy=e.intersection=e.instanceof=void 0;const t=m,s=E,n=T,r=f,a=y;class i{constructor(e,t,s,n){this.parent=e,this.data=t,this._path=s,this._key=n}get path(){return this._path.concat(this._key)}}const o=(e,t)=>{if(n.isValid(t))return{success:!0,data:t.value};if(!e.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,error:new a.ZodError(e.common.issues)}};function d(e){if(!e)return{};const{errorMap:t,invalid_type_error:s,required_error:n,description:r}=e;if(t&&(s||n))throw new Error('Can\'t use "invalid_type_error" or "required_error" in conjunction with custom error map.');if(t)return{errorMap:t,description:r};return{errorMap:(e,t)=>"invalid_type"!==e.code?{message:t.defaultError}:void 0===t.data?{message:null!=n?n:t.defaultError}:{message:null!=s?s:t.defaultError},description:r}}class c{constructor(e){this.spa=this.safeParseAsync,this.superRefine=this._refinement,this._def=e,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.default=this.default.bind(this),this.describe=this.describe.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this)}get description(){return this._def.description}_getType(e){return r.getParsedType(e.data)}_getOrReturnCtx(e,t){return t||{common:e.parent.common,data:e.data,parsedType:r.getParsedType(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}_processInputParams(e){return{status:new n.ParseStatus,ctx:{common:e.parent.common,data:e.data,parsedType:r.getParsedType(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}}_parseSync(e){const t=this._parse(e);if(n.isAsync(t))throw new Error("Synchronous parse encountered promise.");return t}_parseAsync(e){const t=this._parse(e);return Promise.resolve(t)}parse(e,t){const s=this.safeParse(e,t);if(s.success)return s.data;throw s.error}safeParse(e,t){var s;const n={common:{issues:[],async:null!==(s=null==t?void 0:t.async)&&void 0!==s&&s,contextualErrorMap:null==t?void 0:t.errorMap},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:r.getParsedType(e)},a=this._parseSync({data:e,path:n.path,parent:n});return o(n,a)}async parseAsync(e,t){const s=await this.safeParseAsync(e,t);if(s.success)return s.data;throw s.error}async safeParseAsync(e,t){const s={common:{issues:[],contextualErrorMap:null==t?void 0:t.errorMap,async:!0},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:r.getParsedType(e)},a=this._parse({data:e,path:[],parent:s}),i=await(n.isAsync(a)?a:Promise.resolve(a));return o(s,i)}refine(e,t){const s=e=>"string"==typeof t||void 0===t?{message:t}:"function"==typeof t?t(e):t;return this._refinement(((t,n)=>{const r=e(t),i=()=>n.addIssue({code:a.ZodIssueCode.custom,...s(t)});return"undefined"!=typeof Promise&&r instanceof Promise?r.then((e=>!!e||(i(),!1))):!!r||(i(),!1)}))}refinement(e,t){return this._refinement(((s,n)=>!!e(s)||(n.addIssue("function"==typeof t?t(s,n):t),!1)))}_refinement(e){return new G({schema:this,typeName:se.ZodEffects,effect:{type:"refinement",refinement:e}})}optional(){return Q.create(this)}nullable(){return Y.create(this)}nullish(){return this.optional().nullable()}array(){return M.create(this)}promise(){return H.create(this)}or(e){return j.create([this,e])}and(e){return L.create(this,e)}transform(e){return new G({schema:this,typeName:se.ZodEffects,effect:{type:"transform",transform:e}})}default(e){return new X({innerType:this,defaultValue:"function"==typeof e?e:()=>e,typeName:se.ZodDefault})}brand(){return new te({typeName:se.ZodBranded,type:this,...d(void 0)})}describe(e){return new(0,this.constructor)({...this._def,description:e})}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}}e.ZodType=c,e.Schema=c,e.ZodSchema=c;const u=/^c[^\s-]{8,}$/i,l=/^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i,p=/^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;class h extends c{constructor(){super(...arguments),this._regex=(e,t,n)=>this.refinement((t=>e.test(t)),{validation:t,code:a.ZodIssueCode.invalid_string,...s.errorUtil.errToObj(n)}),this.nonempty=e=>this.min(1,s.errorUtil.errToObj(e)),this.trim=()=>new h({...this._def,checks:[...this._def.checks,{kind:"trim"}]})}_parse(e){if(this._getType(e)!==r.ZodParsedType.string){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.string,received:t.parsedType}),n.INVALID}const t=new n.ParseStatus;let s;for(const i of this._def.checks)if("min"===i.kind)e.data.length<i.value&&(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{code:a.ZodIssueCode.too_small,minimum:i.value,type:"string",inclusive:!0,message:i.message}),t.dirty());else if("max"===i.kind)e.data.length>i.value&&(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{code:a.ZodIssueCode.too_big,maximum:i.value,type:"string",inclusive:!0,message:i.message}),t.dirty());else if("email"===i.kind)p.test(e.data)||(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{validation:"email",code:a.ZodIssueCode.invalid_string,message:i.message}),t.dirty());else if("uuid"===i.kind)l.test(e.data)||(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{validation:"uuid",code:a.ZodIssueCode.invalid_string,message:i.message}),t.dirty());else if("cuid"===i.kind)u.test(e.data)||(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{validation:"cuid",code:a.ZodIssueCode.invalid_string,message:i.message}),t.dirty());else if("url"===i.kind)try{new URL(e.data)}catch(r){s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{validation:"url",code:a.ZodIssueCode.invalid_string,message:i.message}),t.dirty()}else if("regex"===i.kind){i.regex.lastIndex=0;i.regex.test(e.data)||(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{validation:"regex",code:a.ZodIssueCode.invalid_string,message:i.message}),t.dirty())}else"trim"===i.kind?e.data=e.data.trim():"startsWith"===i.kind?e.data.startsWith(i.value)||(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{code:a.ZodIssueCode.invalid_string,validation:{startsWith:i.value},message:i.message}),t.dirty()):"endsWith"===i.kind?e.data.endsWith(i.value)||(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{code:a.ZodIssueCode.invalid_string,validation:{endsWith:i.value},message:i.message}),t.dirty()):r.util.assertNever(i);return{status:t.value,value:e.data}}_addCheck(e){return new h({...this._def,checks:[...this._def.checks,e]})}email(e){return this._addCheck({kind:"email",...s.errorUtil.errToObj(e)})}url(e){return this._addCheck({kind:"url",...s.errorUtil.errToObj(e)})}uuid(e){return this._addCheck({kind:"uuid",...s.errorUtil.errToObj(e)})}cuid(e){return this._addCheck({kind:"cuid",...s.errorUtil.errToObj(e)})}regex(e,t){return this._addCheck({kind:"regex",regex:e,...s.errorUtil.errToObj(t)})}startsWith(e,t){return this._addCheck({kind:"startsWith",value:e,...s.errorUtil.errToObj(t)})}endsWith(e,t){return this._addCheck({kind:"endsWith",value:e,...s.errorUtil.errToObj(t)})}min(e,t){return this._addCheck({kind:"min",value:e,...s.errorUtil.errToObj(t)})}max(e,t){return this._addCheck({kind:"max",value:e,...s.errorUtil.errToObj(t)})}length(e,t){return this.min(e,t).max(e,t)}get isEmail(){return!!this._def.checks.find((e=>"email"===e.kind))}get isURL(){return!!this._def.checks.find((e=>"url"===e.kind))}get isUUID(){return!!this._def.checks.find((e=>"uuid"===e.kind))}get isCUID(){return!!this._def.checks.find((e=>"cuid"===e.kind))}get minLength(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxLength(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}}function g(e,t){const s=(e.toString().split(".")[1]||"").length,n=(t.toString().split(".")[1]||"").length,r=s>n?s:n;return parseInt(e.toFixed(r).replace(".",""))%parseInt(t.toFixed(r).replace(".",""))/Math.pow(10,r)}e.ZodString=h,h.create=e=>new h({checks:[],typeName:se.ZodString,...d(e)});class v extends c{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(e){if(this._getType(e)!==r.ZodParsedType.number){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.number,received:t.parsedType}),n.INVALID}let t;const s=new n.ParseStatus;for(const i of this._def.checks)if("int"===i.kind)r.util.isInteger(e.data)||(t=this._getOrReturnCtx(e,t),n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:"integer",received:"float",message:i.message}),s.dirty());else if("min"===i.kind){(i.inclusive?e.data<i.value:e.data<=i.value)&&(t=this._getOrReturnCtx(e,t),n.addIssueToContext(t,{code:a.ZodIssueCode.too_small,minimum:i.value,type:"number",inclusive:i.inclusive,message:i.message}),s.dirty())}else if("max"===i.kind){(i.inclusive?e.data>i.value:e.data>=i.value)&&(t=this._getOrReturnCtx(e,t),n.addIssueToContext(t,{code:a.ZodIssueCode.too_big,maximum:i.value,type:"number",inclusive:i.inclusive,message:i.message}),s.dirty())}else"multipleOf"===i.kind?0!==g(e.data,i.value)&&(t=this._getOrReturnCtx(e,t),n.addIssueToContext(t,{code:a.ZodIssueCode.not_multiple_of,multipleOf:i.value,message:i.message}),s.dirty()):r.util.assertNever(i);return{status:s.value,value:e.data}}gte(e,t){return this.setLimit("min",e,!0,s.errorUtil.toString(t))}gt(e,t){return this.setLimit("min",e,!1,s.errorUtil.toString(t))}lte(e,t){return this.setLimit("max",e,!0,s.errorUtil.toString(t))}lt(e,t){return this.setLimit("max",e,!1,s.errorUtil.toString(t))}setLimit(e,t,n,r){return new v({...this._def,checks:[...this._def.checks,{kind:e,value:t,inclusive:n,message:s.errorUtil.toString(r)}]})}_addCheck(e){return new v({...this._def,checks:[...this._def.checks,e]})}int(e){return this._addCheck({kind:"int",message:s.errorUtil.toString(e)})}positive(e){return this._addCheck({kind:"min",value:0,inclusive:!1,message:s.errorUtil.toString(e)})}negative(e){return this._addCheck({kind:"max",value:0,inclusive:!1,message:s.errorUtil.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:0,inclusive:!0,message:s.errorUtil.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:0,inclusive:!0,message:s.errorUtil.toString(e)})}multipleOf(e,t){return this._addCheck({kind:"multipleOf",value:e,message:s.errorUtil.toString(t)})}get minValue(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}get isInt(){return!!this._def.checks.find((e=>"int"===e.kind))}}e.ZodNumber=v,v.create=e=>new v({checks:[],typeName:se.ZodNumber,...d(e)});class x extends c{_parse(e){if(this._getType(e)!==r.ZodParsedType.bigint){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.bigint,received:t.parsedType}),n.INVALID}return n.OK(e.data)}}e.ZodBigInt=x,x.create=e=>new x({typeName:se.ZodBigInt,...d(e)});class w extends c{_parse(e){if(this._getType(e)!==r.ZodParsedType.boolean){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.boolean,received:t.parsedType}),n.INVALID}return n.OK(e.data)}}e.ZodBoolean=w,w.create=e=>new w({typeName:se.ZodBoolean,...d(e)});class C extends c{_parse(e){if(this._getType(e)!==r.ZodParsedType.date){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.date,received:t.parsedType}),n.INVALID}if(isNaN(e.data.getTime())){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_date}),n.INVALID}const t=new n.ParseStatus;let s;for(const i of this._def.checks)"min"===i.kind?e.data.getTime()<i.value&&(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{code:a.ZodIssueCode.too_small,message:i.message,inclusive:!0,minimum:i.value,type:"date"}),t.dirty()):"max"===i.kind?e.data.getTime()>i.value&&(s=this._getOrReturnCtx(e,s),n.addIssueToContext(s,{code:a.ZodIssueCode.too_big,message:i.message,inclusive:!0,maximum:i.value,type:"date"}),t.dirty()):r.util.assertNever(i);return{status:t.value,value:new Date(e.data.getTime())}}_addCheck(e){return new C({...this._def,checks:[...this._def.checks,e]})}min(e,t){return this._addCheck({kind:"min",value:e.getTime(),message:s.errorUtil.toString(t)})}max(e,t){return this._addCheck({kind:"max",value:e.getTime(),message:s.errorUtil.toString(t)})}get minDate(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return null!=e?new Date(e):null}get maxDate(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return null!=e?new Date(e):null}}e.ZodDate=C,C.create=e=>new C({checks:[],typeName:se.ZodDate,...d(e)});class b extends c{_parse(e){if(this._getType(e)!==r.ZodParsedType.undefined){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.undefined,received:t.parsedType}),n.INVALID}return n.OK(e.data)}}e.ZodUndefined=b,b.create=e=>new b({typeName:se.ZodUndefined,...d(e)});class _ extends c{_parse(e){if(this._getType(e)!==r.ZodParsedType.null){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.null,received:t.parsedType}),n.INVALID}return n.OK(e.data)}}e.ZodNull=_,_.create=e=>new _({typeName:se.ZodNull,...d(e)});class I extends c{constructor(){super(...arguments),this._any=!0}_parse(e){return n.OK(e.data)}}e.ZodAny=I,I.create=e=>new I({typeName:se.ZodAny,...d(e)});class P extends c{constructor(){super(...arguments),this._unknown=!0}_parse(e){return n.OK(e.data)}}e.ZodUnknown=P,P.create=e=>new P({typeName:se.ZodUnknown,...d(e)});class Z extends c{_parse(e){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.never,received:t.parsedType}),n.INVALID}}e.ZodNever=Z,Z.create=e=>new Z({typeName:se.ZodNever,...d(e)});class S extends c{_parse(e){if(this._getType(e)!==r.ZodParsedType.undefined){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.void,received:t.parsedType}),n.INVALID}return n.OK(e.data)}}e.ZodVoid=S,S.create=e=>new S({typeName:se.ZodVoid,...d(e)});class M extends c{_parse(e){const{ctx:t,status:s}=this._processInputParams(e),o=this._def;if(t.parsedType!==r.ZodParsedType.array)return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.array,received:t.parsedType}),n.INVALID;if(null!==o.minLength&&t.data.length<o.minLength.value&&(n.addIssueToContext(t,{code:a.ZodIssueCode.too_small,minimum:o.minLength.value,type:"array",inclusive:!0,message:o.minLength.message}),s.dirty()),null!==o.maxLength&&t.data.length>o.maxLength.value&&(n.addIssueToContext(t,{code:a.ZodIssueCode.too_big,maximum:o.maxLength.value,type:"array",inclusive:!0,message:o.maxLength.message}),s.dirty()),t.common.async)return Promise.all(t.data.map(((e,s)=>o.type._parseAsync(new i(t,e,t.path,s))))).then((e=>n.ParseStatus.mergeArray(s,e)));const d=t.data.map(((e,s)=>o.type._parseSync(new i(t,e,t.path,s))));return n.ParseStatus.mergeArray(s,d)}get element(){return this._def.type}min(e,t){return new M({...this._def,minLength:{value:e,message:s.errorUtil.toString(t)}})}max(e,t){return new M({...this._def,maxLength:{value:e,message:s.errorUtil.toString(t)}})}length(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}var O;e.ZodArray=M,M.create=(e,t)=>new M({type:e,minLength:null,maxLength:null,typeName:se.ZodArray,...d(t)}),function(e){e.mergeShapes=(e,t)=>({...e,...t})}(O=e.objectUtil||(e.objectUtil={}));const k=e=>t=>new N({...e,shape:()=>({...e.shape(),...t})});function D(e){if(e instanceof N){const t={};for(const s in e.shape){const n=e.shape[s];t[s]=Q.create(D(n))}return new N({...e._def,shape:()=>t})}return e instanceof M?M.create(D(e.element)):e instanceof Q?Q.create(D(e.unwrap())):e instanceof Y?Y.create(D(e.unwrap())):e instanceof A?A.create(e.items.map((e=>D(e)))):e}class N extends c{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=k(this._def),this.extend=k(this._def)}_getCached(){if(null!==this._cached)return this._cached;const e=this._def.shape(),t=r.util.objectKeys(e);return this._cached={shape:e,keys:t}}_parse(e){if(this._getType(e)!==r.ZodParsedType.object){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.object,received:t.parsedType}),n.INVALID}const{status:t,ctx:s}=this._processInputParams(e),{shape:o,keys:d}=this._getCached(),c=[];if(!(this._def.catchall instanceof Z&&"strip"===this._def.unknownKeys))for(const e in s.data)d.includes(e)||c.push(e);const u=[];for(const e of d){const t=o[e],n=s.data[e];u.push({key:{status:"valid",value:e},value:t._parse(new i(s,n,s.path,e)),alwaysSet:e in s.data})}if(this._def.catchall instanceof Z){const e=this._def.unknownKeys;if("passthrough"===e)for(const e of c)u.push({key:{status:"valid",value:e},value:{status:"valid",value:s.data[e]}});else if("strict"===e)c.length>0&&(n.addIssueToContext(s,{code:a.ZodIssueCode.unrecognized_keys,keys:c}),t.dirty());else if("strip"!==e)throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{const e=this._def.catchall;for(const t of c){const n=s.data[t];u.push({key:{status:"valid",value:t},value:e._parse(new i(s,n,s.path,t)),alwaysSet:t in s.data})}}return s.common.async?Promise.resolve().then((async()=>{const e=[];for(const t of u){const s=await t.key;e.push({key:s,value:await t.value,alwaysSet:t.alwaysSet})}return e})).then((e=>n.ParseStatus.mergeObjectSync(t,e))):n.ParseStatus.mergeObjectSync(t,u)}get shape(){return this._def.shape()}strict(e){return s.errorUtil.errToObj,new N({...this._def,unknownKeys:"strict",...void 0!==e?{errorMap:(t,n)=>{var r,a,i,o;const d=null!==(i=null===(a=(r=this._def).errorMap)||void 0===a?void 0:a.call(r,t,n).message)&&void 0!==i?i:n.defaultError;return"unrecognized_keys"===t.code?{message:null!==(o=s.errorUtil.errToObj(e).message)&&void 0!==o?o:d}:{message:d}}}:{}})}strip(){return new N({...this._def,unknownKeys:"strip"})}passthrough(){return new N({...this._def,unknownKeys:"passthrough"})}setKey(e,t){return this.augment({[e]:t})}merge(e){return new N({unknownKeys:e._def.unknownKeys,catchall:e._def.catchall,shape:()=>O.mergeShapes(this._def.shape(),e._def.shape()),typeName:se.ZodObject})}catchall(e){return new N({...this._def,catchall:e})}pick(e){const t={};return r.util.objectKeys(e).map((e=>{this.shape[e]&&(t[e]=this.shape[e])})),new N({...this._def,shape:()=>t})}omit(e){const t={};return r.util.objectKeys(this.shape).map((s=>{-1===r.util.objectKeys(e).indexOf(s)&&(t[s]=this.shape[s])})),new N({...this._def,shape:()=>t})}deepPartial(){return D(this)}partial(e){const t={};if(e)return r.util.objectKeys(this.shape).map((s=>{-1===r.util.objectKeys(e).indexOf(s)?t[s]=this.shape[s]:t[s]=this.shape[s].optional()})),new N({...this._def,shape:()=>t});for(const e in this.shape){const s=this.shape[e];t[e]=s.optional()}return new N({...this._def,shape:()=>t})}required(){const e={};for(const t in this.shape){let s=this.shape[t];for(;s instanceof Q;)s=s._def.innerType;e[t]=s}return new N({...this._def,shape:()=>e})}keyof(){return W(r.util.objectKeys(this.shape))}}e.ZodObject=N,N.create=(e,t)=>new N({shape:()=>e,unknownKeys:"strip",catchall:Z.create(),typeName:se.ZodObject,...d(t)}),N.strictCreate=(e,t)=>new N({shape:()=>e,unknownKeys:"strict",catchall:Z.create(),typeName:se.ZodObject,...d(t)}),N.lazycreate=(e,t)=>new N({shape:e,unknownKeys:"strip",catchall:Z.create(),typeName:se.ZodObject,...d(t)});class j extends c{_parse(e){const{ctx:t}=this._processInputParams(e),s=this._def.options;if(t.common.async)return Promise.all(s.map((async e=>{const s={...t,common:{...t.common,issues:[]},parent:null};return{result:await e._parseAsync({data:t.data,path:t.path,parent:s}),ctx:s}}))).then((function(e){for(const t of e)if("valid"===t.result.status)return t.result;for(const s of e)if("dirty"===s.result.status)return t.common.issues.push(...s.ctx.common.issues),s.result;const s=e.map((e=>new a.ZodError(e.ctx.common.issues)));return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_union,unionErrors:s}),n.INVALID}));{let e;const r=[];for(const n of s){const s={...t,common:{...t.common,issues:[]},parent:null},a=n._parseSync({data:t.data,path:t.path,parent:s});if("valid"===a.status)return a;"dirty"!==a.status||e||(e={result:a,ctx:s}),s.common.issues.length&&r.push(s.common.issues)}if(e)return t.common.issues.push(...e.ctx.common.issues),e.result;const i=r.map((e=>new a.ZodError(e)));return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_union,unionErrors:i}),n.INVALID}}get options(){return this._def.options}}e.ZodUnion=j,j.create=(e,t)=>new j({options:e,typeName:se.ZodUnion,...d(t)});class z extends c{_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==r.ZodParsedType.object)return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.object,received:t.parsedType}),n.INVALID;const s=this.discriminator,i=t.data[s],o=this.options.get(i);return o?t.common.async?o._parseAsync({data:t.data,path:t.path,parent:t}):o._parseSync({data:t.data,path:t.path,parent:t}):(n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_union_discriminator,options:this.validDiscriminatorValues,path:[s]}),n.INVALID)}get discriminator(){return this._def.discriminator}get validDiscriminatorValues(){return Array.from(this.options.keys())}get options(){return this._def.options}static create(e,t,s){const n=new Map;try{t.forEach((t=>{const s=t.shape[e].value;n.set(s,t)}))}catch(e){throw new Error("The discriminator value could not be extracted from all the provided schemas")}if(n.size!==t.length)throw new Error("Some of the discriminator values are not unique");return new z({typeName:se.ZodDiscriminatedUnion,discriminator:e,options:n,...d(s)})}}function R(e,t){const s=r.getParsedType(e),n=r.getParsedType(t);if(e===t)return{valid:!0,data:e};if(s===r.ZodParsedType.object&&n===r.ZodParsedType.object){const s=r.util.objectKeys(t),n=r.util.objectKeys(e).filter((e=>-1!==s.indexOf(e))),a={...e,...t};for(const s of n){const n=R(e[s],t[s]);if(!n.valid)return{valid:!1};a[s]=n.data}return{valid:!0,data:a}}if(s===r.ZodParsedType.array&&n===r.ZodParsedType.array){if(e.length!==t.length)return{valid:!1};const s=[];for(let n=0;n<e.length;n++){const r=R(e[n],t[n]);if(!r.valid)return{valid:!1};s.push(r.data)}return{valid:!0,data:s}}return s===r.ZodParsedType.date&&n===r.ZodParsedType.date&&+e==+t?{valid:!0,data:e}:{valid:!1}}e.ZodDiscriminatedUnion=z;class L extends c{_parse(e){const{status:t,ctx:s}=this._processInputParams(e),r=(e,r)=>{if(n.isAborted(e)||n.isAborted(r))return n.INVALID;const i=R(e.value,r.value);return i.valid?((n.isDirty(e)||n.isDirty(r))&&t.dirty(),{status:t.value,value:i.data}):(n.addIssueToContext(s,{code:a.ZodIssueCode.invalid_intersection_types}),n.INVALID)};return s.common.async?Promise.all([this._def.left._parseAsync({data:s.data,path:s.path,parent:s}),this._def.right._parseAsync({data:s.data,path:s.path,parent:s})]).then((([e,t])=>r(e,t))):r(this._def.left._parseSync({data:s.data,path:s.path,parent:s}),this._def.right._parseSync({data:s.data,path:s.path,parent:s}))}}e.ZodIntersection=L,L.create=(e,t,s)=>new L({left:e,right:t,typeName:se.ZodIntersection,...d(s)});class A extends c{_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==r.ZodParsedType.array)return n.addIssueToContext(s,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.array,received:s.parsedType}),n.INVALID;if(s.data.length<this._def.items.length)return n.addIssueToContext(s,{code:a.ZodIssueCode.too_small,minimum:this._def.items.length,inclusive:!0,type:"array"}),n.INVALID;!this._def.rest&&s.data.length>this._def.items.length&&(n.addIssueToContext(s,{code:a.ZodIssueCode.too_big,maximum:this._def.items.length,inclusive:!0,type:"array"}),t.dirty());const o=s.data.map(((e,t)=>{const n=this._def.items[t]||this._def.rest;return n?n._parse(new i(s,e,s.path,t)):null})).filter((e=>!!e));return s.common.async?Promise.all(o).then((e=>n.ParseStatus.mergeArray(t,e))):n.ParseStatus.mergeArray(t,o)}get items(){return this._def.items}rest(e){return new A({...this._def,rest:e})}}e.ZodTuple=A,A.create=(e,t)=>{if(!Array.isArray(e))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new A({items:e,typeName:se.ZodTuple,rest:null,...d(t)})};class B extends c{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==r.ZodParsedType.object)return n.addIssueToContext(s,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.object,received:s.parsedType}),n.INVALID;const o=[],d=this._def.keyType,c=this._def.valueType;for(const e in s.data)o.push({key:d._parse(new i(s,e,s.path,e)),value:c._parse(new i(s,s.data[e],s.path,e))});return s.common.async?n.ParseStatus.mergeObjectAsync(t,o):n.ParseStatus.mergeObjectSync(t,o)}get element(){return this._def.valueType}static create(e,t,s){return new B(t instanceof c?{keyType:e,valueType:t,typeName:se.ZodRecord,...d(s)}:{keyType:h.create(),valueType:e,typeName:se.ZodRecord,...d(t)})}}e.ZodRecord=B;class V extends c{_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==r.ZodParsedType.map)return n.addIssueToContext(s,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.map,received:s.parsedType}),n.INVALID;const o=this._def.keyType,d=this._def.valueType,c=[...s.data.entries()].map((([e,t],n)=>({key:o._parse(new i(s,e,s.path,[n,"key"])),value:d._parse(new i(s,t,s.path,[n,"value"]))})));if(s.common.async){const e=new Map;return Promise.resolve().then((async()=>{for(const s of c){const r=await s.key,a=await s.value;if("aborted"===r.status||"aborted"===a.status)return n.INVALID;"dirty"!==r.status&&"dirty"!==a.status||t.dirty(),e.set(r.value,a.value)}return{status:t.value,value:e}}))}{const e=new Map;for(const s of c){const r=s.key,a=s.value;if("aborted"===r.status||"aborted"===a.status)return n.INVALID;"dirty"!==r.status&&"dirty"!==a.status||t.dirty(),e.set(r.value,a.value)}return{status:t.value,value:e}}}}e.ZodMap=V,V.create=(e,t,s)=>new V({valueType:t,keyType:e,typeName:se.ZodMap,...d(s)});class U extends c{_parse(e){const{status:t,ctx:s}=this._processInputParams(e);if(s.parsedType!==r.ZodParsedType.set)return n.addIssueToContext(s,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.set,received:s.parsedType}),n.INVALID;const o=this._def;null!==o.minSize&&s.data.size<o.minSize.value&&(n.addIssueToContext(s,{code:a.ZodIssueCode.too_small,minimum:o.minSize.value,type:"set",inclusive:!0,message:o.minSize.message}),t.dirty()),null!==o.maxSize&&s.data.size>o.maxSize.value&&(n.addIssueToContext(s,{code:a.ZodIssueCode.too_big,maximum:o.maxSize.value,type:"set",inclusive:!0,message:o.maxSize.message}),t.dirty());const d=this._def.valueType;function c(e){const s=new Set;for(const r of e){if("aborted"===r.status)return n.INVALID;"dirty"===r.status&&t.dirty(),s.add(r.value)}return{status:t.value,value:s}}const u=[...s.data.values()].map(((e,t)=>d._parse(new i(s,e,s.path,t))));return s.common.async?Promise.all(u).then((e=>c(e))):c(u)}min(e,t){return new U({...this._def,minSize:{value:e,message:s.errorUtil.toString(t)}})}max(e,t){return new U({...this._def,maxSize:{value:e,message:s.errorUtil.toString(t)}})}size(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}e.ZodSet=U,U.create=(e,t)=>new U({valueType:e,minSize:null,maxSize:null,typeName:se.ZodSet,...d(t)});class F extends c{constructor(){super(...arguments),this.validate=this.implement}_parse(e){const{ctx:s}=this._processInputParams(e);if(s.parsedType!==r.ZodParsedType.function)return n.addIssueToContext(s,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.function,received:s.parsedType}),n.INVALID;function i(e,r){return n.makeIssue({data:e,path:s.path,errorMaps:[s.common.contextualErrorMap,s.schemaErrorMap,t.getErrorMap(),t.defaultErrorMap].filter((e=>!!e)),issueData:{code:a.ZodIssueCode.invalid_arguments,argumentsError:r}})}function o(e,r){return n.makeIssue({data:e,path:s.path,errorMaps:[s.common.contextualErrorMap,s.schemaErrorMap,t.getErrorMap(),t.defaultErrorMap].filter((e=>!!e)),issueData:{code:a.ZodIssueCode.invalid_return_type,returnTypeError:r}})}const d={errorMap:s.common.contextualErrorMap},c=s.data;return this._def.returns instanceof H?n.OK((async(...e)=>{const t=new a.ZodError([]),s=await this._def.args.parseAsync(e,d).catch((s=>{throw t.addIssue(i(e,s)),t})),n=await c(...s);return await this._def.returns._def.type.parseAsync(n,d).catch((e=>{throw t.addIssue(o(n,e)),t}))})):n.OK(((...e)=>{const t=this._def.args.safeParse(e,d);if(!t.success)throw new a.ZodError([i(e,t.error)]);const s=c(...t.data),n=this._def.returns.safeParse(s,d);if(!n.success)throw new a.ZodError([o(s,n.error)]);return n.data}))}parameters(){return this._def.args}returnType(){return this._def.returns}args(...e){return new F({...this._def,args:A.create(e).rest(P.create())})}returns(e){return new F({...this._def,returns:e})}implement(e){return this.parse(e)}strictImplement(e){return this.parse(e)}static create(e,t,s){return new F({args:e||A.create([]).rest(P.create()),returns:t||P.create(),typeName:se.ZodFunction,...d(s)})}}e.ZodFunction=F;class K extends c{get schema(){return this._def.getter()}_parse(e){const{ctx:t}=this._processInputParams(e);return this._def.getter()._parse({data:t.data,path:t.path,parent:t})}}e.ZodLazy=K,K.create=(e,t)=>new K({getter:e,typeName:se.ZodLazy,...d(t)});class $ extends c{_parse(e){if(e.data!==this._def.value){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_literal,expected:this._def.value}),n.INVALID}return{status:"valid",value:e.data}}get value(){return this._def.value}}function W(e,t){return new J({values:e,typeName:se.ZodEnum,...d(t)})}e.ZodLiteral=$,$.create=(e,t)=>new $({value:e,typeName:se.ZodLiteral,...d(t)});class J extends c{_parse(e){if("string"!=typeof e.data){const t=this._getOrReturnCtx(e),s=this._def.values;return n.addIssueToContext(t,{expected:r.util.joinValues(s),received:t.parsedType,code:a.ZodIssueCode.invalid_type}),n.INVALID}if(-1===this._def.values.indexOf(e.data)){const t=this._getOrReturnCtx(e),s=this._def.values;return n.addIssueToContext(t,{received:t.data,code:a.ZodIssueCode.invalid_enum_value,options:s}),n.INVALID}return n.OK(e.data)}get options(){return this._def.values}get enum(){const e={};for(const t of this._def.values)e[t]=t;return e}get Values(){const e={};for(const t of this._def.values)e[t]=t;return e}get Enum(){const e={};for(const t of this._def.values)e[t]=t;return e}}e.ZodEnum=J,J.create=W;class q extends c{_parse(e){const t=r.util.getValidEnumValues(this._def.values),s=this._getOrReturnCtx(e);if(s.parsedType!==r.ZodParsedType.string&&s.parsedType!==r.ZodParsedType.number){const e=r.util.objectValues(t);return n.addIssueToContext(s,{expected:r.util.joinValues(e),received:s.parsedType,code:a.ZodIssueCode.invalid_type}),n.INVALID}if(-1===t.indexOf(e.data)){const e=r.util.objectValues(t);return n.addIssueToContext(s,{received:s.data,code:a.ZodIssueCode.invalid_enum_value,options:e}),n.INVALID}return n.OK(e.data)}get enum(){return this._def.values}}e.ZodNativeEnum=q,q.create=(e,t)=>new q({values:e,typeName:se.ZodNativeEnum,...d(t)});class H extends c{_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==r.ZodParsedType.promise&&!1===t.common.async)return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.promise,received:t.parsedType}),n.INVALID;const s=t.parsedType===r.ZodParsedType.promise?t.data:Promise.resolve(t.data);return n.OK(s.then((e=>this._def.type.parseAsync(e,{path:t.path,errorMap:t.common.contextualErrorMap}))))}}e.ZodPromise=H,H.create=(e,t)=>new H({type:e,typeName:se.ZodPromise,...d(t)});class G extends c{innerType(){return this._def.schema}_parse(e){const{status:t,ctx:s}=this._processInputParams(e),a=this._def.effect||null;if("preprocess"===a.type){const e=a.transform(s.data);return s.common.async?Promise.resolve(e).then((e=>this._def.schema._parseAsync({data:e,path:s.path,parent:s}))):this._def.schema._parseSync({data:e,path:s.path,parent:s})}const i={addIssue:e=>{n.addIssueToContext(s,e),e.fatal?t.abort():t.dirty()},get path(){return s.path}};if(i.addIssue=i.addIssue.bind(i),"refinement"===a.type){const e=e=>{const t=a.refinement(e,i);if(s.common.async)return Promise.resolve(t);if(t instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return e};if(!1===s.common.async){const r=this._def.schema._parseSync({data:s.data,path:s.path,parent:s});return"aborted"===r.status?n.INVALID:("dirty"===r.status&&t.dirty(),e(r.value),{status:t.value,value:r.value})}return this._def.schema._parseAsync({data:s.data,path:s.path,parent:s}).then((s=>"aborted"===s.status?n.INVALID:("dirty"===s.status&&t.dirty(),e(s.value).then((()=>({status:t.value,value:s.value}))))))}if("transform"===a.type){if(!1===s.common.async){const e=this._def.schema._parseSync({data:s.data,path:s.path,parent:s});if(!n.isValid(e))return e;const r=a.transform(e.value,i);if(r instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:t.value,value:r}}return this._def.schema._parseAsync({data:s.data,path:s.path,parent:s}).then((e=>n.isValid(e)?Promise.resolve(a.transform(e.value,i)).then((e=>({status:t.value,value:e}))):e))}r.util.assertNever(a)}}e.ZodEffects=G,e.ZodTransformer=G,G.create=(e,t,s)=>new G({schema:e,typeName:se.ZodEffects,effect:t,...d(s)}),G.createWithPreprocess=(e,t,s)=>new G({schema:t,effect:{type:"preprocess",transform:e},typeName:se.ZodEffects,...d(s)});class Q extends c{_parse(e){return this._getType(e)===r.ZodParsedType.undefined?n.OK(void 0):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}e.ZodOptional=Q,Q.create=(e,t)=>new Q({innerType:e,typeName:se.ZodOptional,...d(t)});class Y extends c{_parse(e){return this._getType(e)===r.ZodParsedType.null?n.OK(null):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}e.ZodNullable=Y,Y.create=(e,t)=>new Y({innerType:e,typeName:se.ZodNullable,...d(t)});class X extends c{_parse(e){const{ctx:t}=this._processInputParams(e);let s=t.data;return t.parsedType===r.ZodParsedType.undefined&&(s=this._def.defaultValue()),this._def.innerType._parse({data:s,path:t.path,parent:t})}removeDefault(){return this._def.innerType}}e.ZodDefault=X,X.create=(e,t)=>new Q({innerType:e,typeName:se.ZodOptional,...d(t)});class ee extends c{_parse(e){if(this._getType(e)!==r.ZodParsedType.nan){const t=this._getOrReturnCtx(e);return n.addIssueToContext(t,{code:a.ZodIssueCode.invalid_type,expected:r.ZodParsedType.nan,received:t.parsedType}),n.INVALID}return{status:"valid",value:e.data}}}e.ZodNaN=ee,ee.create=e=>new ee({typeName:se.ZodNaN,...d(e)}),e.BRAND=Symbol("zod_brand");class te extends c{_parse(e){const{ctx:t}=this._processInputParams(e),s=t.data;return this._def.type._parse({data:s,path:t.path,parent:t})}unwrap(){return this._def.type}}e.ZodBranded=te;var se;e.custom=(e,t={},s)=>e?I.create().superRefine(((n,r)=>{if(!e(n)){const e="function"==typeof t?t(n):t,a="string"==typeof e?{message:e}:e;r.addIssue({code:"custom",...a,fatal:s})}})):I.create(),e.late={object:N.lazycreate},function(e){e.ZodString="ZodString",e.ZodNumber="ZodNumber",e.ZodNaN="ZodNaN",e.ZodBigInt="ZodBigInt",e.ZodBoolean="ZodBoolean",e.ZodDate="ZodDate",e.ZodUndefined="ZodUndefined",e.ZodNull="ZodNull",e.ZodAny="ZodAny",e.ZodUnknown="ZodUnknown",e.ZodNever="ZodNever",e.ZodVoid="ZodVoid",e.ZodArray="ZodArray",e.ZodObject="ZodObject",e.ZodUnion="ZodUnion",e.ZodDiscriminatedUnion="ZodDiscriminatedUnion",e.ZodIntersection="ZodIntersection",e.ZodTuple="ZodTuple",e.ZodRecord="ZodRecord",e.ZodMap="ZodMap",e.ZodSet="ZodSet",e.ZodFunction="ZodFunction",e.ZodLazy="ZodLazy",e.ZodLiteral="ZodLiteral",e.ZodEnum="ZodEnum",e.ZodEffects="ZodEffects",e.ZodNativeEnum="ZodNativeEnum",e.ZodOptional="ZodOptional",e.ZodNullable="ZodNullable",e.ZodDefault="ZodDefault",e.ZodPromise="ZodPromise",e.ZodBranded="ZodBranded"}(se=e.ZodFirstPartyTypeKind||(e.ZodFirstPartyTypeKind={}));e.instanceof=(t,s={message:`Input not instance of ${t.name}`})=>e.custom((e=>e instanceof t),s,!0);const ne=h.create;e.string=ne;const re=v.create;e.number=re;const ae=ee.create;e.nan=ae;const ie=x.create;e.bigint=ie;const oe=w.create;e.boolean=oe;const de=C.create;e.date=de;const ce=b.create;e.undefined=ce;const ue=_.create;e.null=ue;const le=I.create;e.any=le;const pe=P.create;e.unknown=pe;const he=Z.create;e.never=he;const me=S.create;e.void=me;const ge=M.create;e.array=ge;const fe=N.create;e.object=fe;const ye=N.strictCreate;e.strictObject=ye;const ve=j.create;e.union=ve;const xe=z.create;e.discriminatedUnion=xe;const we=L.create;e.intersection=we;const Ce=A.create;e.tuple=Ce;const be=B.create;e.record=be;const _e=V.create;e.map=_e;const Ie=U.create;e.set=Ie;const Te=F.create;e.function=Te;const Pe=K.create;e.lazy=Pe;const Ze=$.create;e.literal=Ze;const Ee=J.create;e.enum=Ee;const Se=q.create;e.nativeEnum=Se;const Me=H.create;e.promise=Me;const Oe=G.create;e.effect=Oe,e.transformer=Oe;const ke=Q.create;e.optional=ke;const De=Y.create;e.nullable=De;const Ne=G.createWithPreprocess;e.preprocess=Ne;e.ostring=()=>ne().optional();e.onumber=()=>re().optional();e.oboolean=()=>oe().optional(),e.NEVER=n.INVALID}(Z),function(e){var t=s&&s.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[s]}})}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),n=s&&s.__exportStar||function(e,s){for(var n in e)"default"===n||Object.prototype.hasOwnProperty.call(s,n)||t(s,e,n)};Object.defineProperty(e,"__esModule",{value:!0}),e.ZodParsedType=e.getParsedType=void 0,n(m,e),n(T,e),n(P,e);var r=f;Object.defineProperty(e,"getParsedType",{enumerable:!0,get:function(){return r.getParsedType}}),Object.defineProperty(e,"ZodParsedType",{enumerable:!0,get:function(){return r.ZodParsedType}}),n(Z,e),n(y,e)}(h),function(e){var t=s&&s.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[s]}})}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),n=s&&s.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),r=s&&s.__importStar||function(e){if(e&&e.__esModule)return e;var s={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&t(s,e,r);return n(s,e),s},a=s&&s.__exportStar||function(e,s){for(var n in e)"default"===n||Object.prototype.hasOwnProperty.call(s,n)||t(s,e,n)};Object.defineProperty(e,"__esModule",{value:!0}),e.z=void 0;const i=r(h);e.z=i,a(h,e),e.default=i}(p),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.Session=e.CDP=e.Log=e.BrowsingContext=e.Script=e.CommonDataTypes=void 0;const t=l,s=p,n=o,r=(0,i.log)(i.LogType.commandParser);function a(e,t){const s=t.safeParse(e);if(s.success)return s.data;r(`Command ${JSON.stringify(e)} parse failed: ${JSON.stringify(s)}.`);const a=s.error.errors.map((e=>`${e.message} in ${e.path.map((e=>JSON.stringify(e))).join("/")}.`)).join(" ");throw new n.InvalidArgumentException(a)}var d,c,u,h;!function(e){e.RemoteReferenceSchema=s.z.object({handle:s.z.string().min(1)});const t=s.z.object({type:s.z.literal("undefined")}),n=s.z.object({type:s.z.literal("null")}),r=s.z.object({type:s.z.literal("string"),value:s.z.string()}),a=s.z.enum(["NaN","-0","Infinity","+Infinity","-Infinity"]),i=s.z.object({type:s.z.literal("number"),value:s.z.union([a,s.z.number()])}),o=s.z.object({type:s.z.literal("boolean"),value:s.z.boolean()}),d=s.z.object({type:s.z.literal("bigint"),value:s.z.string()}),c=s.z.union([t,n,r,i,o,d]);e.LocalValueSchema=s.z.lazy((()=>s.z.union([c,p,h,g,f,y,v])));const u=s.z.union([e.RemoteReferenceSchema,e.LocalValueSchema]),l=s.z.array(u),p=s.z.lazy((()=>s.z.object({type:s.z.literal("array"),value:l}))),h=s.z.object({type:s.z.literal("date"),value:s.z.string().min(1)}),m=s.z.lazy((()=>s.z.tuple([s.z.union([s.z.string(),u]),u]))),g=s.z.object({type:s.z.literal("map"),value:s.z.array(m)}),f=s.z.object({type:s.z.literal("object"),value:s.z.array(m)}),y=s.z.lazy((()=>s.z.object({type:s.z.literal("regexp"),value:s.z.object({pattern:s.z.string(),flags:s.z.string().optional()})}))),v=s.z.lazy((()=>s.z.object({type:s.z.literal("set"),value:l})));e.BrowsingContextSchema=s.z.string()}(d=e.CommonDataTypes||(e.CommonDataTypes={})),function(e){const t=s.z.enum(["window","dedicated-worker","shared-worker","service-worker","worker","paint-worklet","audio-worklet","worklet"]),n=s.z.object({context:d.BrowsingContextSchema.optional(),type:t.optional()});e.parseGetRealmsParams=function(e){return a(e,n)};const r=s.z.object({context:d.BrowsingContextSchema,sandbox:s.z.string().optional()}),i=s.z.object({realm:s.z.string().min(1)}),o=s.z.union([i,r]),c=s.z.enum(["root","none"]),u=s.z.object({expression:s.z.string(),awaitPromise:s.z.boolean(),target:o,resultOwnership:c.optional()});e.parseEvaluateParams=function(e){return a(e,u)};const l=s.z.object({target:o,handles:s.z.array(s.z.string())});e.parseDisownParams=function(e){return a(e,l)};const p=s.z.union([d.RemoteReferenceSchema,d.LocalValueSchema]),h=s.z.object({functionDeclaration:s.z.string(),target:o,arguments:s.z.array(p).optional(),this:p.optional(),awaitPromise:s.z.boolean(),resultOwnership:c.optional()});e.parseCallFunctionParams=function(e){return a(e,h)}}(e.Script||(e.Script={})),function(e){const n=s.z.object({maxDepth:s.z.number().int().nonnegative().max(9007199254740991).optional(),root:d.BrowsingContextSchema.optional()});e.parseGetTreeParams=function(e){return a(e,n)};const r=s.z.enum(["none","interactive","complete"]),i=s.z.object({context:d.BrowsingContextSchema,url:s.z.string().url(),wait:r.optional()});e.parseNavigateParams=function(e){return a(e,i)};const o=s.z.object({type:s.z.enum(["tab","window"]),referenceContext:d.BrowsingContextSchema.optional()});e.parseCreateParams=function(e){return a(e,o)};const c=s.z.object({context:d.BrowsingContextSchema});e.parseCloseParams=function(e){return a(e,c)};class u extends t.EventResponseClass{static method="browsingContext.load";constructor(e){super(u.method,e)}}e.LoadEvent=u;class l extends t.EventResponseClass{static method="browsingContext.domContentLoaded";constructor(e){super(l.method,e)}}e.DomContentLoadedEvent=l;class p extends t.EventResponseClass{static method="browsingContext.contextCreated";constructor(e){super(p.method,e)}}e.ContextCreatedEvent=p;class h extends t.EventResponseClass{static method="browsingContext.contextDestroyed";constructor(e){super(h.method,e)}}e.ContextDestroyedEvent=h,e.EventNames=[u.method,l.method,p.method,h.method]}(c=e.BrowsingContext||(e.BrowsingContext={})),function(e){class s extends t.EventResponseClass{static method="log.entryAdded";constructor(e){super(s.method,e)}}e.LogEntryAddedEvent=s,e.EventNames=[s.method]}(u=e.Log||(e.Log={})),function(e){const n=s.z.object({cdpMethod:s.z.string(),cdpParams:s.z.object({}).passthrough(),cdpSession:s.z.string().optional()});e.parseSendCommandParams=function(e){return a(e,n)};const r=s.z.object({context:d.BrowsingContextSchema});e.parseGetSessionParams=function(e){return a(e,r)};class i extends t.EventResponseClass{static method="cdp.eventReceived";constructor(e){super(i.method,e)}}e.EventReceivedEvent=i,e.EventNames=[i.method]}(h=e.CDP||(e.CDP={})),function(e){const t=s.z.enum([...c.EventNames,...u.EventNames,...h.EventNames]),n=s.z.object({events:s.z.array(t),contexts:s.z.array(d.BrowsingContextSchema).optional()});e.parseSubscribeParams=function(e){return a(e,n)}}(e.Session||(e.Session={}))}(u);var S={};Object.defineProperty(S,"__esModule",{value:!0}),S.Deferred=void 0;class M{#e=()=>{};#t=()=>{};#s;#n=!1;get isFinished(){return this.#n}constructor(){this.#s=new Promise(((e,t)=>{this.#e=e,this.#t=t}))}then(e,t){return this.#s.then(e,t)}catch(e){return this.#s.catch(e)}resolve(e){this.#n=!0,this.#e(e)}reject(e){this.#n=!0,this.#t(e)}finally(e){return this.#s.finally(e)}[Symbol.toStringTag]="Promise"}S.Deferred=M;var O={},k={};Object.defineProperty(k,"__esModule",{value:!0}),k.getRemoteValuesText=k.logMessageFormatter=void 0;const D=["%s","%d","%i","%f","%o","%O","%c"];function N(e){return D.some((t=>e.includes(t)))}function j(e){let t="";const s=e[0].value.toString(),n=e.slice(1,void 0),r=s.split(new RegExp(D.map((e=>"("+e+")")).join("|"),"g"));for(const s of r)if(void 0!==s&&""!=s)if(N(s)){const r=n.shift();if(void 0===r)throw new Error('Less value is provided: "'+L(e,!1)+'"');"%s"===s?t+=R(r):"%d"===s||"%i"===s?["bigint","number","string"].includes(r.type)?t+=parseInt(r.value.toString(),10):t+="NaN":"%f"===s?["bigint","number","string"].includes(r.type)?t+=parseFloat(r.value.toString()):t+="NaN":t+=z(r)}else t+=s;if(n.length>0)throw new Error('More value is provided: "'+L(e,!1)+'"');return t}function z(e){if(!["array","bigint","date","number","object","string"].includes(e.type))return R(e);if("bigint"===e.type)return e.value.toString()+"n";if("number"===e.type)return e.value.toString();if(["date","string"].includes(e.type))return JSON.stringify(e.value);if("object"===e.type)return"{"+e.value.map((e=>`${JSON.stringify(e[0])}:${z(e[1])}`)).join(",")+"}";if("array"===e.type)return"["+e.value.map((e=>z(e))).join(",")+"]";throw Error("Invalid value type: "+e.toString())}function R(e){if(!e.hasOwnProperty("value"))return e.type;switch(e.type){case"string":case"number":case"boolean":case"bigint":return e.value;case"regexp":return`/${e.value.pattern}/${e.value.flags}`;case"date":return new Date(e.value).toString();case"object":return`Object(${e.value.length})`;case"array":return`Array(${e.value.length})`;case"map":return`Map(${e.value.length})`;case"set":return`Set(${e.value.length})`;case"node":return"node";default:return e.type}}function L(e,t){return 0==e.length?"":"string"===e[0].type&&N(e[0].value.toString())&&t?j(e):e.map((e=>R(e))).join(" ")}k.logMessageFormatter=j,k.getRemoteValuesText=L;var A={},B={};Object.defineProperty(B,"__esModule",{value:!0}),B.ScriptEvaluator=void 0;const V=o;class U{static#r=0;static#a=1;static#i=new Map;static async serializeCdpObject(e,t,s){const n=this.#o(e),r=await s.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((e=>e)),awaitPromise:!1,arguments:[n],generateWebDriverValue:!0,executionContextId:s.executionContextId});return await this.#d(r,s,t)}static#o(e){return void 0!==e.objectId?{objectId:e.objectId}:void 0!==e.unserializableValue?{unserializableValue:e.unserializableValue}:{value:e.value}}static async stringifyObject(e,t){return(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(e){return String(e)})),awaitPromise:!1,arguments:[e],returnByValue:!0,executionContextId:t.executionContextId})).result.value}static async callFunction(e,t,s,n,r,a){const i=`(...args)=>{ return _callFunction((\n${t}\n), args);\n      function _callFunction(f, args) {\n        const deserializedThis = args.shift();\n        const deserializedArgs = args;\n        return f.apply(deserializedThis, deserializedArgs);\n      }}`,o=[await this.#c(s,e)];let d;o.push(...await Promise.all(n.map((async t=>await this.#c(t,e)))));try{d=await e.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:i,awaitPromise:r,arguments:o,generateWebDriverValue:!0,executionContextId:e.executionContextId})}catch(e){if(-32e3===e.code&&["Could not find object with given id","Argument should belong to the same JavaScript world as target object"].includes(e.message))throw new V.InvalidArgumentException("Handle was not found.");throw e}return d.exceptionDetails?{exceptionDetails:await this.#u(d.exceptionDetails,this.#a,a,e),realm:e.realmId}:{result:await U.#d(d,e,a),realm:e.realmId}}static realmDestroyed(e){return Array.from(this.#i.entries()).filter((([,t])=>t===e.realmId)).map((([e])=>this.#i.delete(e)))}static async disown(e,t){if(U.#i.get(t)===e.realmId){try{await e.cdpClient.sendCommand("Runtime.releaseObject",{objectId:t})}catch(e){if(-32e3!==e.code||"Invalid remote object id"!==e.message)throw e}this.#i.delete(t)}}static async#u(e,t,s,n){const r=e.stackTrace?.callFrames.map((e=>({url:e.url,functionName:e.functionName,lineNumber:e.lineNumber-t,columnNumber:e.columnNumber}))),a=await this.serializeCdpObject(e.exception,s,n),i=await this.stringifyObject(e.exception,n);return{exception:a,columnNumber:e.columnNumber,lineNumber:e.lineNumber-t,stackTrace:{callFrames:r||[]},text:i||e.text}}static async#d(e,t,s){const n=e.result.webDriverValue;if(!e.result.objectId)return n;const r=e.result.objectId,a=n;return"root"===s?(a.handle=r,this.#i.set(r,t.realmId)):await t.cdpClient.sendCommand("Runtime.releaseObject",{objectId:r}),a}static async scriptEvaluate(e,t,s,n){let r=await e.cdpClient.sendCommand("Runtime.evaluate",{contextId:e.executionContextId,expression:t,awaitPromise:s,generateWebDriverValue:!0});return r.exceptionDetails?{exceptionDetails:await this.#u(r.exceptionDetails,this.#r,n,e),realm:e.realmId}:{result:await U.#d(r,e,n),realm:e.realmId}}static async#c(e,t){if("handle"in e)return{objectId:e.handle};switch(e.type){case"undefined":return{unserializableValue:"undefined"};case"null":return{unserializableValue:"null"};case"string":return{value:e.value};case"number":return"NaN"===e.value?{unserializableValue:"NaN"}:"-0"===e.value?{unserializableValue:"-0"}:"+Infinity"===e.value?{unserializableValue:"+Infinity"}:"Infinity"===e.value?{unserializableValue:"Infinity"}:"-Infinity"===e.value?{unserializableValue:"-Infinity"}:{value:e.value};case"boolean":return{value:!!e.value};case"bigint":return{unserializableValue:`BigInt(${JSON.stringify(e.value)})`};case"date":return{unserializableValue:`new Date(Date.parse(${JSON.stringify(e.value)}))`};case"regexp":return{unserializableValue:`new RegExp(${JSON.stringify(e.value.pattern)}, ${JSON.stringify(e.value.flags)})`};case"map":{const s=await this.#l(e.value,t);return{objectId:(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(...e){const t=new Map;for(let s=0;s<e.length;s+=2)t.set(e[s],e[s+1]);return t})),awaitPromise:!1,arguments:s,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"object":{const s=await this.#l(e.value,t);return{objectId:(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(...e){const t={};for(let s=0;s<e.length;s+=2){t[e[s]]=e[s+1]}return t})),awaitPromise:!1,arguments:s,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"array":{const s=await U.#p(e.value,t);return{objectId:(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(...e){return e})),awaitPromise:!1,arguments:s,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"set":{const s=await this.#p(e.value,t);return{objectId:(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(...e){return new Set(e)})),awaitPromise:!1,arguments:s,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}default:throw new Error(`Value ${JSON.stringify(e)} is not deserializable.`)}}static async#l(e,t){const s=[];for(let n of e){const e=n[0],r=n[1];let a,i;a="string"==typeof e?{value:e}:await this.#c(e,t),i=await this.#c(r,t),s.push(a),s.push(i)}return s}static async#p(e,t){const s=[];for(let n of e)s.push(await this.#c(n,t));return s}}B.ScriptEvaluator=U;var F={};Object.defineProperty(F,"__esModule",{value:!0}),F.BrowsingContextStorage=void 0;const K=o;class ${static#h=new Map;static getTopLevelContexts(){return Array.from($.#h.values()).filter((e=>null===e.parentId))}static removeContext(e){$.#h.delete(e)}static addContext(e){$.#h.set(e.contextId,e),null!==e.parentId&&$.getKnownContext(e.parentId).addChild(e)}static hasKnownContext(e){return $.#h.has(e)}static findContext(e){return $.#h.get(e)}static getKnownContext(e){const t=$.findContext(e);if(void 0===t)throw new K.NoSuchFrameException(`Context ${e} not found`);return t}}F.BrowsingContextStorage=$,function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.Realm=e.RealmType=void 0;const t=B,s=F,n=o;(e.RealmType||(e.RealmType={})).window="window";class r{static#m=new Map;static create(e,t,s,n,a,i,o,d){const c=new r(e,t,s,n,a,i,o,d);return r.#m.set(c.realmId,c),c}static findRealms(e={}){return Array.from(r.#m.values()).filter((t=>(void 0===e.realmId||e.realmId===t.realmId)&&((void 0===e.browsingContextId||e.browsingContextId===t.browsingContextId)&&((void 0===e.executionContextId||e.executionContextId===t.executionContextId)&&((void 0===e.type||e.type===t.type)&&((void 0===e.sandbox||e.sandbox===t.#g)&&(void 0===e.cdpSessionId||e.cdpSessionId===t.#f)))))))}static findRealm(e){const t=r.findRealms(e);if(1===t.length)return t[0]}static getRealm(e){const t=r.findRealm(e);if(void 0===t)throw new n.NoSuchFrameException(`Realm ${JSON.stringify(e)} not found`);return t}static clearBrowsingContext(e){r.findRealms({browsingContextId:e}).map((e=>e.delete()))}delete(){r.#m.delete(this.realmId),t.ScriptEvaluator.realmDestroyed(this)}#y;#v;#x;#w;#C;#g;#f;#b;constructor(e,t,s,n,r,a,i,o){this.#y=e,this.#v=t,this.#x=s,this.#g=a,this.#w=n,this.#C=r,this.#f=i,this.#b=o}toBiDi(){return{realm:this.realmId,origin:this.origin,type:this.type,context:this.browsingContextId,...void 0!==this.#g?{sandbox:this.#g}:{}}}get realmId(){return this.#y}get browsingContextId(){return this.#v}get executionContextId(){return this.#x}get origin(){return this.#w}get type(){return this.#C}get cdpClient(){return this.#b}async callFunction(e,n,r,a,i){const o=s.BrowsingContextStorage.getKnownContext(this.browsingContextId);return await o.awaitUnblocked(),{result:await t.ScriptEvaluator.callFunction(this,e,n,r,a,i)}}async scriptEvaluate(e,n,r){const a=s.BrowsingContextStorage.getKnownContext(this.browsingContextId);return await a.awaitUnblocked(),{result:await t.ScriptEvaluator.scriptEvaluate(this,e,n,r)}}async disown(e){await t.ScriptEvaluator.disown(this,e)}async serializeCdpObject(e,s){return await t.ScriptEvaluator.serializeCdpObject(e,s,this)}async stringifyObject(e){return t.ScriptEvaluator.stringifyObject(e,this)}}e.Realm=r}(A),Object.defineProperty(O,"__esModule",{value:!0}),O.LogManager=void 0;const W=u,J=k,q=A;class H{#b;#f;#_;constructor(e,t,s){this.#f=t,this.#b=e,this.#_=s}static create(e,t,s){const n=new H(e,t,s);return n.#I(),n}#I(){this.#T()}#T(){this.#P()}#P(){this.#b.on("Runtime.consoleAPICalled",(e=>{const t=q.Realm.findRealm({cdpSessionId:this.#f,executionContextId:e.executionContextId}),s=void 0===t?Promise.resolve(e.args):Promise.all(e.args.map((async e=>t.serializeCdpObject(e,"none"))));this.#_.registerPromiseEvent(s.then((s=>new W.Log.LogEntryAddedEvent({level:H.#Z(e.type),source:{realm:t?.realmId??"UNKNOWN",context:t?.browsingContextId??"UNKNOWN"},text:(0,J.getRemoteValuesText)(s,!0),timestamp:Math.round(e.timestamp),stackTrace:H.#E(e.stackTrace),type:"console",method:"warning"===e.type?"warn":e.type,args:s}))),t?.browsingContextId??"UNKNOWN",W.Log.LogEntryAddedEvent.method)})),this.#b.on("Runtime.exceptionThrown",(e=>{const t=q.Realm.findRealm({cdpSessionId:this.#f,executionContextId:e.exceptionDetails.executionContextId}),s=(async()=>e.exceptionDetails.exception?void 0===t?JSON.stringify(e.exceptionDetails.exception):await t.stringifyObject(e.exceptionDetails.exception):e.exceptionDetails.text)();this.#_.registerPromiseEvent(s.then((s=>new W.Log.LogEntryAddedEvent({level:"error",source:{realm:t?.realmId??"UNKNOWN",context:t?.browsingContextId??"UNKNOWN"},text:s,timestamp:Math.round(e.timestamp),stackTrace:H.#E(e.exceptionDetails.stackTrace),type:"javascript"}))),t?.browsingContextId??"UNKNOWN",W.Log.LogEntryAddedEvent.method)}))}static#Z(e){return["assert","error"].includes(e)?"error":["debug","trace"].includes(e)?"debug":["warn","warning"].includes(e)?"warn":"info"}static#E(e){const t=e?.callFrames.map((e=>({columnNumber:e.columnNumber,functionName:e.functionName,lineNumber:e.lineNumber,url:e.url})));return t?{callFrames:t}:void 0}}O.LogManager=H,Object.defineProperty(c,"__esModule",{value:!0}),c.BrowsingContextImpl=void 0;const G=u,Q=S,Y=o,X=O,ee=A,te=F;var se=G.BrowsingContext.LoadEvent;class ne{#S={documentInitialized:new Q.Deferred,targetUnblocked:new Q.Deferred,Page:{navigatedWithinDocument:new Q.Deferred,lifecycleEvent:{DOMContentLoaded:new Q.Deferred,load:new Q.Deferred}}};#M;#O;#k;#_;#D=new Map;#N="about:blank";#j=null;#f;#b;#z;get#R(){if(void 0===this.#z)throw new Error(`No default realm for browsing context ${this.#M}`);return this.#z}constructor(e,t,s,n,r,a){this.#M=e,this.#O=t,this.#b=s,this.#k=r,this.#_=a,this.#f=n,this.#L(),te.BrowsingContextStorage.addContext(this)}static async createFrameContext(e,t,s,n,r){const a=new ne(e,t,s,n,null,r);a.#S.targetUnblocked.resolve(),await r.registerEvent(new G.BrowsingContext.ContextCreatedEvent(a.serializeToBidiValue()),a.contextId)}static async createTargetContext(e,t,s,n,r,a){const i=new ne(e,t,s,n,r,a);i.#A(),await a.registerEvent(new G.BrowsingContext.ContextCreatedEvent(i.serializeToBidiValue()),i.contextId)}get cdpBrowserContextId(){return this.#k}convertFrameToTargetContext(e,t){this.#B(e,t),this.#A()}async delete(){if(await this.#V(),null!==this.parentId){te.BrowsingContextStorage.getKnownContext(this.parentId).#D.delete(this.contextId)}await this.#_.registerEvent(new G.BrowsingContext.ContextDestroyedEvent(this.serializeToBidiValue()),this.contextId),te.BrowsingContextStorage.removeContext(this.contextId)}async#V(){await Promise.all(this.children.map((e=>e.delete())))}#B(e,t){this.#S.targetUnblocked.isFinished||this.#S.targetUnblocked.reject("OOPiF"),this.#S.targetUnblocked=new Q.Deferred,this.#b=e,this.#f=t,this.#L()}async#A(){X.LogManager.create(this.#b,this.#f,this.#_),await this.#b.sendCommand("Runtime.enable"),await this.#b.sendCommand("Page.enable"),await this.#b.sendCommand("Page.setLifecycleEventsEnabled",{enabled:!0}),await this.#b.sendCommand("Target.setAutoAttach",{autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),await this.#b.sendCommand("Runtime.runIfWaitingForDebugger"),this.#S.targetUnblocked.resolve()}get contextId(){return this.#M}get parentId(){return this.#O}get cdpSessionId(){return this.#f}get children(){return Array.from(this.#D.values())}get url(){return this.#N}addChild(e){this.#D.set(e.contextId,e)}async awaitLoaded(){await this.#S.Page.lifecycleEvent.load}async awaitUnblocked(){await this.#S.targetUnblocked}serializeToBidiValue(e=0,t=!0){return{context:this.#M,url:this.url,children:e>0?this.children.map((t=>t.serializeToBidiValue(e-1,!1))):null,...t?{parent:this.#O}:{}}}#L(){this.#b.on("Target.targetInfoChanged",(e=>{this.contextId===e.targetInfo.targetId&&(this.#N=e.targetInfo.url)})),this.#b.on("Page.frameNavigated",(async e=>{this.contextId===e.frame.id&&(this.#N=e.frame.url+(e.frame.urlFragment??""),await this.#V(),ee.Realm.clearBrowsingContext(this.contextId))})),this.#b.on("Page.navigatedWithinDocument",(e=>{this.contextId===e.frameId&&(this.#N=e.url,this.#S.Page.navigatedWithinDocument.resolve(e))})),this.#b.on("Page.lifecycleEvent",(async e=>{if(this.contextId===e.frameId)if("init"===e.name&&(this.#U(e.loaderId),this.#S.documentInitialized.resolve()),"commit"!==e.name){if(e.loaderId===this.#j)switch(e.name){case"DOMContentLoaded":this.#S.Page.lifecycleEvent.DOMContentLoaded.resolve(e),await this.#_.registerEvent(new G.BrowsingContext.DomContentLoadedEvent({context:this.contextId,navigation:this.#j,url:this.#N}),this.contextId);break;case"load":this.#S.Page.lifecycleEvent.load.resolve(e),await this.#_.registerEvent(new se({context:this.contextId,navigation:this.#j,url:this.#N}),this.contextId)}}else this.#j=e.loaderId})),this.#b.on("Runtime.executionContextCreated",(e=>{if(e.context.auxData.frameId!==this.contextId)return;if(!["default","isolated"].includes(e.context.auxData.type))return;const t=ee.Realm.create(e.context.uniqueId,this.contextId,e.context.id,this.#F(e),ee.RealmType.window,"isolated"===e.context.auxData.type?e.context.name:void 0,this.#f,this.#b);e.context.auxData.isDefault&&(this.#z=t)})),this.#b.on("Runtime.executionContextDestroyed",(e=>{ee.Realm.findRealms({cdpSessionId:this.#f,executionContextId:e.executionContextId}).map((e=>e.delete()))}))}#F(e){return"isolated"===e.context.auxData.type?this.#R.origin:["://",""].includes(e.context.origin)?"null":e.context.origin}#U(e){this.#j!==e&&(this.#S.documentInitialized.isFinished||this.#S.documentInitialized.reject("Document changed"),this.#S.documentInitialized=new Q.Deferred,this.#S.Page.navigatedWithinDocument.isFinished||this.#S.Page.navigatedWithinDocument.reject("Document changed"),this.#S.Page.navigatedWithinDocument=new Q.Deferred,this.#S.Page.lifecycleEvent.DOMContentLoaded.isFinished||this.#S.Page.lifecycleEvent.DOMContentLoaded.reject("Document changed"),this.#S.Page.lifecycleEvent.DOMContentLoaded=new Q.Deferred,this.#S.Page.lifecycleEvent.load.isFinished||this.#S.Page.lifecycleEvent.load.reject("Document changed"),this.#S.Page.lifecycleEvent.load=new Q.Deferred,this.#j=e)}async navigate(e,t){await this.#S.targetUnblocked;const s=await this.#b.sendCommand("Page.navigate",{url:e,frameId:this.contextId});if(s.errorText)throw new Y.UnknownException(s.errorText);switch(void 0!==s.loaderId&&s.loaderId!==this.#j&&this.#U(s.loaderId),t){case"none":break;case"interactive":void 0===s.loaderId?await this.#S.Page.navigatedWithinDocument:await this.#S.Page.lifecycleEvent.DOMContentLoaded;break;case"complete":void 0===s.loaderId?await this.#S.Page.navigatedWithinDocument:await this.#S.Page.lifecycleEvent.load;break;default:throw new Error(`Not implemented wait '${t}'`)}return{result:{navigation:s.loaderId||null,url:e}}}async getOrCreateSandbox(e){if(void 0===e||""===e)return this.#R;let t=ee.Realm.findRealms({browsingContextId:this.contextId,sandbox:e});if(0==t.length&&(await this.#b.sendCommand("Page.createIsolatedWorld",{frameId:this.contextId,worldName:e}),t=ee.Realm.findRealms({browsingContextId:this.contextId,sandbox:e})),1!==t.length)throw Error(`Sandbox ${e} wasn't created.`);return t[0]}}c.BrowsingContextImpl=ne,Object.defineProperty(a,"__esModule",{value:!0}),a.BrowsingContextProcessor=void 0;const re=o,ae=c,ie=A,oe=F,de=(0,i.log)(i.LogType.browsingContexts);class ce{sessions=new Set;#K;#$;#_;constructor(e,t,s){this.#K=e,this.#$=t,this.#_=s,this.#W(this.#K.browserClient())}#W(e){this.#J(e)}#J(e){e.on("Target.attachedToTarget",(async t=>{await this.#q(t,e)})),e.on("Target.detachedFromTarget",(async e=>{await ce.#H(e)}))}#G(e){if(this.sessions.has(e))return;this.sessions.add(e);const t=this.#K.getCdpClient(e);this.#J(t),t.on("*",(async(t,s)=>{await this.#_.registerEvent({method:"cdp.eventReceived",params:{cdpMethod:t,cdpParams:s||{},cdpSession:e}},null)})),t.on("Page.frameAttached",(async s=>{await ae.BrowsingContextImpl.createFrameContext(s.frameId,s.parentFrameId,t,e,this.#_)}))}async#q(e,t){const{sessionId:s,targetInfo:n}=e;let r=this.#K.getCdpClient(s);if(!this.#Q(n))return await r.sendCommand("Runtime.runIfWaitingForDebugger"),void await t.sendCommand("Target.detachFromTarget",e);de("AttachedToTarget event received: "+JSON.stringify(e)),this.#G(s),oe.BrowsingContextStorage.hasKnownContext(n.targetId)?oe.BrowsingContextStorage.getKnownContext(n.targetId).convertFrameToTargetContext(r,s):await ae.BrowsingContextImpl.createTargetContext(n.targetId,null,r,s,e.targetInfo.browserContextId??null,this.#_)}static async#H(e){const t=e.targetId;await(oe.BrowsingContextStorage.findContext(t)?.delete())}async process_browsingContext_getTree(e){return{result:{contexts:(void 0===e.root?oe.BrowsingContextStorage.getTopLevelContexts():[oe.BrowsingContextStorage.getKnownContext(e.root)]).map((t=>t.serializeToBidiValue(e.maxDepth??Number.MAX_VALUE)))}}}async process_browsingContext_create(e){const t=this.#K.browserClient();let s;if(void 0!==e.referenceContext&&(s=oe.BrowsingContextStorage.getKnownContext(e.referenceContext),null!==s.parentId))throw new re.InvalidArgumentException("referenceContext should be a top-level context");const n=(await t.sendCommand("Target.createTarget",{url:"about:blank",newWindow:"window"===e.type,...s?.cdpBrowserContextId?{browserContextId:s.cdpBrowserContextId}:{}})).targetId,r=oe.BrowsingContextStorage.getKnownContext(n);return await r.awaitLoaded(),{result:r.serializeToBidiValue(1)}}async process_browsingContext_navigate(e){const t=oe.BrowsingContextStorage.getKnownContext(e.context);return await t.navigate(e.url,void 0!==e.wait?e.wait:"none")}static async#Y(e){if("realm"in e)return ie.Realm.getRealm({realmId:e.realm});const t=oe.BrowsingContextStorage.getKnownContext(e.context);return await t.getOrCreateSandbox(e.sandbox)}async process_script_evaluate(e){const t=await ce.#Y(e.target);return await t.scriptEvaluate(e.expression,e.awaitPromise,e.resultOwnership??"none")}process_script_getRealms(e){void 0!==e.context&&oe.BrowsingContextStorage.getKnownContext(e.context);const t=ie.Realm.findRealms({browsingContextId:e.context,type:e.type}).map((e=>e.toBiDi()));return{result:{realms:t}}}async process_script_callFunction(e){const t=await ce.#Y(e.target);return await t.callFunction(e.functionDeclaration,e.this||{type:"undefined"},e.arguments||[],e.awaitPromise,e.resultOwnership??"none")}async process_script_disown(e){const t=await ce.#Y(e.target);return await Promise.all(e.handles.map((async e=>await t.disown(e)))),{result:{}}}async process_browsingContext_close(e){const t=this.#K.browserClient();if(null!==oe.BrowsingContextStorage.getKnownContext(e.context).parentId)throw new re.InvalidArgumentException("Not a top-level browsing context cannot be closed.");const s=new Promise((async s=>{const n=r=>{r.targetId===e.context&&(t.off("Target.detachedFromTarget",n),s())};t.on("Target.detachedFromTarget",n)}));return await this.#K.browserClient().sendCommand("Target.closeTarget",{targetId:e.context}),await s,{result:{}}}#Q(e){return e.targetId!==this.#$&&["page","iframe"].includes(e.type)}async process_cdp_sendCommand(e){const t=e.cdpSession?this.#K.getCdpClient(e.cdpSession):this.#K.browserClient();return{result:await t.sendCommand(e.cdpMethod,e.cdpParams),cdpSession:e.cdpSession}}async process_cdp_getSession(e){const t=e.context,s=oe.BrowsingContextStorage.getKnownContext(t).cdpSessionId;return void 0===s?{result:{cdpSession:null}}:{result:{cdpSession:s}}}}a.BrowsingContextProcessor=ce;var ue={},le={},pe=s&&s.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(le,"__esModule",{value:!0}),le.EventEmitter=void 0;const he=pe((function(e){return{all:e=e||new Map,on:function(t,s){var n=e.get(t);n?n.push(s):e.set(t,[s])},off:function(t,s){var n=e.get(t);n&&(s?n.splice(n.indexOf(s)>>>0,1):e.set(t,[]))},emit:function(t,s){var n=e.get(t);n&&n.slice().map((function(e){e(s)})),(n=e.get("*"))&&n.slice().map((function(e){e(t,s)}))}}}));le.EventEmitter=class{#X=(0,he.default)();on(e,t){return this.#X.on(e,t),this}once(e,t){const s=n=>{t(n),this.off(e,s)};return this.on(e,s)}off(e,t){return this.#X.off(e,t),this}emit(e,t){this.#X.emit(e,t)}};var me={};Object.defineProperty(me,"__esModule",{value:!0}),me.ProcessingQueue=void 0;const ge=(0,i.log)(i.LogType.system);me.ProcessingQueue=class{#ee=[];#te;#se;#ne=!1;constructor(e,t=(()=>Promise.resolve())){this.#se=t,this.#te=e}add(e){this.#ee.push(e),this.#re()}async#re(){if(!this.#ne){for(this.#ne=!0;this.#ee.length>0;){const e=this.#ee.shift();void 0!==e&&await e.then((e=>this.#te(e))).catch((e=>{ge("Event was not processed:"+e),this.#se(e)})).finally()}this.#ne=!1}}},Object.defineProperty(ue,"__esModule",{value:!0}),ue.BidiServer=ue.BiDiMessageEntry=void 0;const fe=le,ye=me,ve=(0,i.log)(i.LogType.bidi);class xe{#ae;#ie;constructor(e,t){this.#ae=e,this.#ie=t}static createFromPromise(e,t){return e.then((e=>new xe(e,t)))}static createResolved(e,t){return Promise.resolve(new xe(e,t))}get message(){return this.#ae}get channel(){return this.#ie}}ue.BiDiMessageEntry=xe;class we extends fe.EventEmitter{_transport;#oe;constructor(e){super(),this._transport=e,this.#oe=new ye.ProcessingQueue((e=>this.#de(e))),this._transport.setOnMessage(this.#ce)}async#de(e){const t=e.message;null!==e.channel&&(t.channel=e.channel);const s=JSON.stringify(t);ve("sent > "+s),await this._transport.sendMessage(s)}sendMessage(e){this.#oe.add(e)}close(){this._transport.close()}#ce=async e=>{let t;ve("received < "+e);try{t=we.#ue(e)}catch(t){return void this.#le(e,"invalid argument",t.message,null)}this.emit("message",t)};#le(e,t,s,n){const r=we.#pe(e,t,s);this.sendMessage(xe.createResolved(r,n))}static#he(e){return null===e?"null":Array.isArray(e)?"array":typeof e}static#pe(e,t,s){let n;try{const t=JSON.parse(e);"object"===we.#he(t)&&"id"in t&&(n=t.id)}catch{}return{id:n,error:t,message:s}}static#ue(e){let t;try{t=JSON.parse(e)}catch{throw new Error("Cannot parse data as JSON")}const s=we.#he(t);if("object"!==s)throw new Error(`Expected JSON object but got ${s}`);const{id:n,method:r,params:a}=t,i=we.#he(n);if("number"!==i||!Number.isInteger(n)||n<0)throw new Error(`Expected unsigned integer but got ${i}`);const o=we.#he(r);if("string"!==o)throw new Error(`Expected string method but got ${o}`);const d=we.#he(a);if("object"!==d)throw new Error(`Expected object params but got ${d}`);let c=t.channel;if(void 0!==c){const e=we.#he(c);if("string"!==e)throw new Error(`Expected string channel but got ${e}`);""===c&&(c=void 0)}return{id:n,method:r,params:a,channel:c}}}ue.BidiServer=we,Object.defineProperty(r,"__esModule",{value:!0}),r.CommandProcessor=void 0;const Ce=a,be=u,_e=ue,Ie=o,Te=F;class Pe{#me;#ge;#_;#K;static async run(e,t,s,n){const r=new Pe(e,t,s,n);await r.#fe()}constructor(e,t,s,n){this.#_=s,this.#ge=t,this.#K=e,this.#me=new Ce.BrowsingContextProcessor(e,n,s)}async#ye(){const e=this.#K.browserClient();await e.sendCommand("Target.setDiscoverTargets",{discover:!0}),await e.sendCommand("Target.setAutoAttach",{autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),await Promise.all(Te.BrowsingContextStorage.getTopLevelContexts().map((e=>e.awaitLoaded())))}async#fe(){this.#ge.on("message",(e=>this.#ce(e))),await this.#ye()}async#ve(){return{result:{ready:!1,message:"already connected"}}}async#xe(e,t){return await this.#_.subscribe(e.events,e.contexts??[null],t),{result:{}}}async#we(e,t){return await this.#_.unsubscribe(e.events,e.contexts??[null],t),{result:{}}}async#Ce(e){switch(e.method){case"session.status":return await this.#ve();case"session.subscribe":return await this.#xe(be.Session.parseSubscribeParams(e.params),e.channel??null);case"session.unsubscribe":return await this.#we(be.Session.parseSubscribeParams(e.params),e.channel??null);case"browsingContext.create":return await this.#me.process_browsingContext_create(be.BrowsingContext.parseCreateParams(e.params));case"browsingContext.close":return await this.#me.process_browsingContext_close(be.BrowsingContext.parseCloseParams(e.params));case"browsingContext.getTree":return await this.#me.process_browsingContext_getTree(be.BrowsingContext.parseGetTreeParams(e.params));case"browsingContext.navigate":return await this.#me.process_browsingContext_navigate(be.BrowsingContext.parseNavigateParams(e.params));case"script.getRealms":return this.#me.process_script_getRealms(be.Script.parseGetRealmsParams(e.params));case"script.callFunction":return await this.#me.process_script_callFunction(be.Script.parseCallFunctionParams(e.params));case"script.evaluate":return await this.#me.process_script_evaluate(be.Script.parseEvaluateParams(e.params));case"script.disown":return await this.#me.process_script_disown(be.Script.parseDisownParams(e.params));case"cdp.sendCommand":return await this.#me.process_cdp_sendCommand(be.CDP.parseSendCommandParams(e.params));case"cdp.getSession":return await this.#me.process_cdp_getSession(be.CDP.parseGetSessionParams(e.params));default:throw new Ie.UnknownCommandException(`Unknown command '${e.method}'.`)}}#ce=async e=>{try{const t=await this.#Ce(e),s={id:e.id,...t};this.#ge.sendMessage(_e.BiDiMessageEntry.createResolved(s,e.channel??null))}catch(t){if(t instanceof Ie.ErrorResponseClass){const s=t;this.#ge.sendMessage(_e.BiDiMessageEntry.createResolved(s.toErrorResponse(e.id),e.channel??null))}else{const s=t;console.error(s),this.#ge.sendMessage(_e.BiDiMessageEntry.createResolved(new Ie.UnknownException(s.message).toErrorResponse(e.id),e.channel??null))}}}}r.CommandProcessor=Pe;var Ze={},Ee={};Object.defineProperty(Ee,"__esModule",{value:!0}),Ee.createClient=Ee.CdpClient=void 0;const Se=le;class Me extends Se.EventEmitter{_cdpConnection;_sessionId;constructor(e,t){super(),this._cdpConnection=e,this._sessionId=t}sendCommand(e,...t){const s=t[0];return this._cdpConnection.sendCommand(e,s,this._sessionId)}}Ee.CdpClient=Me,Ee.createClient=function(e,t){return new Me(e,t)};var Oe={};Object.defineProperty(Oe,"__esModule",{value:!0}),Oe.CdpConnection=void 0;const ke=Ee;Oe.CdpConnection=class{#be;#_e;#Ie=new Map;#Te=new Map;#Pe;#Ze=0;constructor(e,t=(()=>{})){this.#be=e,this.#Pe=t,this.#be.setOnMessage(this._onMessage),this.#_e=(0,ke.createClient)(this,null)}close(){this.#be.close();for(const[e,{reject:t}]of this.#Te)t(new Error("Disconnected"));this.#Te.clear(),this.#Ie.clear()}browserClient(){return this.#_e}getCdpClient(e){const t=this.#Ie.get(e);if(!t)throw new Error("Unknown CDP session ID");return t}sendCommand(e,t,s){return new Promise(((n,r)=>{const a=this.#Ze++;this.#Te.set(a,{resolve:n,reject:r});let i={id:a,method:e,params:t};s&&(i.sessionId=s);const o=JSON.stringify(i);this.#be.sendMessage(o),this.#Pe("sent > "+o)}))}_onMessage=async e=>{this.#Pe("received < "+e);const t=JSON.parse(e);if("Target.attachedToTarget"===t.method){const{sessionId:e}=t.params;this.#Ie.set(e,(0,ke.createClient)(this,e))}else if("Target.detachedFromTarget"===t.method){const{sessionId:e}=t.params;this.#Ie.get(e)&&this.#Ie.delete(e)}if(void 0!==t.id){const e=this.#Te.get(t.id);e&&(t.result?e.resolve(t.result):t.error&&e.reject(t.error))}else if(t.method){const e=t.sessionId?this.#Ie.get(t.sessionId):this.#_e;e&&e.emit(t.method,t.params||{})}}};var De={};Object.defineProperty(De,"__esModule",{value:!0}),De.WebSocketTransport=void 0;De.WebSocketTransport=class{_ws;_onMessage=null;constructor(e){this._ws=e,this._ws.on("message",(e=>{this._onMessage&&this._onMessage.call(null,e)}))}setOnMessage(e){this._onMessage=e}async sendMessage(e){this._ws.send(e)}close(){this._onMessage=null,this._ws.close()}},function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.WebSocketTransport=e.CdpConnection=e.CdpClient=void 0;var t=Ee;Object.defineProperty(e,"CdpClient",{enumerable:!0,get:function(){return t.CdpClient}});var s=Oe;Object.defineProperty(e,"CdpConnection",{enumerable:!0,get:function(){return s.CdpConnection}});var n=De;Object.defineProperty(e,"WebSocketTransport",{enumerable:!0,get:function(){return n.WebSocketTransport}})}(Ze);var Ne={},je={};Object.defineProperty(je,"__esModule",{value:!0}),je.SubscriptionManager=void 0;je.SubscriptionManager=class{#Ee=0;#Se=new Map;getChannelsSubscribedToEvent(e,t){return Array.from(this.#Se.keys()).map((s=>({priority:this.#Me(e,t,s),channel:s}))).filter((({priority:e})=>null!==e)).sort(((e,t)=>e.priority-t.priority)).map((({channel:e})=>e))}#Me(e,t,s){const n=this.#Se.get(s);if(void 0===n)return null;let r=[n.get(null)?.get(e),n.get(t)?.get(e)].filter((e=>void 0!==e));return 0===r.length?null:Math.min(...r)}subscribe(e,t,s){this.#Se.has(s)||this.#Se.set(s,new Map);const n=this.#Se.get(s);n.has(t)||n.set(t,new Map);const r=n.get(t);r.has(e)||r.set(e,this.#Ee++)}unsubscribe(e,t,s){if(!this.#Se.has(s))return;const n=this.#Se.get(s);if(!n.has(t))return;const r=n.get(t);r.delete(e),0===r.size&&n.delete(e),0===n.size&&this.#Se.delete(s)}};var ze={};Object.defineProperty(ze,"__esModule",{value:!0}),ze.IdWrapper=void 0;class Re{static#Oe=0;#ke;constructor(){this.#ke=++Re.#Oe}get id(){return this.#ke}}ze.IdWrapper=Re;var Le={};Object.defineProperty(Le,"__esModule",{value:!0}),Le.Buffer=void 0;Le.Buffer=class{#De;#Ne=[];#je;constructor(e,t=(()=>{})){this.#De=e,this.#je=t}get(){return this.#Ne}add(e){for(this.#Ne.push(e);this.#Ne.length>this.#De;){const e=this.#Ne.shift();void 0!==e&&this.#je(e)}}},Object.defineProperty(Ne,"__esModule",{value:!0}),Ne.EventManager=void 0;const Ae=ue,Be=je,Ve=ze,Ue=Le,Fe=F;class Ke extends Ve.IdWrapper{#M;#ze;constructor(e,t){super(),this.#M=t,this.#ze=e}get contextId(){return this.#M}get event(){return this.#ze}}class $e{static#Re=new Map([["log.entryAdded",100]]);#Le=new Map;#Ae=new Map;#Be=new Map;#Ve;#ge;constructor(e){this.#ge=e,this.#Ve=new Be.SubscriptionManager}static#Ue(e,t,s){return JSON.stringify({eventName:e,browsingContext:t,channel:s})}async registerEvent(e,t){await this.registerPromiseEvent(Promise.resolve(e),t,e.method)}async registerPromiseEvent(e,t,s){const n=new Ke(e,t),r=this.#Ve.getChannelsSubscribedToEvent(s,t);this.#Fe(n,s);for(const t of r)this.#ge.sendMessage(Ae.BiDiMessageEntry.createFromPromise(e,t)),this.#Ke(n,t,s)}async subscribe(e,t,s){for(let n of e)for(let e of t)if(null===e||Fe.BrowsingContextStorage.hasKnownContext(e)){this.#Ve.subscribe(n,e,s);for(let t of this.#$e(n,e,s))this.#ge.sendMessage(Ae.BiDiMessageEntry.createFromPromise(t.event,s)),this.#Ke(t,s,n)}}async unsubscribe(e,t,s){for(let n of e)for(let e of t)this.#Ve.unsubscribe(n,e,s)}#Fe(e,t){if(!$e.#Re.has(t))return;const s=$e.#Ue(t,e.contextId);this.#Ae.has(s)||this.#Ae.set(s,new Ue.Buffer($e.#Re.get(t))),this.#Ae.get(s).add(e),this.#Le.has(t)||this.#Le.set(t,new Set),this.#Le.get(t).add(e.contextId)}#Ke(e,t,s){if(!$e.#Re.has(s))return;const n=$e.#Ue(s,e.contextId,t);this.#Be.set(n,Math.max(this.#Be.get(n)??0,e.id))}#$e(e,t,s){const n=$e.#Ue(e,t),r=$e.#Ue(e,t,s),a=this.#Be.get(r)??-1/0,i=this.#Ae.get(n)?.get().filter((e=>e.id>a))??[];return null===t&&Array.from(this.#Le.get(e)?.keys()??[]).filter((e=>null!==e)).map((t=>this.#$e(e,t,s))).forEach((e=>i.push(...e))),i.sort(((e,t)=>e.id-t.id))}}Ne.EventManager=$e;var We={};Object.defineProperty(We,"__esModule",{value:!0}),We.MapperTabPage=void 0;class Je{static#We='<!DOCTYPE html><title>BiDi-CDP Mapper</title><style>body{font-family: Roboto, serif; font-size: 13px; color: #202124;}.log{padding: 12px; font-family: Menlo, serif; font-size: 11px; line-height: 180%; background: #f1f3f4; border-radius: 4px;}.pre{overflow-wrap: break-word; padding: 10px;}.card{margin: 60px auto; padding: 2px 0; max-width: 900px; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15), 0 1px 6px rgba(0, 0, 0, 0.2); border-radius: 8px;}.divider{height: 1px; background: #f0f0f0;}.item{padding: 16px 20px;}</style><div class="card"><div class="item"><h1>BiDi-CDP Mapper is controlling this tab</h1><p>Closing or reloading it will stop the BiDi process. <a target="_blank" title="BiDi-CDP Mapper GitHub Repository" href="https://github.com/GoogleChromeLabs/chromium-bidi">Details.</a></p></div><div class="divider"></div><details id="details"><summary class="item">Debug information</summary></details></div>';static generatePage(){globalThis.document?.documentElement&&(window.MapperTabPage=Je,window.document.documentElement.innerHTML=this.#We,this.#Je("System"),this.#Je("BiDi Messages"),this.#Je("Browsing Contexts"),this.#Je("CDP"))}static log(e,...t){if(!globalThis.document?.documentElement)return;const s=this.#Je(e),n=document.createElement("div");n.className="pre",n.textContent=t.join(", "),s.appendChild(n)}static#Je(e){const t=e+"_log",s=document.getElementById(t);if(s)return s;const n=document.getElementById("details"),r=document.createElement("div");r.className="divider",n.appendChild(r);const a=document.createElement("div");return a.className="item",a.innerHTML=`<h3>${e}</h3><div id="${t}" class="log"></div>`,n.appendChild(a),document.getElementById(t)}}We.MapperTabPage=Je,
+!function(){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},t={},s={},n={},r={},a=e&&e.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(r,"__esModule",{value:!0}),r.EventEmitter=void 0;const o=a((function(e){return{all:e=e||new Map,on:function(t,s){var n=e.get(t);n?n.push(s):e.set(t,[s])},off:function(t,s){var n=e.get(t);n&&(s?n.splice(n.indexOf(s)>>>0,1):e.set(t,[]))},emit:function(t,s){var n=e.get(t);n&&n.slice().map((function(e){e(s)})),(n=e.get("*"))&&n.slice().map((function(e){e(t,s)}))}}}));r.EventEmitter=class{#e=(0,o.default)();on(e,t){return this.#e.on(e,t),this}once(e,t){const s=n=>{t(n),this.off(e,s)};return this.on(e,s)}off(e,t){return this.#e.off(e,t),this}emit(e,t){this.#e.emit(e,t)}},Object.defineProperty(n,"__esModule",{value:!0}),n.createClient=n.CdpClient=void 0;const i=r;class d extends i.EventEmitter{_cdpConnection;_sessionId;constructor(e,t){super(),this._cdpConnection=e,this._sessionId=t}sendCommand(e,...t){const s=t[0];return this._cdpConnection.sendCommand(e,s,this._sessionId)}}n.CdpClient=d,n.createClient=function(e,t){return new d(e,t)};var c={};Object.defineProperty(c,"__esModule",{value:!0}),c.CdpConnection=void 0;const u=n;c.CdpConnection=class{#t;#s;#n=new Map;#r=new Map;#a;#o=0;constructor(e,t=(()=>{})){this.#t=e,this.#a=t,this.#t.setOnMessage(this._onMessage),this.#s=(0,u.createClient)(this,null)}close(){this.#t.close();for(const[e,{reject:t}]of this.#r)t(new Error("Disconnected"));this.#r.clear(),this.#n.clear()}browserClient(){return this.#s}getCdpClient(e){const t=this.#n.get(e);if(!t)throw new Error("Unknown CDP session ID");return t}sendCommand(e,t,s){return new Promise(((n,r)=>{const a=this.#o++;this.#r.set(a,{resolve:n,reject:r});let o={id:a,method:e,params:t};s&&(o.sessionId=s);const i=JSON.stringify(o);this.#t.sendMessage(i),this.#a("sent > "+i)}))}_onMessage=async e=>{this.#a("received < "+e);const t=JSON.parse(e);if("Target.attachedToTarget"===t.method){const{sessionId:e}=t.params;this.#n.set(e,(0,u.createClient)(this,e))}else if("Target.detachedFromTarget"===t.method){const{sessionId:e}=t.params;this.#n.get(e)&&this.#n.delete(e)}if(void 0!==t.id){const e=this.#r.get(t.id);e&&(t.result?e.resolve(t.result):t.error&&e.reject(t.error))}else if(t.method){const e=t.sessionId?this.#n.get(t.sessionId):this.#s;e&&e.emit(t.method,t.params||{})}}};var l={};Object.defineProperty(l,"__esModule",{value:!0}),l.WebSocketTransport=void 0;l.WebSocketTransport=class{_ws;_onMessage=null;constructor(e){this._ws=e,this._ws.on("message",(e=>{this._onMessage&&this._onMessage.call(null,e)}))}setOnMessage(e){this._onMessage=e}async sendMessage(e){this._ws.send(e)}close(){this._onMessage=null,this._ws.close()}},function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.WebSocketTransport=e.CdpConnection=e.CdpClient=void 0;var t=n;Object.defineProperty(e,"CdpClient",{enumerable:!0,get:function(){return t.CdpClient}});var s=c;Object.defineProperty(e,"CdpConnection",{enumerable:!0,get:function(){return s.CdpConnection}});var r=l;Object.defineProperty(e,"WebSocketTransport",{enumerable:!0,get:function(){return r.WebSocketTransport}})}(s);var p,m,h={},g={},f={};p=f,Object.defineProperty(p,"__esModule",{value:!0}),p.log=p.LogType=void 0,(m=p.LogType||(p.LogType={})).system="System",m.bidi="BiDi Messages",m.browsingContexts="Browsing Contexts",m.cdp="CDP",m.commandParser="Command parser",p.log=function(e){return(...t)=>{console.log(e,...t),"MapperTabPage"in globalThis&&globalThis.MapperTabPage.log(e,...t)}},Object.defineProperty(g,"__esModule",{value:!0}),g.ProcessingQueue=void 0;const y=(0,f.log)(f.LogType.system);g.ProcessingQueue=class{#i=[];#d;#c;#u=!1;constructor(e,t=(()=>Promise.resolve())){this.#c=t,this.#d=e}add(e){this.#i.push(e),this.#l()}async#l(){if(!this.#u){for(this.#u=!0;this.#i.length>0;){const e=this.#i.shift();void 0!==e&&await e.then((e=>this.#d(e))).catch((e=>{y("Event was not processed:"+e),this.#c(e)})).finally()}this.#u=!1}}};var v={},x={};Object.defineProperty(x,"__esModule",{value:!0}),x.OutgoingBidiMessage=void 0;class C{#p;#m;constructor(e,t){this.#p=e,this.#m=t}static async createFromPromise(e,t){const s=await e;return new C(s,t)}static createResolved(e,t){return Promise.resolve(new C(e,t))}get message(){return this.#p}get channel(){return this.#m}}x.OutgoingBidiMessage=C;var w={};Object.defineProperty(w,"__esModule",{value:!0}),w.SubscriptionManager=void 0;w.SubscriptionManager=class{#h=0;#g=new Map;getChannelsSubscribedToEvent(e,t){return Array.from(this.#g.keys()).map((s=>({priority:this.#f(e,t,s),channel:s}))).filter((({priority:e})=>null!==e)).sort(((e,t)=>e.priority-t.priority)).map((({channel:e})=>e))}#f(e,t,s){const n=this.#g.get(s);if(void 0===n)return null;let r=[n.get(null)?.get(e),n.get(t)?.get(e)].filter((e=>void 0!==e));return 0===r.length?null:Math.min(...r)}subscribe(e,t,s){this.#g.has(s)||this.#g.set(s,new Map);const n=this.#g.get(s);n.has(t)||n.set(t,new Map);const r=n.get(t);r.has(e)||r.set(e,this.#h++)}unsubscribe(e,t,s){if(!this.#g.has(s))return;const n=this.#g.get(s);if(!n.has(t))return;const r=n.get(t);r.delete(e),0===r.size&&n.delete(e),0===n.size&&this.#g.delete(s)}};var b={};Object.defineProperty(b,"__esModule",{value:!0}),b.IdWrapper=void 0;class _{static#y=0;#v;constructor(){this.#v=++_.#y}get id(){return this.#v}}b.IdWrapper=_;var I={};Object.defineProperty(I,"__esModule",{value:!0}),I.Buffer=void 0;I.Buffer=class{#x;#C=[];#w;constructor(e,t=(()=>{})){this.#x=e,this.#w=t}get(){return this.#C}add(e){for(this.#C.push(e);this.#C.length>this.#x;){const e=this.#C.shift();void 0!==e&&this.#w(e)}}};var T={},P={};!function(e){var t,s,n,r;Object.defineProperty(e,"__esModule",{value:!0}),e.CDP=e.Log=e.BrowsingContext=e.Message=void 0,function(e){class t{constructor(e,t,s){this.error=e,this.message=t,this.stacktrace=s}error;message;stacktrace;toErrorResponse(e){return{id:e,error:this.error,message:this.message,stacktrace:this.stacktrace}}}e.ErrorResponseClass=t;e.UnknownException=class extends t{constructor(e,t){super("unknown error",e,t)}};e.UnknownCommandException=class extends t{constructor(e,t){super("unknown command",e,t)}};e.InvalidArgumentException=class extends t{constructor(e,t){super("invalid argument",e,t)}};e.NoSuchFrameException=class extends t{constructor(e){super("no such frame",e)}}}(e.Message||(e.Message={})),t=e.BrowsingContext||(e.BrowsingContext={}),(s=t.EventNames||(t.EventNames={})).LoadEvent="browsingContext.load",s.DomContentLoadedEvent="browsingContext.domContentLoaded",s.ContextCreatedEvent="browsingContext.contextCreated",s.ContextDestroyedEvent="browsingContext.contextDestroyed",function(e){e.LogEntryAddedEvent="log.entryAdded"}((n=e.Log||(e.Log={})).EventNames||(n.EventNames={})),function(e){e.EventReceivedEvent="cdp.eventReceived"}((r=e.CDP||(e.CDP={})).EventNames||(r.EventNames={}))}(P),Object.defineProperty(T,"__esModule",{value:!0}),T.BrowsingContextStorage=void 0;const Z=P;class E{static#b=new Map;static getTopLevelContexts(){return Array.from(E.#b.values()).filter((e=>null===e.parentId))}static removeContext(e){E.#b.delete(e)}static addContext(e){E.#b.set(e.contextId,e),null!==e.parentId&&E.getKnownContext(e.parentId).addChild(e)}static hasKnownContext(e){return E.#b.has(e)}static findContext(e){return E.#b.get(e)}static getKnownContext(e){const t=E.findContext(e);if(void 0===t)throw new Z.Message.NoSuchFrameException(`Context ${e} not found`);return t}}T.BrowsingContextStorage=E,Object.defineProperty(v,"__esModule",{value:!0}),v.EventManager=void 0;const S=x,M=w,O=b,N=I,D=T;class j extends O.IdWrapper{#_;#I;constructor(e,t){super(),this.#_=t,this.#I=e}get contextId(){return this.#_}get event(){return this.#I}}class k{static#T=new Map([["log.entryAdded",100]]);#P=new Map;#Z=new Map;#E=new Map;#S;#M;constructor(e){this.#M=e,this.#S=new M.SubscriptionManager}static#O(e,t,s){return JSON.stringify({eventName:e,browsingContext:t,channel:s})}async registerEvent(e,t){await this.registerPromiseEvent(Promise.resolve(e),t,e.method)}async registerPromiseEvent(e,t,s){const n=new j(e,t),r=this.#S.getChannelsSubscribedToEvent(s,t);this.#N(n,s);for(const t of r)this.#M.emitOutgoingMessage(S.OutgoingBidiMessage.createFromPromise(e,t)),this.#D(n,t,s)}async subscribe(e,t,s){for(let n of e)for(let e of t)if(null===e||D.BrowsingContextStorage.hasKnownContext(e)){this.#S.subscribe(n,e,s);for(let t of this.#j(n,e,s))this.#M.emitOutgoingMessage(S.OutgoingBidiMessage.createFromPromise(t.event,s)),this.#D(t,s,n)}}async unsubscribe(e,t,s){for(let n of e)for(let e of t)this.#S.unsubscribe(n,e,s)}#N(e,t){if(!k.#T.has(t))return;const s=k.#O(t,e.contextId);this.#Z.has(s)||this.#Z.set(s,new N.Buffer(k.#T.get(t))),this.#Z.get(s).add(e),this.#P.has(t)||this.#P.set(t,new Set),this.#P.get(t).add(e.contextId)}#D(e,t,s){if(!k.#T.has(s))return;const n=k.#O(s,e.contextId,t);this.#E.set(n,Math.max(this.#E.get(n)??0,e.id))}#j(e,t,s){const n=k.#O(e,t),r=k.#O(e,t,s),a=this.#E.get(r)??-1/0,o=this.#Z.get(n)?.get().filter((e=>e.id>a))??[];return null===t&&Array.from(this.#P.get(e)?.keys()??[]).filter((e=>null!==e)).map((t=>this.#j(e,t,s))).forEach((e=>o.push(...e))),o.sort(((e,t)=>e.id-t.id))}}v.EventManager=k;var z={},L={},R={},A={};Object.defineProperty(A,"__esModule",{value:!0}),A.Deferred=void 0;class B{#k=()=>{};#z=()=>{};#L;#R=!1;get isFinished(){return this.#R}constructor(){this.#L=new Promise(((e,t)=>{this.#k=e,this.#z=t}))}then(e,t){return this.#L.then(e,t)}catch(e){return this.#L.catch(e)}resolve(e){this.#R=!0,this.#k(e)}reject(e){this.#R=!0,this.#z(e)}finally(e){return this.#L.finally(e)}[Symbol.toStringTag]="Promise"}A.Deferred=B;var V={},U={};Object.defineProperty(U,"__esModule",{value:!0}),U.getRemoteValuesText=U.logMessageFormatter=void 0;const F=["%s","%d","%i","%f","%o","%O","%c"];function K(e){return F.some((t=>e.includes(t)))}function $(e){let t="";const s=e[0].value.toString(),n=e.slice(1,void 0),r=s.split(new RegExp(F.map((e=>"("+e+")")).join("|"),"g"));for(const s of r)if(void 0!==s&&""!=s)if(K(s)){const r=n.shift();if(void 0===r)throw new Error('Less value is provided: "'+G(e,!1)+'"');"%s"===s?t+=W(r):"%d"===s||"%i"===s?"bigint"===r.type||"number"===r.type||"string"===r.type?t+=parseInt(r.value.toString(),10):t+="NaN":"%f"===s?"bigint"===r.type||"number"===r.type||"string"===r.type?t+=parseFloat(r.value.toString()):t+="NaN":t+=J(r)}else t+=s;if(n.length>0)throw new Error('More value is provided: "'+G(e,!1)+'"');return t}function J(e){if("array"!==e.type&&"bigint"!==e.type&&"date"!==e.type&&"number"!==e.type&&"object"!==e.type&&"string"!==e.type)return W(e);if("bigint"===e.type)return e.value.toString()+"n";if("number"===e.type)return e.value.toString();if(["date","string"].includes(e.type))return JSON.stringify(e.value);if("object"===e.type)return"{"+e.value.map((e=>`${JSON.stringify(e[0])}:${J(e[1])}`)).join(",")+"}";if("array"===e.type)return"["+e.value.map((e=>J(e))).join(",")+"]";throw Error("Invalid value type: "+e.toString())}function W(e){if(!e.hasOwnProperty("value"))return e.type;switch(e.type){case"string":case"number":case"boolean":case"bigint":return String(e.value);case"regexp":return`/${e.value.pattern}/${e.value.flags}`;case"date":return new Date(e.value).toString();case"object":return`Object(${e.value?.length})`;case"array":return`Array(${e.value?.length})`;case"map":return`Map(${e.value.length})`;case"set":return`Set(${e.value.length})`;case"node":return"node";default:return e.type}}function G(e,t){const s=e[0];return s?"string"===s.type&&K(s.value.toString())&&t?$(e):e.map((e=>W(e))).join(" "):""}U.logMessageFormatter=$,U.getRemoteValuesText=G;var q={},H={};Object.defineProperty(H,"__esModule",{value:!0}),H.ScriptEvaluator=void 0;const Q=P;class Y{static#A=0;static#B=1;static#V=new Map;static async serializeCdpObject(e,t,s){const n=this.#U(e),r=await s.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((e=>e)),awaitPromise:!1,arguments:[n],generateWebDriverValue:!0,executionContextId:s.executionContextId});return await this.#F(r,s,t)}static#U(e){return void 0!==e.objectId?{objectId:e.objectId}:void 0!==e.unserializableValue?{unserializableValue:e.unserializableValue}:{value:e.value}}static async stringifyObject(e,t){return(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(e){return String(e)})),awaitPromise:!1,arguments:[e],returnByValue:!0,executionContextId:t.executionContextId})).result.value}static async callFunction(e,t,s,n,r,a){const o=`(...args)=>{ return _callFunction((\n${t}\n), args);\n      function _callFunction(f, args) {\n        const deserializedThis = args.shift();\n        const deserializedArgs = args;\n        return f.apply(deserializedThis, deserializedArgs);\n      }}`,i=[await this.#K(s,e)];let d;i.push(...await Promise.all(n.map((async t=>await this.#K(t,e)))));try{d=await e.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:o,awaitPromise:r,arguments:i,generateWebDriverValue:!0,executionContextId:e.executionContextId})}catch(e){if(-32e3===e.code&&["Could not find object with given id","Argument should belong to the same JavaScript world as target object"].includes(e.message))throw new Q.Message.InvalidArgumentException("Handle was not found.");throw e}return d.exceptionDetails?{exceptionDetails:await this.#$(d.exceptionDetails,this.#B,a,e),type:"exception",realm:e.realmId}:{type:"success",result:await Y.#F(d,e,a),realm:e.realmId}}static realmDestroyed(e){return Array.from(this.#V.entries()).filter((([,t])=>t===e.realmId)).map((([e])=>this.#V.delete(e)))}static async disown(e,t){if(Y.#V.get(t)===e.realmId){try{await e.cdpClient.sendCommand("Runtime.releaseObject",{objectId:t})}catch(e){if(-32e3!==e.code||"Invalid remote object id"!==e.message)throw e}this.#V.delete(t)}}static async#$(e,t,s,n){const r=e.stackTrace?.callFrames.map((e=>({url:e.url,functionName:e.functionName,lineNumber:e.lineNumber-t,columnNumber:e.columnNumber}))),a=await this.serializeCdpObject(e.exception,s,n),o=await this.stringifyObject(e.exception,n);return{exception:a,columnNumber:e.columnNumber,lineNumber:e.lineNumber-t,stackTrace:{callFrames:r||[]},text:o||e.text}}static async#F(e,t,s){const n=e.result.webDriverValue;if(!e.result.objectId)return n;const r=e.result.objectId,a=n;return"root"===s?(a.handle=r,this.#V.set(r,t.realmId)):await t.cdpClient.sendCommand("Runtime.releaseObject",{objectId:r}),a}static async scriptEvaluate(e,t,s,n){let r=await e.cdpClient.sendCommand("Runtime.evaluate",{contextId:e.executionContextId,expression:t,awaitPromise:s,generateWebDriverValue:!0});return r.exceptionDetails?{exceptionDetails:await this.#$(r.exceptionDetails,this.#A,n,e),type:"exception",realm:e.realmId}:{type:"success",result:await Y.#F(r,e,n),realm:e.realmId}}static async#K(e,t){if("handle"in e)return{objectId:e.handle};switch(e.type){case"undefined":return{unserializableValue:"undefined"};case"null":return{unserializableValue:"null"};case"string":return{value:e.value};case"number":return"NaN"===e.value?{unserializableValue:"NaN"}:"-0"===e.value?{unserializableValue:"-0"}:"+Infinity"===e.value?{unserializableValue:"+Infinity"}:"Infinity"===e.value?{unserializableValue:"Infinity"}:"-Infinity"===e.value?{unserializableValue:"-Infinity"}:{value:e.value};case"boolean":return{value:!!e.value};case"bigint":return{unserializableValue:`BigInt(${JSON.stringify(e.value)})`};case"date":return{unserializableValue:`new Date(Date.parse(${JSON.stringify(e.value)}))`};case"regexp":return{unserializableValue:`new RegExp(${JSON.stringify(e.value.pattern)}, ${JSON.stringify(e.value.flags)})`};case"map":{const s=await this.#J(e.value,t);return{objectId:(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(...e){const t=new Map;for(let s=0;s<e.length;s+=2)t.set(e[s],e[s+1]);return t})),awaitPromise:!1,arguments:s,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"object":{const s=await this.#J(e.value,t);return{objectId:(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(...e){const t={};for(let s=0;s<e.length;s+=2){t[e[s]]=e[s+1]}return t})),awaitPromise:!1,arguments:s,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"array":{const s=await Y.#W(e.value,t);return{objectId:(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(...e){return e})),awaitPromise:!1,arguments:s,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}case"set":{const s=await this.#W(e.value,t);return{objectId:(await t.cdpClient.sendCommand("Runtime.callFunctionOn",{functionDeclaration:String((function(...e){return new Set(e)})),awaitPromise:!1,arguments:s,returnByValue:!1,executionContextId:t.executionContextId})).result.objectId}}default:throw new Error(`Value ${JSON.stringify(e)} is not deserializable.`)}}static async#J(e,t){const s=[];for(let n of e){const e=n[0],r=n[1];let a,o;a="string"==typeof e?{value:e}:await this.#K(e,t),o=await this.#K(r,t),s.push(a),s.push(o)}return s}static async#W(e,t){const s=[];for(let n of e)s.push(await this.#K(n,t));return s}}H.ScriptEvaluator=Y,function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.Realm=e.RealmType=void 0;const t=P,s=H,n=T;(e.RealmType||(e.RealmType={})).window="window";class r{static#G=new Map;static create(e,t,s,n,a,o,i,d){const c=new r(e,t,s,n,a,o,i,d);return r.#G.set(c.realmId,c),c}static findRealms(e={}){return Array.from(r.#G.values()).filter((t=>(void 0===e.realmId||e.realmId===t.realmId)&&((void 0===e.browsingContextId||e.browsingContextId===t.browsingContextId)&&((void 0===e.executionContextId||e.executionContextId===t.executionContextId)&&((void 0===e.type||e.type===t.type)&&((void 0===e.sandbox||e.sandbox===t.#q)&&(void 0===e.cdpSessionId||e.cdpSessionId===t.#H)))))))}static findRealm(e){const t=r.findRealms(e);if(1===t.length)return t[0]}static getRealm(e){const s=r.findRealm(e);if(void 0===s)throw new t.Message.NoSuchFrameException(`Realm ${JSON.stringify(e)} not found`);return s}static clearBrowsingContext(e){r.findRealms({browsingContextId:e}).map((e=>e.delete()))}delete(){r.#G.delete(this.realmId),s.ScriptEvaluator.realmDestroyed(this)}#Q;#Y;#X;#ee;#te;#q;#H;#se;constructor(e,t,s,n,r,a,o,i){this.#Q=e,this.#Y=t,this.#X=s,this.#q=a,this.#ee=n,this.#te=r,this.#H=o,this.#se=i}toBiDi(){return{realm:this.realmId,origin:this.origin,type:this.type,context:this.browsingContextId,...void 0!==this.#q?{sandbox:this.#q}:{}}}get realmId(){return this.#Q}get browsingContextId(){return this.#Y}get executionContextId(){return this.#X}get origin(){return this.#ee}get type(){return this.#te}get cdpClient(){return this.#se}async callFunction(e,t,r,a,o){const i=n.BrowsingContextStorage.getKnownContext(this.browsingContextId);return await i.awaitUnblocked(),{result:await s.ScriptEvaluator.callFunction(this,e,t,r,a,o)}}async scriptEvaluate(e,t,r){const a=n.BrowsingContextStorage.getKnownContext(this.browsingContextId);return await a.awaitUnblocked(),{result:await s.ScriptEvaluator.scriptEvaluate(this,e,t,r)}}async disown(e){await s.ScriptEvaluator.disown(this,e)}async serializeCdpObject(e,t){return await s.ScriptEvaluator.serializeCdpObject(e,t,this)}async stringifyObject(e){return s.ScriptEvaluator.stringifyObject(e,this)}}e.Realm=r}(q),Object.defineProperty(V,"__esModule",{value:!0}),V.LogManager=void 0;const X=P,ee=U,te=q;class se{#se;#H;#ne;constructor(e,t,s){this.#H=t,this.#se=e,this.#ne=s}static create(e,t,s){const n=new se(e,t,s);return n.#re(),n}#re(){this.#ae()}#ae(){this.#oe()}#oe(){this.#se.on("Runtime.consoleAPICalled",(e=>{const t=te.Realm.findRealm({cdpSessionId:this.#H,executionContextId:e.executionContextId}),s=void 0===t?Promise.resolve(e.args):Promise.all(e.args.map((async e=>t.serializeCdpObject(e,"none"))));this.#ne.registerPromiseEvent(s.then((s=>({method:X.Log.EventNames.LogEntryAddedEvent,params:{level:se.#ie(e.type),source:{realm:t?.realmId??"UNKNOWN",context:t?.browsingContextId??"UNKNOWN"},text:(0,ee.getRemoteValuesText)(s,!0),timestamp:Math.round(e.timestamp),stackTrace:se.#de(e.stackTrace),type:"console",method:"warning"===e.type?"warn":e.type,args:s}}))),t?.browsingContextId??"UNKNOWN",X.Log.EventNames.LogEntryAddedEvent)})),this.#se.on("Runtime.exceptionThrown",(e=>{const t=te.Realm.findRealm({cdpSessionId:this.#H,executionContextId:e.exceptionDetails.executionContextId}),s=(async()=>e.exceptionDetails.exception?void 0===t?JSON.stringify(e.exceptionDetails.exception):await t.stringifyObject(e.exceptionDetails.exception):e.exceptionDetails.text)();this.#ne.registerPromiseEvent(s.then((s=>({method:X.Log.EventNames.LogEntryAddedEvent,params:{level:"error",source:{realm:t?.realmId??"UNKNOWN",context:t?.browsingContextId??"UNKNOWN"},text:s,timestamp:Math.round(e.timestamp),stackTrace:se.#de(e.exceptionDetails.stackTrace),type:"javascript"}}))),t?.browsingContextId??"UNKNOWN",X.Log.EventNames.LogEntryAddedEvent)}))}static#ie(e){return["assert","error"].includes(e)?"error":["debug","trace"].includes(e)?"debug":["warn","warning"].includes(e)?"warn":"info"}static#de(e){const t=e?.callFrames.map((e=>({columnNumber:e.columnNumber,functionName:e.functionName,lineNumber:e.lineNumber,url:e.url})));return t?{callFrames:t}:void 0}}V.LogManager=se,Object.defineProperty(R,"__esModule",{value:!0}),R.BrowsingContextImpl=void 0;const ne=P,re=A,ae=V,oe=q,ie=T;class de{#ce={documentInitialized:new re.Deferred,targetUnblocked:new re.Deferred,Page:{navigatedWithinDocument:new re.Deferred,lifecycleEvent:{DOMContentLoaded:new re.Deferred,load:new re.Deferred}}};#_;#ue;#le;#ne;#pe=new Map;#me="about:blank";#he=null;#H;#se;#ge;get#fe(){if(void 0===this.#ge)throw new Error(`No default realm for browsing context ${this.#_}`);return this.#ge}constructor(e,t,s,n,r,a){this.#_=e,this.#ue=t,this.#se=s,this.#le=r,this.#ne=a,this.#H=n,this.#ye(),ie.BrowsingContextStorage.addContext(this)}static async createFrameContext(e,t,s,n,r){const a=new de(e,t,s,n,null,r);a.#ce.targetUnblocked.resolve(),await r.registerEvent({method:ne.BrowsingContext.EventNames.ContextCreatedEvent,params:a.serializeToBidiValue()},a.contextId)}static async createTargetContext(e,t,s,n,r,a){const o=new de(e,t,s,n,r,a);o.#ve(),await a.registerEvent({method:ne.BrowsingContext.EventNames.ContextCreatedEvent,params:o.serializeToBidiValue()},o.contextId)}get cdpBrowserContextId(){return this.#le}convertFrameToTargetContext(e,t){this.#xe(e,t),this.#ve()}async delete(){if(await this.#Ce(),null!==this.parentId){ie.BrowsingContextStorage.getKnownContext(this.parentId).#pe.delete(this.contextId)}await this.#ne.registerEvent({method:ne.BrowsingContext.EventNames.ContextDestroyedEvent,params:this.serializeToBidiValue()},this.contextId),ie.BrowsingContextStorage.removeContext(this.contextId)}async#Ce(){await Promise.all(this.children.map((e=>e.delete())))}#xe(e,t){this.#ce.targetUnblocked.isFinished||this.#ce.targetUnblocked.reject("OOPiF"),this.#ce.targetUnblocked=new re.Deferred,this.#se=e,this.#H=t,this.#ye()}async#ve(){ae.LogManager.create(this.#se,this.#H,this.#ne),await this.#se.sendCommand("Runtime.enable"),await this.#se.sendCommand("Page.enable"),await this.#se.sendCommand("Page.setLifecycleEventsEnabled",{enabled:!0}),await this.#se.sendCommand("Target.setAutoAttach",{autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),await this.#se.sendCommand("Runtime.runIfWaitingForDebugger"),this.#ce.targetUnblocked.resolve()}get contextId(){return this.#_}get parentId(){return this.#ue}get cdpSessionId(){return this.#H}get children(){return Array.from(this.#pe.values())}get url(){return this.#me}addChild(e){this.#pe.set(e.contextId,e)}async awaitLoaded(){await this.#ce.Page.lifecycleEvent.load}async awaitUnblocked(){await this.#ce.targetUnblocked}serializeToBidiValue(e=0,t=!0){return{context:this.#_,url:this.url,children:e>0?this.children.map((t=>t.serializeToBidiValue(e-1,!1))):null,...t?{parent:this.#ue}:{}}}#ye(){this.#se.on("Target.targetInfoChanged",(e=>{this.contextId===e.targetInfo.targetId&&(this.#me=e.targetInfo.url)})),this.#se.on("Page.frameNavigated",(async e=>{this.contextId===e.frame.id&&(this.#me=e.frame.url+(e.frame.urlFragment??""),await this.#Ce(),oe.Realm.clearBrowsingContext(this.contextId))})),this.#se.on("Page.navigatedWithinDocument",(e=>{this.contextId===e.frameId&&(this.#me=e.url,this.#ce.Page.navigatedWithinDocument.resolve(e))})),this.#se.on("Page.lifecycleEvent",(async e=>{if(this.contextId===e.frameId)if("init"===e.name&&(this.#we(e.loaderId),this.#ce.documentInitialized.resolve()),"commit"!==e.name){if(e.loaderId===this.#he)switch(e.name){case"DOMContentLoaded":this.#ce.Page.lifecycleEvent.DOMContentLoaded.resolve(e),await this.#ne.registerEvent({method:ne.BrowsingContext.EventNames.DomContentLoadedEvent,params:{context:this.contextId,navigation:this.#he,url:this.#me}},this.contextId);break;case"load":this.#ce.Page.lifecycleEvent.load.resolve(e),await this.#ne.registerEvent({method:ne.BrowsingContext.EventNames.LoadEvent,params:{context:this.contextId,navigation:this.#he,url:this.#me}},this.contextId)}}else this.#he=e.loaderId})),this.#se.on("Runtime.executionContextCreated",(e=>{if(e.context.auxData.frameId!==this.contextId)return;if(!["default","isolated"].includes(e.context.auxData.type))return;const t=oe.Realm.create(e.context.uniqueId,this.contextId,e.context.id,this.#be(e),oe.RealmType.window,"isolated"===e.context.auxData.type?e.context.name:void 0,this.#H,this.#se);e.context.auxData.isDefault&&(this.#ge=t)})),this.#se.on("Runtime.executionContextDestroyed",(e=>{oe.Realm.findRealms({cdpSessionId:this.#H,executionContextId:e.executionContextId}).map((e=>e.delete()))}))}#be(e){return"isolated"===e.context.auxData.type?this.#fe.origin:["://",""].includes(e.context.origin)?"null":e.context.origin}#we(e){this.#he!==e&&(this.#ce.documentInitialized.isFinished||this.#ce.documentInitialized.reject("Document changed"),this.#ce.documentInitialized=new re.Deferred,this.#ce.Page.navigatedWithinDocument.isFinished||this.#ce.Page.navigatedWithinDocument.reject("Document changed"),this.#ce.Page.navigatedWithinDocument=new re.Deferred,this.#ce.Page.lifecycleEvent.DOMContentLoaded.isFinished||this.#ce.Page.lifecycleEvent.DOMContentLoaded.reject("Document changed"),this.#ce.Page.lifecycleEvent.DOMContentLoaded=new re.Deferred,this.#ce.Page.lifecycleEvent.load.isFinished||this.#ce.Page.lifecycleEvent.load.reject("Document changed"),this.#ce.Page.lifecycleEvent.load=new re.Deferred,this.#he=e)}async navigate(e,t){await this.#ce.targetUnblocked;const s=await this.#se.sendCommand("Page.navigate",{url:e,frameId:this.contextId});if(s.errorText)throw new ne.Message.UnknownException(s.errorText);switch(void 0!==s.loaderId&&s.loaderId!==this.#he&&this.#we(s.loaderId),t){case"none":break;case"interactive":void 0===s.loaderId?await this.#ce.Page.navigatedWithinDocument:await this.#ce.Page.lifecycleEvent.DOMContentLoaded;break;case"complete":void 0===s.loaderId?await this.#ce.Page.navigatedWithinDocument:await this.#ce.Page.lifecycleEvent.load;break;default:throw new Error(`Not implemented wait '${t}'`)}return{result:{navigation:s.loaderId||null,url:e}}}async getOrCreateSandbox(e){if(void 0===e||""===e)return this.#fe;let t=oe.Realm.findRealms({browsingContextId:this.contextId,sandbox:e});if(0==t.length&&(await this.#se.sendCommand("Page.createIsolatedWorld",{frameId:this.contextId,worldName:e}),t=oe.Realm.findRealms({browsingContextId:this.contextId,sandbox:e})),1!==t.length)throw Error(`Sandbox ${e} wasn't created.`);return t[0]}}R.BrowsingContextImpl=de,Object.defineProperty(L,"__esModule",{value:!0}),L.BrowsingContextProcessor=void 0;const ce=P,ue=R,le=q,pe=T,me=(0,f.log)(f.LogType.browsingContexts);class he{sessions=new Set;#_e;#Ie;#ne;constructor(e,t,s){this.#_e=e,this.#Ie=t,this.#ne=s,this.#Te(this.#_e.browserClient())}#Te(e){this.#Pe(e)}#Pe(e){e.on("Target.attachedToTarget",(async t=>{await this.#Ze(t,e)})),e.on("Target.detachedFromTarget",(async e=>{await he.#Ee(e)}))}#Se(e){if(this.sessions.has(e))return;this.sessions.add(e);const t=this.#_e.getCdpClient(e);this.#Pe(t),t.on("*",(async(t,s)=>{await this.#ne.registerEvent({method:ce.CDP.EventNames.EventReceivedEvent,params:{cdpMethod:t,cdpParams:s||{},cdpSession:e}},null)})),t.on("Page.frameAttached",(async s=>{await ue.BrowsingContextImpl.createFrameContext(s.frameId,s.parentFrameId,t,e,this.#ne)}))}async#Ze(e,t){const{sessionId:s,targetInfo:n}=e;let r=this.#_e.getCdpClient(s);if(!this.#Me(n))return await r.sendCommand("Runtime.runIfWaitingForDebugger"),void await t.sendCommand("Target.detachFromTarget",e);me("AttachedToTarget event received: "+JSON.stringify(e)),this.#Se(s),pe.BrowsingContextStorage.hasKnownContext(n.targetId)?pe.BrowsingContextStorage.getKnownContext(n.targetId).convertFrameToTargetContext(r,s):await ue.BrowsingContextImpl.createTargetContext(n.targetId,null,r,s,e.targetInfo.browserContextId??null,this.#ne)}static async#Ee(e){const t=e.targetId;await(pe.BrowsingContextStorage.findContext(t)?.delete())}async process_browsingContext_getTree(e){return{result:{contexts:(void 0===e.root?pe.BrowsingContextStorage.getTopLevelContexts():[pe.BrowsingContextStorage.getKnownContext(e.root)]).map((t=>t.serializeToBidiValue(e.maxDepth??Number.MAX_VALUE)))}}}async process_browsingContext_create(e){const t=this.#_e.browserClient();let s;if(void 0!==e.referenceContext&&(s=pe.BrowsingContextStorage.getKnownContext(e.referenceContext),null!==s.parentId))throw new ce.Message.InvalidArgumentException("referenceContext should be a top-level context");const n=(await t.sendCommand("Target.createTarget",{url:"about:blank",newWindow:"window"===e.type,...s?.cdpBrowserContextId?{browserContextId:s.cdpBrowserContextId}:{}})).targetId,r=pe.BrowsingContextStorage.getKnownContext(n);return await r.awaitLoaded(),{result:r.serializeToBidiValue(1)}}async process_browsingContext_navigate(e){const t=pe.BrowsingContextStorage.getKnownContext(e.context);return await t.navigate(e.url,void 0!==e.wait?e.wait:"none")}static async#Oe(e){if("realm"in e)return le.Realm.getRealm({realmId:e.realm});const t=pe.BrowsingContextStorage.getKnownContext(e.context);return await t.getOrCreateSandbox(e.sandbox)}async process_script_evaluate(e){const t=await he.#Oe(e.target);return await t.scriptEvaluate(e.expression,e.awaitPromise,e.resultOwnership??"none")}process_script_getRealms(e){void 0!==e.context&&pe.BrowsingContextStorage.getKnownContext(e.context);const t=le.Realm.findRealms({browsingContextId:e.context,type:e.type}).map((e=>e.toBiDi()));return{result:{realms:t}}}async process_script_callFunction(e){const t=await he.#Oe(e.target);return await t.callFunction(e.functionDeclaration,e.this||{type:"undefined"},e.arguments||[],e.awaitPromise,e.resultOwnership??"none")}async process_script_disown(e){const t=await he.#Oe(e.target);return await Promise.all(e.handles.map((async e=>await t.disown(e)))),{result:{}}}async process_browsingContext_close(e){const t=this.#_e.browserClient();if(null!==pe.BrowsingContextStorage.getKnownContext(e.context).parentId)throw new ce.Message.InvalidArgumentException("Not a top-level browsing context cannot be closed.");const s=new Promise((async s=>{const n=r=>{r.targetId===e.context&&(t.off("Target.detachedFromTarget",n),s())};t.on("Target.detachedFromTarget",n)}));return await this.#_e.browserClient().sendCommand("Target.closeTarget",{targetId:e.context}),await s,{result:{}}}#Me(e){return e.targetId!==this.#Ie&&["page","iframe"].includes(e.type)}async process_cdp_sendCommand(e){const t=e.cdpSession?this.#_e.getCdpClient(e.cdpSession):this.#_e.browserClient();return{result:await t.sendCommand(e.cdpMethod,e.cdpParams),cdpSession:e.cdpSession}}async process_cdp_getSession(e){const t=e.context,s=pe.BrowsingContextStorage.getKnownContext(t).cdpSessionId;return void 0===s?{result:{cdpSession:null}}:{result:{cdpSession:s}}}}L.BrowsingContextProcessor=he,Object.defineProperty(z,"__esModule",{value:!0}),z.CommandProcessor=void 0;const ge=L,fe=P,ye=x,ve=r;class xe{parseGetRealmsParams(e){return e}parseCallFunctionParams(e){return e}parseEvaluateParams(e){return e}parseDisownParams(e){return e}parseSendCommandParams(e){return e}parseGetSessionParams(e){return e}parseNavigateParams(e){return e}parseGetTreeParams(e){return e}parseSubscribeParams(e){return e}parseCreateParams(e){return e}parseCloseParams(e){return e}}class Ce extends ve.EventEmitter{#Ne;#ne;#De;constructor(e,t,s,n=new xe){super(),this.#ne=t,this.#Ne=new ge.BrowsingContextProcessor(e,s,t),this.#De=n}async#je(){return{result:{ready:!1,message:"already connected"}}}async#ke(e,t){return await this.#ne.subscribe(e.events,e.contexts??[null],t),{result:{}}}async#ze(e,t){return await this.#ne.unsubscribe(e.events,e.contexts??[null],t),{result:{}}}async#Le(e){switch(e.method){case"session.status":return await this.#je();case"session.subscribe":return await this.#ke(this.#De.parseSubscribeParams(e.params),e.channel??null);case"session.unsubscribe":return await this.#ze(this.#De.parseSubscribeParams(e.params),e.channel??null);case"browsingContext.create":return await this.#Ne.process_browsingContext_create(this.#De.parseCreateParams(e.params));case"browsingContext.close":return await this.#Ne.process_browsingContext_close(this.#De.parseCloseParams(e.params));case"browsingContext.getTree":return await this.#Ne.process_browsingContext_getTree(this.#De.parseGetTreeParams(e.params));case"browsingContext.navigate":return await this.#Ne.process_browsingContext_navigate(this.#De.parseNavigateParams(e.params));case"script.getRealms":return this.#Ne.process_script_getRealms(this.#De.parseGetRealmsParams(e.params));case"script.callFunction":return await this.#Ne.process_script_callFunction(this.#De.parseCallFunctionParams(e.params));case"script.evaluate":return await this.#Ne.process_script_evaluate(this.#De.parseEvaluateParams(e.params));case"script.disown":return await this.#Ne.process_script_disown(this.#De.parseDisownParams(e.params));case"cdp.sendCommand":return await this.#Ne.process_cdp_sendCommand(this.#De.parseSendCommandParams(e.params));case"cdp.getSession":return await this.#Ne.process_cdp_getSession(this.#De.parseGetSessionParams(e.params));default:throw new fe.Message.UnknownCommandException(`Unknown command '${e.method}'.`)}}processCommand=async e=>{try{const t=await this.#Le(e),s={id:e.id,...t};this.emit("response",ye.OutgoingBidiMessage.createResolved(s,e.channel??null))}catch(t){if(t instanceof fe.Message.ErrorResponseClass){const s=t;this.emit("response",ye.OutgoingBidiMessage.createResolved(s.toErrorResponse(e.id),e.channel??null))}else{const s=t;console.error(s),this.emit("response",ye.OutgoingBidiMessage.createResolved(new fe.Message.UnknownException(s.message).toErrorResponse(e.id),e.channel??null))}}}}z.CommandProcessor=Ce,Object.defineProperty(h,"__esModule",{value:!0}),h.BidiServer=void 0;const we=r,be=g,_e=v,Ie=z,Te=T;class Pe extends we.EventEmitter{#Re;#t;#Ae;constructor(e,t,s,n){super(),this.#Re=new be.ProcessingQueue(this.#Be),this.#t=e,this.#t.setOnMessage(this.#Ve),this.#Ae=new Ie.CommandProcessor(t,new _e.EventManager(this),s,n),this.#Ae.on("response",(e=>{this.emitOutgoingMessage(e)}))}static async createAndStart(e,t,s,n){const r=new Pe(e,t,s,n),a=t.browserClient();return await a.sendCommand("Target.setDiscoverTargets",{discover:!0}),await a.sendCommand("Target.setAutoAttach",{autoAttach:!0,waitForDebuggerOnStart:!0,flatten:!0}),await Promise.all(Te.BrowsingContextStorage.getTopLevelContexts().map((e=>e.awaitLoaded()))),r}#Be=async e=>{const t=e.message;null!==e.channel&&(t.channel=e.channel),await this.#t.sendMessage(t)};emitOutgoingMessage(e){this.#Re.add(e)}close(){this.#t.close()}#Ve=async e=>{this.#Ae.processCommand(e)}}h.BidiServer=Pe;var Ze={};Object.defineProperty(Ze,"__esModule",{value:!0}),Ze.MapperTabPage=void 0;class Ee{static#Ue='<!DOCTYPE html><title>BiDi-CDP Mapper</title><style>body{font-family: Roboto, serif; font-size: 13px; color: #202124;}.log{padding: 12px; font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; font-size: 11px; line-height: 180%; background: #f1f3f4; border-radius: 4px;}.pre{overflow-wrap: break-word; padding: 10px;}.card{margin: 60px auto; padding: 2px 0; max-width: 900px; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15), 0 1px 6px rgba(0, 0, 0, 0.2); border-radius: 8px;}.divider{height: 1px; background: #f0f0f0;}.item{padding: 16px 20px;}</style><div class="card"><div class="item"><h1>BiDi-CDP Mapper is controlling this tab</h1><p>Closing or reloading it will stop the BiDi process. <a target="_blank" title="BiDi-CDP Mapper GitHub Repository" href="https://github.com/GoogleChromeLabs/chromium-bidi">Details.</a></p></div><div class="divider"></div><details id="details"><summary class="item">Debug information</summary></details></div>';static generatePage(){globalThis.document?.documentElement&&(window.MapperTabPage=Ee,window.document.documentElement.innerHTML=this.#Ue,this.#Fe("System"),this.#Fe("BiDi Messages"),this.#Fe("Browsing Contexts"),this.#Fe("CDP"))}static log(e,...t){if(!globalThis.document?.documentElement)return;const s=this.#Fe(e),n=document.createElement("div");n.className="pre",n.textContent=t.join(", "),s.appendChild(n)}static#Fe(e){const t=e+"_log",s=document.getElementById(t);if(s)return s;const n=document.getElementById("details"),r=document.createElement("div");r.className="divider",n.appendChild(r);const a=document.createElement("div");return a.className="item",a.innerHTML=`<h3>${e}</h3><div id="${t}" class="log"></div>`,n.appendChild(a),document.getElementById(t)}}Ze.MapperTabPage=Ee;var Se={},Me={},Oe={},Ne={},De={},je={};!function(e){var t;Object.defineProperty(e,"__esModule",{value:!0}),e.getParsedType=e.ZodParsedType=e.util=void 0,function(e){e.assertNever=function(e){throw new Error},e.arrayToEnum=e=>{const t={};for(const s of e)t[s]=s;return t},e.getValidEnumValues=t=>{const s=e.objectKeys(t).filter((e=>"number"!=typeof t[t[e]])),n={};for(const e of s)n[e]=t[e];return e.objectValues(n)},e.objectValues=t=>e.objectKeys(t).map((function(e){return t[e]})),e.objectKeys="function"==typeof Object.keys?e=>Object.keys(e):e=>{const t=[];for(const s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.push(s);return t},e.find=(e,t)=>{for(const s of e)if(t(s))return s},e.isInteger="function"==typeof Number.isInteger?e=>Number.isInteger(e):e=>"number"==typeof e&&isFinite(e)&&Math.floor(e)===e,e.joinValues=function(e,t=" | "){return e.map((e=>"string"==typeof e?`'${e}'`:e)).join(t)}}(t=e.util||(e.util={})),e.ZodParsedType=t.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]);e.getParsedType=t=>{switch(typeof t){case"undefined":return e.ZodParsedType.undefined;case"string":return e.ZodParsedType.string;case"number":return isNaN(t)?e.ZodParsedType.nan:e.ZodParsedType.number;case"boolean":return e.ZodParsedType.boolean;case"function":return e.ZodParsedType.function;case"bigint":return e.ZodParsedType.bigint;case"object":return Array.isArray(t)?e.ZodParsedType.array:null===t?e.ZodParsedType.null:t.then&&"function"==typeof t.then&&t.catch&&"function"==typeof t.catch?e.ZodParsedType.promise:"undefined"!=typeof Map&&t instanceof Map?e.ZodParsedType.map:"undefined"!=typeof Set&&t instanceof Set?e.ZodParsedType.set:"undefined"!=typeof Date&&t instanceof Date?e.ZodParsedType.date:e.ZodParsedType.object;default:return e.ZodParsedType.unknown}}}(je),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.setErrorMap=e.overrideErrorMap=e.defaultErrorMap=e.ZodError=e.quotelessJson=e.ZodIssueCode=void 0;const t=je;e.ZodIssueCode=t.util.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of"]);e.quotelessJson=e=>JSON.stringify(e,null,2).replace(/"([^"]+)":/g,"$1:");class s extends Error{constructor(e){super(),this.issues=[],this.addIssue=e=>{this.issues=[...this.issues,e]},this.addIssues=(e=[])=>{this.issues=[...this.issues,...e]};const t=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,t):this.__proto__=t,this.name="ZodError",this.issues=e}get errors(){return this.issues}format(e){const t=e||function(e){return e.message},s={_errors:[]},n=e=>{for(const r of e.issues)if("invalid_union"===r.code)r.unionErrors.map(n);else if("invalid_return_type"===r.code)n(r.returnTypeError);else if("invalid_arguments"===r.code)n(r.argumentsError);else if(0===r.path.length)s._errors.push(t(r));else{let e=s,n=0;for(;n<r.path.length;){const s=r.path[n];n===r.path.length-1?(e[s]=e[s]||{_errors:[]},e[s]._errors.push(t(r))):e[s]=e[s]||{_errors:[]},e=e[s],n++}}};return n(this),s}toString(){return this.message}get message(){return JSON.stringify(this.issues,null,2)}get isEmpty(){return 0===this.issues.length}flatten(e=(e=>e.message)){const t={},s=[];for(const n of this.issues)n.path.length>0?(t[n.path[0]]=t[n.path[0]]||[],t[n.path[0]].push(e(n))):s.push(e(n));return{formErrors:s,fieldErrors:t}}get formErrors(){return this.flatten()}}e.ZodError=s,s.create=e=>new s(e);e.defaultErrorMap=(s,n)=>{let r;switch(s.code){case e.ZodIssueCode.invalid_type:r=s.received===t.ZodParsedType.undefined?"Required":`Expected ${s.expected}, received ${s.received}`;break;case e.ZodIssueCode.invalid_literal:r=`Invalid literal value, expected ${JSON.stringify(s.expected)}`;break;case e.ZodIssueCode.unrecognized_keys:r=`Unrecognized key(s) in object: ${t.util.joinValues(s.keys,", ")}`;break;case e.ZodIssueCode.invalid_union:r="Invalid input";break;case e.ZodIssueCode.invalid_union_discriminator:r=`Invalid discriminator value. Expected ${t.util.joinValues(s.options)}`;break;case e.ZodIssueCode.invalid_enum_value:r=`Invalid enum value. Expected ${t.util.joinValues(s.options)}, received '${s.received}'`;break;case e.ZodIssueCode.invalid_arguments:r="Invalid function arguments";break;case e.ZodIssueCode.invalid_return_type:r="Invalid function return type";break;case e.ZodIssueCode.invalid_date:r="Invalid date";break;case e.ZodIssueCode.invalid_string:r="regex"!==s.validation?`Invalid ${s.validation}`:"Invalid";break;case e.ZodIssueCode.too_small:r="array"===s.type?`Array must contain ${s.inclusive?"at least":"more than"} ${s.minimum} element(s)`:"string"===s.type?`String must contain ${s.inclusive?"at least":"over"} ${s.minimum} character(s)`:"number"===s.type?`Number must be greater than ${s.inclusive?"or equal to ":""}${s.minimum}`:"Invalid input";break;case e.ZodIssueCode.too_big:r="array"===s.type?`Array must contain ${s.inclusive?"at most":"less than"} ${s.maximum} element(s)`:"string"===s.type?`String must contain ${s.inclusive?"at most":"under"} ${s.maximum} character(s)`:"number"===s.type?`Number must be less than ${s.inclusive?"or equal to ":""}${s.maximum}`:"Invalid input";break;case e.ZodIssueCode.custom:r="Invalid input";break;case e.ZodIssueCode.invalid_intersection_types:r="Intersection results could not be merged";break;case e.ZodIssueCode.not_multiple_of:r=`Number must be a multiple of ${s.multipleOf}`;break;default:r=n.defaultError,t.util.assertNever(s)}return{message:r}},e.overrideErrorMap=e.defaultErrorMap;e.setErrorMap=t=>{e.overrideErrorMap=t}}(De),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.isAsync=e.isValid=e.isDirty=e.isAborted=e.OK=e.DIRTY=e.INVALID=e.ParseStatus=e.addIssueToContext=e.EMPTY_PATH=e.makeIssue=void 0;const t=De;e.makeIssue=e=>{const{data:t,path:s,errorMaps:n,issueData:r}=e,a=[...s,...r.path||[]],o={...r,path:a};let i="";const d=n.filter((e=>!!e)).slice().reverse();for(const e of d)i=e(o,{data:t,defaultError:i}).message;return{...r,path:a,message:r.message||i}},e.EMPTY_PATH=[],e.addIssueToContext=function(s,n){const r=e.makeIssue({issueData:n,data:s.data,path:s.path,errorMaps:[s.common.contextualErrorMap,s.schemaErrorMap,t.overrideErrorMap,t.defaultErrorMap].filter((e=>!!e))});s.common.issues.push(r)};class s{constructor(){this.value="valid"}dirty(){"valid"===this.value&&(this.value="dirty")}abort(){"aborted"!==this.value&&(this.value="aborted")}static mergeArray(t,s){const n=[];for(const r of s){if("aborted"===r.status)return e.INVALID;"dirty"===r.status&&t.dirty(),n.push(r.value)}return{status:t.value,value:n}}static async mergeObjectAsync(e,t){const n=[];for(const e of t)n.push({key:await e.key,value:await e.value});return s.mergeObjectSync(e,n)}static mergeObjectSync(t,s){const n={};for(const r of s){const{key:s,value:a}=r;if("aborted"===s.status)return e.INVALID;if("aborted"===a.status)return e.INVALID;"dirty"===s.status&&t.dirty(),"dirty"===a.status&&t.dirty(),(void 0!==a.value||r.alwaysSet)&&(n[s.value]=a.value)}return{status:t.value,value:n}}}e.ParseStatus=s,e.INVALID=Object.freeze({status:"aborted"});e.DIRTY=e=>({status:"dirty",value:e});e.OK=e=>({status:"valid",value:e});e.isAborted=e=>"aborted"===e.status;e.isDirty=e=>"dirty"===e.status;e.isValid=e=>"valid"===e.status;e.isAsync=e=>void 0!==typeof Promise&&e instanceof Promise}(Ne);var ke={};Object.defineProperty(ke,"__esModule",{value:!0});var ze={},Le={};!function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.errorUtil=void 0,function(e){e.errToObj=e=>"string"==typeof e?{message:e}:e||{},e.toString=e=>"string"==typeof e?e:null==e?void 0:e.message}(e.errorUtil||(e.errorUtil={}))}(Le),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.intersection=e.instanceof=e.function=e.enum=e.effect=e.discriminatedUnion=e.date=e.boolean=e.bigint=e.array=e.any=e.ZodFirstPartyTypeKind=e.late=e.ZodSchema=e.Schema=e.custom=e.ZodNaN=e.ZodDefault=e.ZodNullable=e.ZodOptional=e.ZodTransformer=e.ZodEffects=e.ZodPromise=e.ZodNativeEnum=e.ZodEnum=e.ZodLiteral=e.ZodLazy=e.ZodFunction=e.ZodSet=e.ZodMap=e.ZodRecord=e.ZodTuple=e.ZodIntersection=e.ZodDiscriminatedUnion=e.ZodUnion=e.ZodObject=e.objectUtil=e.ZodArray=e.ZodVoid=e.ZodNever=e.ZodUnknown=e.ZodAny=e.ZodNull=e.ZodUndefined=e.ZodDate=e.ZodBoolean=e.ZodBigInt=e.ZodNumber=e.ZodString=e.ZodType=void 0,e.void=e.unknown=e.union=e.undefined=e.tuple=e.transformer=e.string=e.strictObject=e.set=e.record=e.promise=e.preprocess=e.ostring=e.optional=e.onumber=e.oboolean=e.object=e.number=e.nullable=e.null=e.never=e.nativeEnum=e.nan=e.map=e.literal=e.lazy=void 0;const t=Le,s=Ne,n=je,r=De;class a{constructor(e,t,s,n){this.parent=e,this.data=t,this._path=s,this._key=n}get path(){return this._path.concat(this._key)}}const o=(e,t)=>{if(s.isValid(t))return{success:!0,data:t.value};if(!e.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,error:new r.ZodError(e.common.issues)}};function i(e){if(!e)return{};const{errorMap:t,invalid_type_error:s,required_error:n,description:r}=e;if(t&&(s||n))throw new Error('Can\'t use "invalid" or "required" in conjunction with custom error map.');if(t)return{errorMap:t,description:r};return{errorMap:(t,s)=>"invalid_type"!==t.code?{message:s.defaultError}:void 0===s.data&&n?{message:n}:e.invalid_type_error?{message:e.invalid_type_error}:{message:s.defaultError},description:r}}class d{constructor(e){this.spa=this.safeParseAsync,this.superRefine=this._refinement,this._def=e,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.default=this.default.bind(this),this.describe=this.describe.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this)}get description(){return this._def.description}_getType(e){return n.getParsedType(e.data)}_getOrReturnCtx(e,t){return t||{common:e.parent.common,data:e.data,parsedType:n.getParsedType(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}_processInputParams(e){return{status:new s.ParseStatus,ctx:{common:e.parent.common,data:e.data,parsedType:n.getParsedType(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}}_parseSync(e){const t=this._parse(e);if(s.isAsync(t))throw new Error("Synchronous parse encountered promise.");return t}_parseAsync(e){const t=this._parse(e);return Promise.resolve(t)}parse(e,t){const s=this.safeParse(e,t);if(s.success)return s.data;throw s.error}safeParse(e,t){var s;const r={common:{issues:[],async:null!==(s=null==t?void 0:t.async)&&void 0!==s&&s,contextualErrorMap:null==t?void 0:t.errorMap},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:n.getParsedType(e)},a=this._parseSync({data:e,path:r.path,parent:r});return o(r,a)}async parseAsync(e,t){const s=await this.safeParseAsync(e,t);if(s.success)return s.data;throw s.error}async safeParseAsync(e,t){const r={common:{issues:[],contextualErrorMap:null==t?void 0:t.errorMap,async:!0},path:(null==t?void 0:t.path)||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:n.getParsedType(e)},a=this._parse({data:e,path:[],parent:r}),i=await(s.isAsync(a)?a:Promise.resolve(a));return o(r,i)}refine(e,t){const s=e=>"string"==typeof t||void 0===t?{message:t}:"function"==typeof t?t(e):t;return this._refinement(((t,n)=>{const a=e(t),o=()=>n.addIssue({code:r.ZodIssueCode.custom,...s(t)});return"undefined"!=typeof Promise&&a instanceof Promise?a.then((e=>!!e||(o(),!1))):!!a||(o(),!1)}))}refinement(e,t){return this._refinement(((s,n)=>!!e(s)||(n.addIssue("function"==typeof t?t(s,n):t),!1)))}_refinement(e){return new F({schema:this,typeName:G.ZodEffects,effect:{type:"refinement",refinement:e}})}optional(){return K.create(this)}nullable(){return $.create(this)}nullish(){return this.optional().nullable()}array(){return I.create(this)}promise(){return U.create(this)}or(e){return S.create([this,e])}and(e){return N.create(this,e)}transform(e){return new F({schema:this,typeName:G.ZodEffects,effect:{type:"transform",transform:e}})}default(e){return new J({innerType:this,defaultValue:"function"==typeof e?e:()=>e,typeName:G.ZodDefault})}describe(e){return new(0,this.constructor)({...this._def,description:e})}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}}e.ZodType=d,e.Schema=d,e.ZodSchema=d;const c=/^c[^\s-]{8,}$/i,u=/^([a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[a-f0-9]{4}-[a-f0-9]{12}|00000000-0000-0000-0000-000000000000)$/i,l=/^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;class p extends d{constructor(){super(...arguments),this._regex=(e,s,n)=>this.refinement((t=>e.test(t)),{validation:s,code:r.ZodIssueCode.invalid_string,...t.errorUtil.errToObj(n)}),this.nonempty=e=>this.min(1,t.errorUtil.errToObj(e)),this.trim=()=>new p({...this._def,checks:[...this._def.checks,{kind:"trim"}]})}_parse(e){if(this._getType(e)!==n.ZodParsedType.string){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.string,received:t.parsedType}),s.INVALID}const t=new s.ParseStatus;let a;for(const o of this._def.checks)if("min"===o.kind)e.data.length<o.value&&(a=this._getOrReturnCtx(e,a),s.addIssueToContext(a,{code:r.ZodIssueCode.too_small,minimum:o.value,type:"string",inclusive:!0,message:o.message}),t.dirty());else if("max"===o.kind)e.data.length>o.value&&(a=this._getOrReturnCtx(e,a),s.addIssueToContext(a,{code:r.ZodIssueCode.too_big,maximum:o.value,type:"string",inclusive:!0,message:o.message}),t.dirty());else if("email"===o.kind)l.test(e.data)||(a=this._getOrReturnCtx(e,a),s.addIssueToContext(a,{validation:"email",code:r.ZodIssueCode.invalid_string,message:o.message}),t.dirty());else if("uuid"===o.kind)u.test(e.data)||(a=this._getOrReturnCtx(e,a),s.addIssueToContext(a,{validation:"uuid",code:r.ZodIssueCode.invalid_string,message:o.message}),t.dirty());else if("cuid"===o.kind)c.test(e.data)||(a=this._getOrReturnCtx(e,a),s.addIssueToContext(a,{validation:"cuid",code:r.ZodIssueCode.invalid_string,message:o.message}),t.dirty());else if("url"===o.kind)try{new URL(e.data)}catch(n){a=this._getOrReturnCtx(e,a),s.addIssueToContext(a,{validation:"url",code:r.ZodIssueCode.invalid_string,message:o.message}),t.dirty()}else if("regex"===o.kind){o.regex.lastIndex=0;o.regex.test(e.data)||(a=this._getOrReturnCtx(e,a),s.addIssueToContext(a,{validation:"regex",code:r.ZodIssueCode.invalid_string,message:o.message}),t.dirty())}else"trim"===o.kind?e.data=e.data.trim():n.util.assertNever(o);return{status:t.value,value:e.data}}_addCheck(e){return new p({...this._def,checks:[...this._def.checks,e]})}email(e){return this._addCheck({kind:"email",...t.errorUtil.errToObj(e)})}url(e){return this._addCheck({kind:"url",...t.errorUtil.errToObj(e)})}uuid(e){return this._addCheck({kind:"uuid",...t.errorUtil.errToObj(e)})}cuid(e){return this._addCheck({kind:"cuid",...t.errorUtil.errToObj(e)})}regex(e,s){return this._addCheck({kind:"regex",regex:e,...t.errorUtil.errToObj(s)})}min(e,s){return this._addCheck({kind:"min",value:e,...t.errorUtil.errToObj(s)})}max(e,s){return this._addCheck({kind:"max",value:e,...t.errorUtil.errToObj(s)})}length(e,t){return this.min(e,t).max(e,t)}get isEmail(){return!!this._def.checks.find((e=>"email"===e.kind))}get isURL(){return!!this._def.checks.find((e=>"url"===e.kind))}get isUUID(){return!!this._def.checks.find((e=>"uuid"===e.kind))}get isCUID(){return!!this._def.checks.find((e=>"cuid"===e.kind))}get minLength(){let e=-1/0;return this._def.checks.map((t=>{"min"===t.kind&&(null===e||t.value>e)&&(e=t.value)})),e}get maxLength(){let e=null;return this._def.checks.map((t=>{"max"===t.kind&&(null===e||t.value<e)&&(e=t.value)})),e}}function m(e,t){const s=(e.toString().split(".")[1]||"").length,n=(t.toString().split(".")[1]||"").length,r=s>n?s:n;return parseInt(e.toFixed(r).replace(".",""))%parseInt(t.toFixed(r).replace(".",""))/Math.pow(10,r)}e.ZodString=p,p.create=e=>new p({checks:[],typeName:G.ZodString,...i(e)});class h extends d{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(e){if(this._getType(e)!==n.ZodParsedType.number){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.number,received:t.parsedType}),s.INVALID}let t;const a=new s.ParseStatus;for(const o of this._def.checks)if("int"===o.kind)n.util.isInteger(e.data)||(t=this._getOrReturnCtx(e,t),s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:"integer",received:"float",message:o.message}),a.dirty());else if("min"===o.kind){(o.inclusive?e.data<o.value:e.data<=o.value)&&(t=this._getOrReturnCtx(e,t),s.addIssueToContext(t,{code:r.ZodIssueCode.too_small,minimum:o.value,type:"number",inclusive:o.inclusive,message:o.message}),a.dirty())}else if("max"===o.kind){(o.inclusive?e.data>o.value:e.data>=o.value)&&(t=this._getOrReturnCtx(e,t),s.addIssueToContext(t,{code:r.ZodIssueCode.too_big,maximum:o.value,type:"number",inclusive:o.inclusive,message:o.message}),a.dirty())}else"multipleOf"===o.kind?0!==m(e.data,o.value)&&(t=this._getOrReturnCtx(e,t),s.addIssueToContext(t,{code:r.ZodIssueCode.not_multiple_of,multipleOf:o.value,message:o.message}),a.dirty()):n.util.assertNever(o);return{status:a.value,value:e.data}}gte(e,s){return this.setLimit("min",e,!0,t.errorUtil.toString(s))}gt(e,s){return this.setLimit("min",e,!1,t.errorUtil.toString(s))}lte(e,s){return this.setLimit("max",e,!0,t.errorUtil.toString(s))}lt(e,s){return this.setLimit("max",e,!1,t.errorUtil.toString(s))}setLimit(e,s,n,r){return new h({...this._def,checks:[...this._def.checks,{kind:e,value:s,inclusive:n,message:t.errorUtil.toString(r)}]})}_addCheck(e){return new h({...this._def,checks:[...this._def.checks,e]})}int(e){return this._addCheck({kind:"int",message:t.errorUtil.toString(e)})}positive(e){return this._addCheck({kind:"min",value:0,inclusive:!1,message:t.errorUtil.toString(e)})}negative(e){return this._addCheck({kind:"max",value:0,inclusive:!1,message:t.errorUtil.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:0,inclusive:!0,message:t.errorUtil.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:0,inclusive:!0,message:t.errorUtil.toString(e)})}multipleOf(e,s){return this._addCheck({kind:"multipleOf",value:e,message:t.errorUtil.toString(s)})}get minValue(){let e=null;for(const t of this._def.checks)"min"===t.kind&&(null===e||t.value>e)&&(e=t.value);return e}get maxValue(){let e=null;for(const t of this._def.checks)"max"===t.kind&&(null===e||t.value<e)&&(e=t.value);return e}get isInt(){return!!this._def.checks.find((e=>"int"===e.kind))}}e.ZodNumber=h,h.create=e=>new h({checks:[],typeName:G.ZodNumber,...i(e)});class g extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.bigint){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.bigint,received:t.parsedType}),s.INVALID}return s.OK(e.data)}}e.ZodBigInt=g,g.create=e=>new g({typeName:G.ZodBigInt,...i(e)});class f extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.boolean){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.boolean,received:t.parsedType}),s.INVALID}return s.OK(e.data)}}e.ZodBoolean=f,f.create=e=>new f({typeName:G.ZodBoolean,...i(e)});class y extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.date){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.date,received:t.parsedType}),s.INVALID}if(isNaN(e.data.getTime())){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_date}),s.INVALID}return{status:"valid",value:new Date(e.data.getTime())}}}e.ZodDate=y,y.create=e=>new y({typeName:G.ZodDate,...i(e)});class v extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.undefined){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.undefined,received:t.parsedType}),s.INVALID}return s.OK(e.data)}}e.ZodUndefined=v,v.create=e=>new v({typeName:G.ZodUndefined,...i(e)});class x extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.null){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.null,received:t.parsedType}),s.INVALID}return s.OK(e.data)}}e.ZodNull=x,x.create=e=>new x({typeName:G.ZodNull,...i(e)});class C extends d{constructor(){super(...arguments),this._any=!0}_parse(e){return s.OK(e.data)}}e.ZodAny=C,C.create=e=>new C({typeName:G.ZodAny,...i(e)});class w extends d{constructor(){super(...arguments),this._unknown=!0}_parse(e){return s.OK(e.data)}}e.ZodUnknown=w,w.create=e=>new w({typeName:G.ZodUnknown,...i(e)});class b extends d{_parse(e){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.never,received:t.parsedType}),s.INVALID}}e.ZodNever=b,b.create=e=>new b({typeName:G.ZodNever,...i(e)});class _ extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.undefined){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.void,received:t.parsedType}),s.INVALID}return s.OK(e.data)}}e.ZodVoid=_,_.create=e=>new _({typeName:G.ZodVoid,...i(e)});class I extends d{_parse(e){const{ctx:t,status:o}=this._processInputParams(e),i=this._def;if(t.parsedType!==n.ZodParsedType.array)return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.array,received:t.parsedType}),s.INVALID;if(null!==i.minLength&&t.data.length<i.minLength.value&&(s.addIssueToContext(t,{code:r.ZodIssueCode.too_small,minimum:i.minLength.value,type:"array",inclusive:!0,message:i.minLength.message}),o.dirty()),null!==i.maxLength&&t.data.length>i.maxLength.value&&(s.addIssueToContext(t,{code:r.ZodIssueCode.too_big,maximum:i.maxLength.value,type:"array",inclusive:!0,message:i.maxLength.message}),o.dirty()),t.common.async)return Promise.all(t.data.map(((e,s)=>i.type._parseAsync(new a(t,e,t.path,s))))).then((e=>s.ParseStatus.mergeArray(o,e)));const d=t.data.map(((e,s)=>i.type._parseSync(new a(t,e,t.path,s))));return s.ParseStatus.mergeArray(o,d)}get element(){return this._def.type}min(e,s){return new I({...this._def,minLength:{value:e,message:t.errorUtil.toString(s)}})}max(e,s){return new I({...this._def,maxLength:{value:e,message:t.errorUtil.toString(s)}})}length(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}var T;e.ZodArray=I,I.create=(e,t)=>new I({type:e,minLength:null,maxLength:null,typeName:G.ZodArray,...i(t)}),function(e){e.mergeShapes=(e,t)=>({...e,...t})}(T=e.objectUtil||(e.objectUtil={}));const P=e=>t=>new E({...e,shape:()=>({...e.shape(),...t})});function Z(e){if(e instanceof E){const t={};for(const s in e.shape){const n=e.shape[s];t[s]=K.create(Z(n))}return new E({...e._def,shape:()=>t})}return e instanceof I?I.create(Z(e.element)):e instanceof K?K.create(Z(e.unwrap())):e instanceof $?$.create(Z(e.unwrap())):e instanceof D?D.create(e.items.map((e=>Z(e)))):e}class E extends d{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=P(this._def),this.extend=P(this._def)}_getCached(){if(null!==this._cached)return this._cached;const e=this._def.shape(),t=n.util.objectKeys(e);return this._cached={shape:e,keys:t}}_parse(e){if(this._getType(e)!==n.ZodParsedType.object){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.object,received:t.parsedType}),s.INVALID}const{status:t,ctx:o}=this._processInputParams(e),{shape:i,keys:d}=this._getCached(),c=[];for(const e in o.data)d.includes(e)||c.push(e);const u=[];for(const e of d){const t=i[e],s=o.data[e];u.push({key:{status:"valid",value:e},value:t._parse(new a(o,s,o.path,e)),alwaysSet:e in o.data})}if(this._def.catchall instanceof b){const e=this._def.unknownKeys;if("passthrough"===e)for(const e of c)u.push({key:{status:"valid",value:e},value:{status:"valid",value:o.data[e]}});else if("strict"===e)c.length>0&&(s.addIssueToContext(o,{code:r.ZodIssueCode.unrecognized_keys,keys:c}),t.dirty());else if("strip"!==e)throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{const e=this._def.catchall;for(const t of c){const s=o.data[t];u.push({key:{status:"valid",value:t},value:e._parse(new a(o,s,o.path,t)),alwaysSet:t in o.data})}}return o.common.async?Promise.resolve().then((async()=>{const e=[];for(const t of u){const s=await t.key;e.push({key:s,value:await t.value,alwaysSet:t.alwaysSet})}return e})).then((e=>s.ParseStatus.mergeObjectSync(t,e))):s.ParseStatus.mergeObjectSync(t,u)}get shape(){return this._def.shape()}strict(e){return t.errorUtil.errToObj,new E({...this._def,unknownKeys:"strict",...void 0!==e?{errorMap:(s,n)=>{var r,a,o,i;const d=null!==(o=null===(a=(r=this._def).errorMap)||void 0===a?void 0:a.call(r,s,n).message)&&void 0!==o?o:n.defaultError;return"unrecognized_keys"===s.code?{message:null!==(i=t.errorUtil.errToObj(e).message)&&void 0!==i?i:d}:{message:d}}}:{}})}strip(){return new E({...this._def,unknownKeys:"strip"})}passthrough(){return new E({...this._def,unknownKeys:"passthrough"})}setKey(e,t){return this.augment({[e]:t})}merge(e){return new E({unknownKeys:e._def.unknownKeys,catchall:e._def.catchall,shape:()=>T.mergeShapes(this._def.shape(),e._def.shape()),typeName:G.ZodObject})}catchall(e){return new E({...this._def,catchall:e})}pick(e){const t={};return n.util.objectKeys(e).map((e=>{this.shape[e]&&(t[e]=this.shape[e])})),new E({...this._def,shape:()=>t})}omit(e){const t={};return n.util.objectKeys(this.shape).map((s=>{-1===n.util.objectKeys(e).indexOf(s)&&(t[s]=this.shape[s])})),new E({...this._def,shape:()=>t})}deepPartial(){return Z(this)}partial(e){const t={};if(e)return n.util.objectKeys(this.shape).map((s=>{-1===n.util.objectKeys(e).indexOf(s)?t[s]=this.shape[s]:t[s]=this.shape[s].optional()})),new E({...this._def,shape:()=>t});for(const e in this.shape){const s=this.shape[e];t[e]=s.optional()}return new E({...this._def,shape:()=>t})}required(){const e={};for(const t in this.shape){let s=this.shape[t];for(;s instanceof K;)s=s._def.innerType;e[t]=s}return new E({...this._def,shape:()=>e})}}e.ZodObject=E,E.create=(e,t)=>new E({shape:()=>e,unknownKeys:"strip",catchall:b.create(),typeName:G.ZodObject,...i(t)}),E.strictCreate=(e,t)=>new E({shape:()=>e,unknownKeys:"strict",catchall:b.create(),typeName:G.ZodObject,...i(t)}),E.lazycreate=(e,t)=>new E({shape:e,unknownKeys:"strip",catchall:b.create(),typeName:G.ZodObject,...i(t)});class S extends d{_parse(e){const{ctx:t}=this._processInputParams(e),n=this._def.options;if(t.common.async)return Promise.all(n.map((async e=>{const s={...t,common:{...t.common,issues:[]},parent:null};return{result:await e._parseAsync({data:t.data,path:t.path,parent:s}),ctx:s}}))).then((function(e){for(const t of e)if("valid"===t.result.status)return t.result;for(const s of e)if("dirty"===s.result.status)return t.common.issues.push(...s.ctx.common.issues),s.result;const n=e.map((e=>new r.ZodError(e.ctx.common.issues)));return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_union,unionErrors:n}),s.INVALID}));{let e;const a=[];for(const s of n){const n={...t,common:{...t.common,issues:[]},parent:null},r=s._parseSync({data:t.data,path:t.path,parent:n});if("valid"===r.status)return r;"dirty"!==r.status||e||(e={result:r,ctx:n}),n.common.issues.length&&a.push(n.common.issues)}if(e)return t.common.issues.push(...e.ctx.common.issues),e.result;const o=a.map((e=>new r.ZodError(e)));return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_union,unionErrors:o}),s.INVALID}}get options(){return this._def.options}}e.ZodUnion=S,S.create=(e,t)=>new S({options:e,typeName:G.ZodUnion,...i(t)});class M extends d{_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==n.ZodParsedType.object)return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.object,received:t.parsedType}),s.INVALID;const a=this.discriminator,o=t.data[a],i=this.options.get(o);return i?t.common.async?i._parseAsync({data:t.data,path:t.path,parent:t}):i._parseSync({data:t.data,path:t.path,parent:t}):(s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_union_discriminator,options:this.validDiscriminatorValues,path:[a]}),s.INVALID)}get discriminator(){return this._def.discriminator}get validDiscriminatorValues(){return Array.from(this.options.keys())}get options(){return this._def.options}static create(e,t,s){const n=new Map;try{t.forEach((t=>{const s=t.shape[e].value;n.set(s,t)}))}catch(e){throw new Error("The discriminator value could not be extracted from all the provided schemas")}if(n.size!==t.length)throw new Error("Some of the discriminator values are not unique");return new M({typeName:G.ZodDiscriminatedUnion,discriminator:e,options:n,...i(s)})}}function O(e,t){const s=n.getParsedType(e),r=n.getParsedType(t);if(e===t)return{valid:!0,data:e};if(s===n.ZodParsedType.object&&r===n.ZodParsedType.object){const s=n.util.objectKeys(t),r=n.util.objectKeys(e).filter((e=>-1!==s.indexOf(e))),a={...e,...t};for(const s of r){const n=O(e[s],t[s]);if(!n.valid)return{valid:!1};a[s]=n.data}return{valid:!0,data:a}}if(s===n.ZodParsedType.array&&r===n.ZodParsedType.array){if(e.length!==t.length)return{valid:!1};const s=[];for(let n=0;n<e.length;n++){const r=O(e[n],t[n]);if(!r.valid)return{valid:!1};s.push(r.data)}return{valid:!0,data:s}}return s===n.ZodParsedType.date&&r===n.ZodParsedType.date&&+e==+t?{valid:!0,data:e}:{valid:!1}}e.ZodDiscriminatedUnion=M;class N extends d{_parse(e){const{status:t,ctx:n}=this._processInputParams(e),a=(e,a)=>{if(s.isAborted(e)||s.isAborted(a))return s.INVALID;const o=O(e.value,a.value);return o.valid?((s.isDirty(e)||s.isDirty(a))&&t.dirty(),{status:t.value,value:o.data}):(s.addIssueToContext(n,{code:r.ZodIssueCode.invalid_intersection_types}),s.INVALID)};return n.common.async?Promise.all([this._def.left._parseAsync({data:n.data,path:n.path,parent:n}),this._def.right._parseAsync({data:n.data,path:n.path,parent:n})]).then((([e,t])=>a(e,t))):a(this._def.left._parseSync({data:n.data,path:n.path,parent:n}),this._def.right._parseSync({data:n.data,path:n.path,parent:n}))}}e.ZodIntersection=N,N.create=(e,t,s)=>new N({left:e,right:t,typeName:G.ZodIntersection,...i(s)});class D extends d{_parse(e){const{status:t,ctx:o}=this._processInputParams(e);if(o.parsedType!==n.ZodParsedType.array)return s.addIssueToContext(o,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.array,received:o.parsedType}),s.INVALID;if(o.data.length<this._def.items.length)return s.addIssueToContext(o,{code:r.ZodIssueCode.too_small,minimum:this._def.items.length,inclusive:!0,type:"array"}),s.INVALID;!this._def.rest&&o.data.length>this._def.items.length&&(s.addIssueToContext(o,{code:r.ZodIssueCode.too_big,maximum:this._def.items.length,inclusive:!0,type:"array"}),t.dirty());const i=o.data.map(((e,t)=>{const s=this._def.items[t]||this._def.rest;return s?s._parse(new a(o,e,o.path,t)):null})).filter((e=>!!e));return o.common.async?Promise.all(i).then((e=>s.ParseStatus.mergeArray(t,e))):s.ParseStatus.mergeArray(t,i)}get items(){return this._def.items}rest(e){return new D({...this._def,rest:e})}}e.ZodTuple=D,D.create=(e,t)=>new D({items:e,typeName:G.ZodTuple,rest:null,...i(t)});class j extends d{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){const{status:t,ctx:o}=this._processInputParams(e);if(o.parsedType!==n.ZodParsedType.object)return s.addIssueToContext(o,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.object,received:o.parsedType}),s.INVALID;const i=[],d=this._def.keyType,c=this._def.valueType;for(const e in o.data)i.push({key:d._parse(new a(o,e,o.path,e)),value:c._parse(new a(o,o.data[e],o.path,e))});return o.common.async?s.ParseStatus.mergeObjectAsync(t,i):s.ParseStatus.mergeObjectSync(t,i)}get element(){return this._def.valueType}static create(e,t,s){return new j(t instanceof d?{keyType:e,valueType:t,typeName:G.ZodRecord,...i(s)}:{keyType:p.create(),valueType:e,typeName:G.ZodRecord,...i(t)})}}e.ZodRecord=j;class k extends d{_parse(e){const{status:t,ctx:o}=this._processInputParams(e);if(o.parsedType!==n.ZodParsedType.map)return s.addIssueToContext(o,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.map,received:o.parsedType}),s.INVALID;const i=this._def.keyType,d=this._def.valueType,c=[...o.data.entries()].map((([e,t],s)=>({key:i._parse(new a(o,e,o.path,[s,"key"])),value:d._parse(new a(o,t,o.path,[s,"value"]))})));if(o.common.async){const e=new Map;return Promise.resolve().then((async()=>{for(const n of c){const r=await n.key,a=await n.value;if("aborted"===r.status||"aborted"===a.status)return s.INVALID;"dirty"!==r.status&&"dirty"!==a.status||t.dirty(),e.set(r.value,a.value)}return{status:t.value,value:e}}))}{const e=new Map;for(const n of c){const r=n.key,a=n.value;if("aborted"===r.status||"aborted"===a.status)return s.INVALID;"dirty"!==r.status&&"dirty"!==a.status||t.dirty(),e.set(r.value,a.value)}return{status:t.value,value:e}}}}e.ZodMap=k,k.create=(e,t,s)=>new k({valueType:t,keyType:e,typeName:G.ZodMap,...i(s)});class z extends d{_parse(e){const{status:t,ctx:o}=this._processInputParams(e);if(o.parsedType!==n.ZodParsedType.set)return s.addIssueToContext(o,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.set,received:o.parsedType}),s.INVALID;const i=this._def;null!==i.minSize&&o.data.size<i.minSize.value&&(s.addIssueToContext(o,{code:r.ZodIssueCode.too_small,minimum:i.minSize.value,type:"set",inclusive:!0,message:i.minSize.message}),t.dirty()),null!==i.maxSize&&o.data.size>i.maxSize.value&&(s.addIssueToContext(o,{code:r.ZodIssueCode.too_big,maximum:i.maxSize.value,type:"set",inclusive:!0,message:i.maxSize.message}),t.dirty());const d=this._def.valueType;function c(e){const n=new Set;for(const r of e){if("aborted"===r.status)return s.INVALID;"dirty"===r.status&&t.dirty(),n.add(r.value)}return{status:t.value,value:n}}const u=[...o.data.values()].map(((e,t)=>d._parse(new a(o,e,o.path,t))));return o.common.async?Promise.all(u).then((e=>c(e))):c(u)}min(e,s){return new z({...this._def,minSize:{value:e,message:t.errorUtil.toString(s)}})}max(e,s){return new z({...this._def,maxSize:{value:e,message:t.errorUtil.toString(s)}})}size(e,t){return this.min(e,t).max(e,t)}nonempty(e){return this.min(1,e)}}e.ZodSet=z,z.create=(e,t)=>new z({valueType:e,minSize:null,maxSize:null,typeName:G.ZodSet,...i(t)});class L extends d{constructor(){super(...arguments),this.validate=this.implement}_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==n.ZodParsedType.function)return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.function,received:t.parsedType}),s.INVALID;function a(e,n){return s.makeIssue({data:e,path:t.path,errorMaps:[t.common.contextualErrorMap,t.schemaErrorMap,r.overrideErrorMap,r.defaultErrorMap].filter((e=>!!e)),issueData:{code:r.ZodIssueCode.invalid_arguments,argumentsError:n}})}function o(e,n){return s.makeIssue({data:e,path:t.path,errorMaps:[t.common.contextualErrorMap,t.schemaErrorMap,r.overrideErrorMap,r.defaultErrorMap].filter((e=>!!e)),issueData:{code:r.ZodIssueCode.invalid_return_type,returnTypeError:n}})}const i={errorMap:t.common.contextualErrorMap},d=t.data;return this._def.returns instanceof U?s.OK((async(...e)=>{const t=new r.ZodError([]),s=await this._def.args.parseAsync(e,i).catch((s=>{throw t.addIssue(a(e,s)),t})),n=await d(...s);return await this._def.returns._def.type.parseAsync(n,i).catch((e=>{throw t.addIssue(o(n,e)),t}))})):s.OK(((...e)=>{const t=this._def.args.safeParse(e,i);if(!t.success)throw new r.ZodError([a(e,t.error)]);const s=d(...t.data),n=this._def.returns.safeParse(s,i);if(!n.success)throw new r.ZodError([o(s,n.error)]);return n.data}))}parameters(){return this._def.args}returnType(){return this._def.returns}args(...e){return new L({...this._def,args:D.create(e).rest(w.create())})}returns(e){return new L({...this._def,returns:e})}implement(e){return this.parse(e)}strictImplement(e){return this.parse(e)}}e.ZodFunction=L,L.create=(e,t,s)=>new L({args:e?e.rest(w.create()):D.create([]).rest(w.create()),returns:t||w.create(),typeName:G.ZodFunction,...i(s)});class R extends d{get schema(){return this._def.getter()}_parse(e){const{ctx:t}=this._processInputParams(e);return this._def.getter()._parse({data:t.data,path:t.path,parent:t})}}e.ZodLazy=R,R.create=(e,t)=>new R({getter:e,typeName:G.ZodLazy,...i(t)});class A extends d{_parse(e){if(e.data!==this._def.value){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_literal,expected:this._def.value}),s.INVALID}return{status:"valid",value:e.data}}get value(){return this._def.value}}e.ZodLiteral=A,A.create=(e,t)=>new A({value:e,typeName:G.ZodLiteral,...i(t)});class B extends d{_parse(e){if("string"!=typeof e.data){const t=this._getOrReturnCtx(e),a=this._def.values;return s.addIssueToContext(t,{expected:n.util.joinValues(a),received:t.parsedType,code:r.ZodIssueCode.invalid_type}),s.INVALID}if(-1===this._def.values.indexOf(e.data)){const t=this._getOrReturnCtx(e),n=this._def.values;return s.addIssueToContext(t,{received:t.data,code:r.ZodIssueCode.invalid_enum_value,options:n}),s.INVALID}return s.OK(e.data)}get options(){return this._def.values}get enum(){const e={};for(const t of this._def.values)e[t]=t;return e}get Values(){const e={};for(const t of this._def.values)e[t]=t;return e}get Enum(){const e={};for(const t of this._def.values)e[t]=t;return e}}e.ZodEnum=B,B.create=function(e,t){return new B({values:e,typeName:G.ZodEnum,...i(t)})};class V extends d{_parse(e){const t=n.util.getValidEnumValues(this._def.values),a=this._getOrReturnCtx(e);if(a.parsedType!==n.ZodParsedType.string&&a.parsedType!==n.ZodParsedType.number){const e=n.util.objectValues(t);return s.addIssueToContext(a,{expected:n.util.joinValues(e),received:a.parsedType,code:r.ZodIssueCode.invalid_type}),s.INVALID}if(-1===t.indexOf(e.data)){const e=n.util.objectValues(t);return s.addIssueToContext(a,{received:a.data,code:r.ZodIssueCode.invalid_enum_value,options:e}),s.INVALID}return s.OK(e.data)}get enum(){return this._def.values}}e.ZodNativeEnum=V,V.create=(e,t)=>new V({values:e,typeName:G.ZodNativeEnum,...i(t)});class U extends d{_parse(e){const{ctx:t}=this._processInputParams(e);if(t.parsedType!==n.ZodParsedType.promise&&!1===t.common.async)return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.promise,received:t.parsedType}),s.INVALID;const a=t.parsedType===n.ZodParsedType.promise?t.data:Promise.resolve(t.data);return s.OK(a.then((e=>this._def.type.parseAsync(e,{path:t.path,errorMap:t.common.contextualErrorMap}))))}}e.ZodPromise=U,U.create=(e,t)=>new U({type:e,typeName:G.ZodPromise,...i(t)});class F extends d{innerType(){return this._def.schema}_parse(e){const{status:t,ctx:r}=this._processInputParams(e),a=this._def.effect||null;if("preprocess"===a.type){const e=a.transform(r.data);return r.common.async?Promise.resolve(e).then((e=>this._def.schema._parseAsync({data:e,path:r.path,parent:r}))):this._def.schema._parseSync({data:e,path:r.path,parent:r})}const o={addIssue:e=>{s.addIssueToContext(r,e),e.fatal?t.abort():t.dirty()},get path(){return r.path}};if(o.addIssue=o.addIssue.bind(o),"refinement"===a.type){const e=e=>{const t=a.refinement(e,o);if(r.common.async)return Promise.resolve(t);if(t instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return e};if(!1===r.common.async){const n=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});return"aborted"===n.status?s.INVALID:("dirty"===n.status&&t.dirty(),e(n.value),{status:t.value,value:n.value})}return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then((n=>"aborted"===n.status?s.INVALID:("dirty"===n.status&&t.dirty(),e(n.value).then((()=>({status:t.value,value:n.value}))))))}if("transform"===a.type){if(!1===r.common.async){const e=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});if(!s.isValid(e))return e;const n=a.transform(e.value,o);if(n instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:t.value,value:n}}return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then((e=>s.isValid(e)?Promise.resolve(a.transform(e.value,o)).then((e=>({status:t.value,value:e}))):e))}n.util.assertNever(a)}}e.ZodEffects=F,e.ZodTransformer=F,F.create=(e,t,s)=>new F({schema:e,typeName:G.ZodEffects,effect:t,...i(s)}),F.createWithPreprocess=(e,t,s)=>new F({schema:t,effect:{type:"preprocess",transform:e},typeName:G.ZodEffects,...i(s)});class K extends d{_parse(e){return this._getType(e)===n.ZodParsedType.undefined?s.OK(void 0):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}e.ZodOptional=K,K.create=(e,t)=>new K({innerType:e,typeName:G.ZodOptional,...i(t)});class $ extends d{_parse(e){return this._getType(e)===n.ZodParsedType.null?s.OK(null):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}}e.ZodNullable=$,$.create=(e,t)=>new $({innerType:e,typeName:G.ZodNullable,...i(t)});class J extends d{_parse(e){const{ctx:t}=this._processInputParams(e);let s=t.data;return t.parsedType===n.ZodParsedType.undefined&&(s=this._def.defaultValue()),this._def.innerType._parse({data:s,path:t.path,parent:t})}removeDefault(){return this._def.innerType}}e.ZodDefault=J,J.create=(e,t)=>new K({innerType:e,typeName:G.ZodOptional,...i(t)});class W extends d{_parse(e){if(this._getType(e)!==n.ZodParsedType.nan){const t=this._getOrReturnCtx(e);return s.addIssueToContext(t,{code:r.ZodIssueCode.invalid_type,expected:n.ZodParsedType.nan,received:t.parsedType}),s.INVALID}return{status:"valid",value:e.data}}}e.ZodNaN=W,W.create=e=>new W({typeName:G.ZodNaN,...i(e)});var G;e.custom=(e,t={},s)=>e?C.create().superRefine(((n,r)=>{if(!e(n)){const e="function"==typeof t?t(n):t,a="string"==typeof e?{message:e}:e;r.addIssue({code:"custom",...a,fatal:s})}})):C.create(),e.late={object:E.lazycreate},function(e){e.ZodString="ZodString",e.ZodNumber="ZodNumber",e.ZodNaN="ZodNaN",e.ZodBigInt="ZodBigInt",e.ZodBoolean="ZodBoolean",e.ZodDate="ZodDate",e.ZodUndefined="ZodUndefined",e.ZodNull="ZodNull",e.ZodAny="ZodAny",e.ZodUnknown="ZodUnknown",e.ZodNever="ZodNever",e.ZodVoid="ZodVoid",e.ZodArray="ZodArray",e.ZodObject="ZodObject",e.ZodUnion="ZodUnion",e.ZodDiscriminatedUnion="ZodDiscriminatedUnion",e.ZodIntersection="ZodIntersection",e.ZodTuple="ZodTuple",e.ZodRecord="ZodRecord",e.ZodMap="ZodMap",e.ZodSet="ZodSet",e.ZodFunction="ZodFunction",e.ZodLazy="ZodLazy",e.ZodLiteral="ZodLiteral",e.ZodEnum="ZodEnum",e.ZodEffects="ZodEffects",e.ZodNativeEnum="ZodNativeEnum",e.ZodOptional="ZodOptional",e.ZodNullable="ZodNullable",e.ZodDefault="ZodDefault",e.ZodPromise="ZodPromise"}(G=e.ZodFirstPartyTypeKind||(e.ZodFirstPartyTypeKind={}));e.instanceof=(t,s={message:`Input not instance of ${t.name}`})=>e.custom((e=>e instanceof t),s,!0);const q=p.create;e.string=q;const H=h.create;e.number=H;const Q=W.create;e.nan=Q;const Y=g.create;e.bigint=Y;const X=f.create;e.boolean=X;const ee=y.create;e.date=ee;const te=v.create;e.undefined=te;const se=x.create;e.null=se;const ne=C.create;e.any=ne;const re=w.create;e.unknown=re;const ae=b.create;e.never=ae;const oe=_.create;e.void=oe;const ie=I.create;e.array=ie;const de=E.create;e.object=de;const ce=E.strictCreate;e.strictObject=ce;const ue=S.create;e.union=ue;const le=M.create;e.discriminatedUnion=le;const pe=N.create;e.intersection=pe;const me=D.create;e.tuple=me;const he=j.create;e.record=he;const ge=k.create;e.map=ge;const fe=z.create;e.set=fe;const ye=L.create;e.function=ye;const ve=R.create;e.lazy=ve;const xe=A.create;e.literal=xe;const Ce=B.create;e.enum=Ce;const we=V.create;e.nativeEnum=we;const be=U.create;e.promise=be;const _e=F.create;e.effect=_e,e.transformer=_e;const Ie=K.create;e.optional=Ie;const Te=$.create;e.nullable=Te;const Pe=F.createWithPreprocess;e.preprocess=Pe;e.ostring=()=>q().optional();e.onumber=()=>H().optional();e.oboolean=()=>X().optional()}(ze),function(t){var s=e&&e.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[s]}})}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),n=e&&e.__exportStar||function(e,t){for(var n in e)"default"===n||Object.prototype.hasOwnProperty.call(t,n)||s(t,e,n)};Object.defineProperty(t,"__esModule",{value:!0}),t.ZodParsedType=t.getParsedType=void 0,n(Ne,t),n(ke,t);var r=je;Object.defineProperty(t,"getParsedType",{enumerable:!0,get:function(){return r.getParsedType}}),Object.defineProperty(t,"ZodParsedType",{enumerable:!0,get:function(){return r.ZodParsedType}}),n(ze,t),n(De,t)}(Oe),function(t){var s=e&&e.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[s]}})}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),n=e&&e.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),r=e&&e.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&s(t,e,r);return n(t,e),t},a=e&&e.__exportStar||function(e,t){for(var n in e)"default"===n||Object.prototype.hasOwnProperty.call(t,n)||s(t,e,n)};Object.defineProperty(t,"__esModule",{value:!0}),t.z=void 0;const o=r(Oe);t.z=o,a(Oe,t),t.default=o}(Me),function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.Session=e.CDP=e.Log=e.BrowsingContext=e.Script=e.CommonDataTypes=e.parseObject=void 0;const t=Me,s=P,n=(0,f.log)(f.LogType.commandParser);function r(e,t){const r=t.safeParse(e);if(r.success)return r.data;n(`Command ${JSON.stringify(e)} parse failed: ${JSON.stringify(r)}.`);const a=r.error.errors.map((e=>`${e.message} in ${e.path.map((e=>JSON.stringify(e))).join("/")}.`)).join(" ");throw new s.Message.InvalidArgumentException(a)}var a,o,i,d;e.parseObject=r,function(e){e.RemoteReferenceSchema=t.z.object({handle:t.z.string().min(1)});const s=t.z.object({type:t.z.literal("undefined")}),n=t.z.object({type:t.z.literal("null")}),r=t.z.object({type:t.z.literal("string"),value:t.z.string()}),a=t.z.enum(["NaN","-0","Infinity","+Infinity","-Infinity"]),o=t.z.object({type:t.z.literal("number"),value:t.z.union([a,t.z.number()])}),i=t.z.object({type:t.z.literal("boolean"),value:t.z.boolean()}),d=t.z.object({type:t.z.literal("bigint"),value:t.z.string()}),c=t.z.union([s,n,r,o,i,d]);e.LocalValueSchema=t.z.lazy((()=>t.z.union([c,p,m,g,f,y,v])));const u=t.z.union([e.RemoteReferenceSchema,e.LocalValueSchema]),l=t.z.array(u),p=t.z.lazy((()=>t.z.object({type:t.z.literal("array"),value:l}))),m=t.z.object({type:t.z.literal("date"),value:t.z.string().min(1)}),h=t.z.lazy((()=>t.z.tuple([t.z.union([t.z.string(),u]),u]))),g=t.z.object({type:t.z.literal("map"),value:t.z.array(h)}),f=t.z.object({type:t.z.literal("object"),value:t.z.array(h)}),y=t.z.lazy((()=>t.z.object({type:t.z.literal("regexp"),value:t.z.object({pattern:t.z.string(),flags:t.z.string().optional()})}))),v=t.z.lazy((()=>t.z.object({type:t.z.literal("set"),value:l})));e.BrowsingContextSchema=t.z.string()}(a=e.CommonDataTypes||(e.CommonDataTypes={})),function(e){const s=t.z.enum(["window","dedicated-worker","shared-worker","service-worker","worker","paint-worklet","audio-worklet","worklet"]);e.GetRealmsParametersSchema=t.z.object({context:a.BrowsingContextSchema.optional(),type:s.optional()}),e.parseGetRealmsParams=function(t){return r(t,e.GetRealmsParametersSchema)};const n=t.z.object({context:a.BrowsingContextSchema,sandbox:t.z.string().optional()}),o=t.z.object({realm:t.z.string().min(1)}),i=t.z.union([o,n]),d=t.z.enum(["root","none"]),c=t.z.object({expression:t.z.string(),awaitPromise:t.z.boolean(),target:i,resultOwnership:d.optional()});e.parseEvaluateParams=function(e){return r(e,c)};const u=t.z.object({target:i,handles:t.z.array(t.z.string())});e.parseDisownParams=function(e){return r(e,u)};const l=t.z.union([a.RemoteReferenceSchema,a.LocalValueSchema]),p=t.z.object({functionDeclaration:t.z.string(),target:i,arguments:t.z.array(l).optional(),this:l.optional(),awaitPromise:t.z.boolean(),resultOwnership:d.optional()});e.parseCallFunctionParams=function(e){return r(e,p)}}(e.Script||(e.Script={})),function(e){const s=t.z.object({maxDepth:t.z.number().int().nonnegative().max(9007199254740991).optional(),root:a.BrowsingContextSchema.optional()});e.parseGetTreeParams=function(e){return r(e,s)};const n=t.z.enum(["none","interactive","complete"]),o=t.z.object({context:a.BrowsingContextSchema,url:t.z.string().url(),wait:n.optional()});e.parseNavigateParams=function(e){return r(e,o)};const i=t.z.object({type:t.z.enum(["tab","window"]),referenceContext:a.BrowsingContextSchema.optional()});e.parseCreateParams=function(e){return r(e,i)};const d=t.z.object({context:a.BrowsingContextSchema});var c;e.parseCloseParams=function(e){return r(e,d)},(c=e.EventNames||(e.EventNames={})).LoadEvent="browsingContext.load",c.DomContentLoadedEvent="browsingContext.domContentLoaded",c.ContextCreatedEvent="browsingContext.contextCreated",c.ContextDestroyedEvent="browsingContext.contextDestroyed"}(o=e.BrowsingContext||(e.BrowsingContext={})),function(e){(e.EventNames||(e.EventNames={})).LogEntryAddedEvent="log.entryAdded"}(i=e.Log||(e.Log={})),function(e){const s=t.z.object({cdpMethod:t.z.string(),cdpParams:t.z.object({}).passthrough(),cdpSession:t.z.string().optional()});e.parseSendCommandParams=function(e){return r(e,s)};const n=t.z.object({context:a.BrowsingContextSchema});e.parseGetSessionParams=function(e){return r(e,n)},(e.EventNames||(e.EventNames={})).EventReceivedEvent="cdp.eventReceived"}(d=e.CDP||(e.CDP={})),function(e){const s=t.z.enum([o.EventNames.ContextCreatedEvent,o.EventNames.ContextDestroyedEvent,o.EventNames.DomContentLoadedEvent,o.EventNames.LoadEvent,i.EventNames.LogEntryAddedEvent,d.EventNames.EventReceivedEvent]),n=t.z.object({events:t.z.array(s),contexts:t.z.array(a.BrowsingContextSchema).optional()});e.parseSubscribeParams=function(e){return r(e,n)}}(e.Session||(e.Session={}))}(Se);
 /**
 	 * Copyright 2021 Google LLC.
 	 * Copyright (c) Microsoft Corporation.
@@ -17,5 +17,5 @@
 	 *
 	 * @license
 	 */
-Object.defineProperty(n,"__esModule",{value:!0});const qe=r,He=Ze,Ge=ue,Qe=i,Ye=Ne,Xe=We,et=(0,Qe.log)(Qe.LogType.system),tt=async function(){return await new Promise((e=>{window.setSelfTargetId=function(t){et("current target ID: "+t),e(t)}}))}();(async()=>{Xe.MapperTabPage.generatePage();const e=function(){class e{_onMessage=null;constructor(){window.cdp.onmessage=e=>{this._onMessage&&this._onMessage.call(null,e)}}setOnMessage(e){this._onMessage=e}async sendMessage(e){window.cdp.send(e)}close(){this._onMessage=null,window.cdp.onmessage=null}}return new He.CdpConnection(new e,(0,Qe.log)(Qe.LogType.cdp))}(),t=function(){class e{_onMessage=null;constructor(){window.onBidiMessage=e=>{this._onMessage&&this._onMessage.call(null,e)}}setOnMessage(e){this._onMessage=e}async sendMessage(e){window.sendBidiResponse(e)}close(){this._onMessage=null,window.onBidiMessage=null}}return new Ge.BidiServer(new e)}(),s=new Ye.EventManager(t),n=await tt;await qe.CommandProcessor.run(e,t,s,n),et("launched"),t.sendMessage(Ge.BiDiMessageEntry.createResolved({launched:!0},null))})()}();
+var Re=e&&e.__createBinding||(Object.create?function(e,t,s,n){void 0===n&&(n=s);var r=Object.getOwnPropertyDescriptor(t,s);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[s]}}),Object.defineProperty(e,n,r)}:function(e,t,s,n){void 0===n&&(n=s),e[n]=t[s]}),Ae=e&&e.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),Be=e&&e.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var s in e)"default"!==s&&Object.prototype.hasOwnProperty.call(e,s)&&Re(t,e,s);return Ae(t,e),t};Object.defineProperty(t,"__esModule",{value:!0});const Ve=s,Ue=h,Fe=f,Ke=Ze,$e=x,Je=Be(Se),We=(0,Fe.log)(Fe.LogType.system),Ge=(0,Fe.log)(Fe.LogType.bidi),qe=async function(){return await new Promise((e=>{window.setSelfTargetId=function(t){We("current target ID: "+t),e(t)}}))}();(async()=>{Ke.MapperTabPage.generatePage();const e=await qe,t=await async function(e){class t{_onMessage=null;constructor(){window.onBidiMessage=e=>{let s;Ge("received < ",e);try{s=t.#Ke(e)}catch(t){return void this.#$e(e,"invalid argument",t.message,null)}this._onMessage&&this._onMessage.call(null,s)}}setOnMessage(e){this._onMessage=e}async sendMessage(e){const t=JSON.stringify(e);Ge("sent > ",t),window.sendBidiResponse(t)}close(){this._onMessage=null,window.onBidiMessage=null}#$e(e,s,n,r){const a=t.#Je(e,s,n);r?this.sendMessage({...a,channel:r}):this.sendMessage(a)}static#We(e){return null===e?"null":Array.isArray(e)?"array":typeof e}static#Je(e,s,n){let r;try{const s=JSON.parse(e);"object"===t.#We(s)&&"id"in s&&(r=s.id)}catch{}return{id:r,error:s,message:n}}static#Ke(e){let s;try{s=JSON.parse(e)}catch{throw new Error("Cannot parse data as JSON")}const n=t.#We(s);if("object"!==n)throw new Error(`Expected JSON object but got ${n}`);const{id:r,method:a,params:o}=s,i=t.#We(r);if("number"!==i||!Number.isInteger(r)||r<0)throw new Error(`Expected unsigned integer but got ${i}`);const d=t.#We(a);if("string"!==d)throw new Error(`Expected string method but got ${d}`);const c=t.#We(o);if("object"!==c)throw new Error(`Expected object params but got ${c}`);let u=s.channel;if(void 0!==u){const e=t.#We(u);if("string"!==e)throw new Error(`Expected string channel but got ${e}`);""===u&&(u=void 0)}return{id:r,method:a,params:o,channel:u}}}return await Ue.BidiServer.createAndStart(new t,function(){class e{_onMessage=null;constructor(){window.cdp.onmessage=e=>{this._onMessage&&this._onMessage.call(null,e)}}setOnMessage(e){this._onMessage=e}async sendMessage(e){window.cdp.send(e)}close(){this._onMessage=null,window.cdp.onmessage=null}}return new Ve.CdpConnection(new e,(0,Fe.log)(Fe.LogType.cdp))}(),e,new He)}(e);We("launched"),t.emitOutgoingMessage($e.OutgoingBidiMessage.createResolved({launched:!0},null))})();class He{parseGetRealmsParams(e){return Je.Script.parseGetRealmsParams(e)}parseCallFunctionParams(e){return Je.Script.parseCallFunctionParams(e)}parseEvaluateParams(e){return Je.Script.parseEvaluateParams(e)}parseDisownParams(e){return Je.Script.parseDisownParams(e)}parseSendCommandParams(e){return Je.CDP.parseSendCommandParams(e)}parseGetSessionParams(e){return Je.CDP.parseGetSessionParams(e)}parseNavigateParams(e){return Je.BrowsingContext.parseNavigateParams(e)}parseGetTreeParams(e){return Je.BrowsingContext.parseGetTreeParams(e)}parseSubscribeParams(e){return Je.Session.parseSubscribeParams(e)}parseCreateParams(e){return Je.BrowsingContext.parseCreateParams(e)}parseCloseParams(e){return Je.BrowsingContext.parseCloseParams(e)}}}();
 //# sourceMappingURL=mapperTab.js.map
diff --git a/third_party/blink/public/devtools_protocol/browser_protocol.pdl b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
index 5d32bc7..d31963cf 100644
--- a/third_party/blink/public/devtools_protocol/browser_protocol.pdl
+++ b/third_party/blink/public/devtools_protocol/browser_protocol.pdl
@@ -8548,6 +8548,7 @@
       SameSiteCrossOriginNavigationNotOptIn
       ActivationNavigationParameterMismatch
       ActivatedInBackground
+      EmbedderHostDisallowed
 
   # Fired when a prerender attempt is completed.
   experimental event prerenderAttemptCompleted
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index cf0a01b..1c163ff 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -1676,6 +1676,13 @@
         return T("bindings::V8SetReturnValue(${info}, ${return_value}, "
                  "${isolate}, ${blink_receiver});")
 
+    if return_type.is_typedef and return_type.identifier == "SyncIteratorType":
+        # Sync iterator objects (default iterator objects, map iterator objects,
+        # and set iterator objects) are implemented as ScriptWrappable
+        # instances.
+        return T("bindings::V8SetReturnValue(${info}, ${return_value}, "
+                 "${blink_receiver});")
+
     # [CheckSecurity=ReturnValue]
     #
     # The returned object must be wrapped in its own realm instead of the
diff --git a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
index 6c4cce2e..405e6b4b 100644
--- a/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
+++ b/third_party/blink/renderer/bindings/scripts/web_idl/ir_builder.py
@@ -1021,49 +1021,46 @@
         """Constructs a set of iterator operations."""
         return {
             Identifier('forEach'):
-            self._create_operation(
-                Identifier('forEach'),
-                arguments=self._create_arguments([
-                    (Identifier('callback'),
-                     Identifier('ForEachIteratorCallback')),
-                    (Identifier('thisArg'), 'any', 'null'),
-                ]),
-                extended_attributes={
-                    'CallWith': ('ScriptState', 'ThisValue'),
-                    'RaisesException': None,
-                    'ImplementedAs': 'forEachForBinding',
-                },
-                node=node),
+            self._create_operation(Identifier('forEach'),
+                                   arguments=self._create_arguments([
+                                       (Identifier('callback'),
+                                        Identifier('ForEachIteratorCallback')),
+                                       (Identifier('thisArg'), 'any', 'null'),
+                                   ]),
+                                   extended_attributes={
+                                       'CallWith':
+                                       ('ScriptState', 'ThisValue'),
+                                       'RaisesException': None,
+                                       'ImplementedAs': 'forEachForBinding',
+                                   },
+                                   node=node),
             Identifier('entries'):
-            self._create_operation(
-                Identifier('entries'),
-                return_type=Identifier('Iterator'),
-                extended_attributes={
-                    'CallWith': 'ScriptState',
-                    'RaisesException': None,
-                    'ImplementedAs': 'entriesForBinding',
-                },
-                node=node),
+            self._create_operation(Identifier('entries'),
+                                   return_type=Identifier('SyncIteratorType'),
+                                   extended_attributes={
+                                       'CallWith': 'ScriptState',
+                                       'RaisesException': None,
+                                       'ImplementedAs': 'entriesForBinding',
+                                   },
+                                   node=node),
             Identifier('keys'):
-            self._create_operation(
-                Identifier('keys'),
-                return_type=Identifier('Iterator'),
-                extended_attributes={
-                    'CallWith': 'ScriptState',
-                    'RaisesException': None,
-                    'ImplementedAs': 'keysForBinding',
-                },
-                node=node),
+            self._create_operation(Identifier('keys'),
+                                   return_type=Identifier('SyncIteratorType'),
+                                   extended_attributes={
+                                       'CallWith': 'ScriptState',
+                                       'RaisesException': None,
+                                       'ImplementedAs': 'keysForBinding',
+                                   },
+                                   node=node),
             Identifier('values'):
-            self._create_operation(
-                Identifier('values'),
-                return_type=Identifier('Iterator'),
-                extended_attributes={
-                    'CallWith': 'ScriptState',
-                    'RaisesException': None,
-                    'ImplementedAs': 'valuesForBinding',
-                },
-                node=node),
+            self._create_operation(Identifier('values'),
+                                   return_type=Identifier('SyncIteratorType'),
+                                   extended_attributes={
+                                       'CallWith': 'ScriptState',
+                                       'RaisesException': None,
+                                       'ImplementedAs': 'valuesForBinding',
+                                   },
+                                   node=node),
         }
 
     def _create_literal_constant(self, token):
diff --git a/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc b/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc
index 7941a2c3..307a629 100644
--- a/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc
+++ b/third_party/blink/renderer/core/animation/css/css_scroll_timeline_test.cc
@@ -121,7 +121,8 @@
       }
       #element {
         color: red;
-        animation: anim 10s timeline;
+        animation: anim 10s;
+        animation-timeline: timeline;
       }
     </style>
     <div id=scroller>
diff --git a/third_party/blink/renderer/core/css/css_font_feature_values_map.cc b/third_party/blink/renderer/core/css/css_font_feature_values_map.cc
index 9344fab..94c8b7c 100644
--- a/third_party/blink/renderer/core/css/css_font_feature_values_map.cc
+++ b/third_party/blink/renderer/core/css/css_font_feature_values_map.cc
@@ -3,25 +3,23 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/core/css/css_font_feature_values_map.h"
+
 #include "third_party/blink/renderer/core/css/css_font_feature_values_rule.h"
 #include "third_party/blink/renderer/core/css/css_style_sheet.h"
 
 namespace blink {
 
 class FontFeatureValuesMapIterationSource final
-    : public PairIterable<String,
-                          IDLString,
-                          Vector<uint32_t>,
-                          IDLSequence<IDLUnsignedLong>>::IterationSource {
+    : public PairSyncIterable<CSSFontFeatureValuesMap>::IterationSource {
  public:
   FontFeatureValuesMapIterationSource(const CSSFontFeatureValuesMap& map,
                                       const FontFeatureAliases* aliases)
       : map_(map), aliases_(aliases), iterator_(aliases->begin()) {}
 
-  bool Next(ScriptState* script_state,
-            String& map_key,
-            Vector<uint32_t>& map_value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState* script_state,
+                     String& map_key,
+                     Vector<uint32_t>& map_value,
+                     ExceptionState&) override {
     if (!aliases_)
       return false;
     if (iterator_ == aliases_->end())
@@ -34,8 +32,7 @@
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(map_);
-    PairIterable<String, IDLString, Vector<uint32_t>,
-                 IDLSequence<IDLUnsignedLong>>::IterationSource::Trace(visitor);
+    PairSyncIterable<CSSFontFeatureValuesMap>::IterationSource::Trace(visitor);
   }
 
  private:
@@ -49,11 +46,8 @@
   return aliases_ ? aliases_->size() : 0u;
 }
 
-PairIterable<String,
-             IDLString,
-             Vector<uint32_t>,
-             IDLSequence<IDLUnsignedLong>>::IterationSource*
-CSSFontFeatureValuesMap::StartIteration(ScriptState*, ExceptionState&) {
+PairSyncIterable<CSSFontFeatureValuesMap>::IterationSource*
+CSSFontFeatureValuesMap::CreateIterationSource(ScriptState*, ExceptionState&) {
   return MakeGarbageCollected<FontFeatureValuesMapIterationSource>(*this,
                                                                    aliases_);
 }
diff --git a/third_party/blink/renderer/core/css/css_font_feature_values_map.h b/third_party/blink/renderer/core/css/css_font_feature_values_map.h
index be56228..b9cafa4 100644
--- a/third_party/blink/renderer/core/css/css_font_feature_values_map.h
+++ b/third_party/blink/renderer/core/css/css_font_feature_values_map.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_css_font_feature_values_map.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_union_unsignedlong_unsignedlongsequence.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_function_value.h"
@@ -18,8 +19,7 @@
 
 class CSSFontFeatureValuesRule;
 
-using FeatureValuesMaplike =
-    Maplike<String, IDLString, Vector<uint32_t>, IDLSequence<IDLUnsignedLong>>;
+using FeatureValuesMaplike = MaplikeReadAPIs<CSSFontFeatureValuesMap>;
 
 class CSSFontFeatureValuesMap : public ScriptWrappable,
                                 public FeatureValuesMaplike {
@@ -55,11 +55,8 @@
  private:
   CSSFontFeatureValuesMap() = default;
 
-  PairIterable<String,
-               IDLString,
-               Vector<uint32_t>,
-               IDLSequence<IDLUnsignedLong>>::IterationSource*
-  StartIteration(ScriptState*, ExceptionState&) override;
+  PairSyncIterable<CSSFontFeatureValuesMap>::IterationSource*
+  CreateIterationSource(ScriptState*, ExceptionState&) override;
   bool GetMapEntry(ScriptState*,
                    const String& key,
                    Vector<uint32_t>& value,
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.cc b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.cc
index 70c8f12..afb2d22 100644
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.cc
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.cc
@@ -2,11 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h"
+
 #include <memory>
 #include <utility>
 
-#include "third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h"
-
 #include "third_party/blink/renderer/core/animation/compositor_animations.h"
 #include "third_party/blink/renderer/core/css/css_custom_property_declaration.h"
 #include "third_party/blink/renderer/core/css/css_variable_data.h"
@@ -25,19 +25,16 @@
 namespace {
 
 class PaintWorkletStylePropertyMapIterationSource final
-    : public PairIterable<String,
-                          IDLString,
-                          CSSStyleValueVector,
-                          IDLSequence<CSSStyleValue>>::IterationSource {
+    : public PairSyncIterable<StylePropertyMapReadOnly>::IterationSource {
  public:
   explicit PaintWorkletStylePropertyMapIterationSource(
       HeapVector<PaintWorkletStylePropertyMap::StylePropertyMapEntry> values)
       : index_(0), values_(values) {}
 
-  bool Next(ScriptState*,
-            String& key,
-            CSSStyleValueVector& value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState*,
+                     String& key,
+                     CSSStyleValueVector& value,
+                     ExceptionState&) override {
     if (index_ >= values_.size())
       return false;
 
@@ -50,8 +47,7 @@
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(values_);
-    PairIterable<String, IDLString, CSSStyleValueVector,
-                 IDLSequence<CSSStyleValue>>::IterationSource::Trace(visitor);
+    PairSyncIterable<StylePropertyMapReadOnly>::IterationSource::Trace(visitor);
   }
 
  private:
@@ -197,8 +193,9 @@
 }
 
 PaintWorkletStylePropertyMap::IterationSource*
-PaintWorkletStylePropertyMap::StartIteration(ScriptState* script_state,
-                                             ExceptionState& exception_state) {
+PaintWorkletStylePropertyMap::CreateIterationSource(
+    ScriptState* script_state,
+    ExceptionState& exception_state) {
   // TODO(xidachen): implement this function. Note that the output should be
   // sorted.
   HeapVector<PaintWorkletStylePropertyMap::StylePropertyMapEntry> result;
diff --git a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h
index 2ee1527..0bcc910 100644
--- a/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h
+++ b/third_party/blink/renderer/core/css/cssom/paint_worklet_style_property_map.h
@@ -75,7 +75,8 @@
   CrossThreadData& StyleMapData() { return data_; }
 
  private:
-  IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
+  IterationSource* CreateIterationSource(ScriptState*,
+                                         ExceptionState&) override;
 
   CrossThreadData data_;
 };
diff --git a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.h b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.h
index d71bcac..e3e0ab7 100644
--- a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.h
+++ b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSSOM_STYLE_PROPERTY_MAP_READ_ONLY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_style_property_map_read_only.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/css_property_names.h"
 #include "third_party/blink/renderer/core/css/cssom/css_style_value.h"
@@ -15,10 +16,7 @@
 
 class CORE_EXPORT StylePropertyMapReadOnly
     : public ScriptWrappable,
-      public PairIterable<String,
-                          IDLString,
-                          CSSStyleValueVector,
-                          IDLSequence<CSSStyleValue>> {
+      public PairSyncIterable<StylePropertyMapReadOnly> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only_main_thread.cc b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only_main_thread.cc
index 6801a6c..e00ff97 100644
--- a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only_main_thread.cc
+++ b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only_main_thread.cc
@@ -24,20 +24,17 @@
 namespace {
 
 class StylePropertyMapIterationSource final
-    : public PairIterable<String,
-                          IDLString,
-                          CSSStyleValueVector,
-                          IDLSequence<CSSStyleValue>>::IterationSource {
+    : public PairSyncIterable<StylePropertyMapReadOnly>::IterationSource {
  public:
   explicit StylePropertyMapIterationSource(
       HeapVector<StylePropertyMapReadOnlyMainThread::StylePropertyMapEntry>
           values)
       : index_(0), values_(values) {}
 
-  bool Next(ScriptState*,
-            String& key,
-            CSSStyleValueVector& value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState*,
+                     String& key,
+                     CSSStyleValueVector& value,
+                     ExceptionState&) override {
     if (index_ >= values_.size())
       return false;
 
@@ -50,8 +47,7 @@
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(values_);
-    PairIterable<String, IDLString, CSSStyleValueVector,
-                 IDLSequence<CSSStyleValue>>::IterationSource::Trace(visitor);
+    PairSyncIterable<StylePropertyMapReadOnly>::IterationSource::Trace(visitor);
   }
 
  private:
@@ -130,8 +126,9 @@
 }
 
 StylePropertyMapReadOnlyMainThread::IterationSource*
-StylePropertyMapReadOnlyMainThread::StartIteration(ScriptState* script_state,
-                                                   ExceptionState&) {
+StylePropertyMapReadOnlyMainThread::CreateIterationSource(
+    ScriptState* script_state,
+    ExceptionState&) {
   HeapVector<StylePropertyMapReadOnlyMainThread::StylePropertyMapEntry> result;
 
   ForEachProperty([&result](const CSSPropertyName& name,
diff --git a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only_main_thread.h b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only_main_thread.h
index 22210080..43b374e6 100644
--- a/third_party/blink/renderer/core/css/cssom/style_property_map_read_only_main_thread.h
+++ b/third_party/blink/renderer/core/css/cssom/style_property_map_read_only_main_thread.h
@@ -50,7 +50,8 @@
   virtual String SerializationForShorthand(const CSSProperty&) const = 0;
 
  private:
-  IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
+  IterationSource* CreateIterationSource(ScriptState*,
+                                         ExceptionState&) override;
 
   CSSStyleValue* GetShorthandProperty(const CSSPropertyName&) const;
 };
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.cc b/third_party/blink/renderer/core/css/element_rule_collector.cc
index 5f22e2a5..d60ddb0 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector.cc
+++ b/third_party/blink/renderer/core/css/element_rule_collector.cc
@@ -752,19 +752,31 @@
                                              StyleRule* style_rule) {
   if (!css_rules)
     return nullptr;
-  CSSRule* result = nullptr;
-  for (unsigned i = 0; i < css_rules->length() && !result; ++i) {
+
+  for (unsigned i = 0; i < css_rules->length(); ++i) {
     CSSRule* css_rule = css_rules->item(i);
     if (auto* css_style_rule = DynamicTo<CSSStyleRule>(css_rule)) {
-      if (css_style_rule->GetStyleRule() == style_rule)
-        result = css_rule;
+      if (css_style_rule->GetStyleRule() == style_rule) {
+        return css_rule;
+      }
+      if (CSSRule* result =
+              FindStyleRule(css_style_rule->cssRules(), style_rule);
+          result) {
+        return result;
+      }
     } else if (auto* css_import_rule = DynamicTo<CSSImportRule>(css_rule)) {
-      result = FindStyleRule(css_import_rule->styleSheet(), style_rule);
-    } else {
-      result = FindStyleRule(css_rule->cssRules(), style_rule);
+      if (CSSRule* result =
+              FindStyleRule(css_import_rule->styleSheet(), style_rule);
+          result) {
+        return result;
+      }
+    } else if (CSSRule* result =
+                   FindStyleRule(css_rule->cssRules(), style_rule);
+               result) {
+      return result;
     }
   }
-  return result;
+  return nullptr;
 }
 
 void ElementRuleCollector::AppendCSSOMWrapperForRule(
diff --git a/third_party/blink/renderer/core/css/element_rule_collector.h b/third_party/blink/renderer/core/css/element_rule_collector.h
index 749ae727..966caec 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector.h
+++ b/third_party/blink/renderer/core/css/element_rule_collector.h
@@ -247,6 +247,10 @@
                     const CSSStyleSheet* style_sheet,
                     int style_sheet_index);
 
+  // Find the CSSRule within the CSSRuleCollection that corresponds to the
+  // incoming StyleRule. This mapping is needed because Inspector needs to
+  // interact with the CSSOM-wrappers (i.e. CSSRules) of the matched rules, but
+  // ElementRuleCollector's result is a list of StyleRules.
   template <class CSSRuleCollection>
   CSSRule* FindStyleRule(CSSRuleCollection*, StyleRule*);
   void AppendCSSOMWrapperForRule(CSSStyleSheet*, const RuleData*, wtf_size_t);
diff --git a/third_party/blink/renderer/core/css/element_rule_collector_test.cc b/third_party/blink/renderer/core/css/element_rule_collector_test.cc
index 80e4080..284e6de 100644
--- a/third_party/blink/renderer/core/css/element_rule_collector_test.cc
+++ b/third_party/blink/renderer/core/css/element_rule_collector_test.cc
@@ -6,6 +6,7 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/blink/renderer/core/css/css_style_rule.h"
 #include "third_party/blink/renderer/core/css/css_test_helpers.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
@@ -17,6 +18,7 @@
 #include "third_party/blink/renderer/core/execution_context/security_context.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
+#include "third_party/blink/renderer/core/html/html_style_element.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/core/testing/page_test_base.h"
 
@@ -96,6 +98,24 @@
     collector.CollectMatchingRules(request);
     return Vector<MatchedRule>{collector.MatchedRulesForTest()};
   }
+
+  RuleIndexList* GetMatchedCSSRuleList(Element* element,
+                                       RuleSet* rule_set,
+                                       const CSSStyleSheet* sheet) {
+    ElementResolveContext context(*element);
+    SelectorFilter filter;
+    MatchResult result;
+    ElementRuleCollector collector(context, StyleRecalcContext(), filter,
+                                   result, InsideLink(element));
+
+    MatchRequest request(rule_set, {}, sheet);
+
+    collector.SetMode(SelectorChecker::kCollectingCSSRules);
+    collector.CollectMatchingRules(request);
+    collector.SortAndTransferMatchedRules();
+
+    return collector.MatchedCSSRuleList();
+  }
 };
 
 TEST_F(ElementRuleCollectorTest, LinkMatchType) {
@@ -478,4 +498,42 @@
   EXPECT_EQ(0u, baz_rules.size());
 }
 
+TEST_F(ElementRuleCollectorTest, FindStyleRuleWithNesting) {
+  SetBodyInnerHTML(R"HTML(
+    <style id="style">
+      #foo {
+        color: green;
+        &.a { color: red; }
+        & > .b { color: navy; }
+      }
+    </style>
+    <div id="foo" class="a">
+      <div id="bar" class="b">
+      </div>
+    </div>
+  )HTML");
+  CSSStyleSheet* sheet =
+      To<HTMLStyleElement>(GetDocument().getElementById("style"))->sheet();
+
+  RuleSet* rule_set = &sheet->Contents()->GetRuleSet();
+  ASSERT_NE(nullptr, rule_set);
+
+  Element* foo = GetDocument().getElementById("foo");
+  Element* bar = GetDocument().getElementById("bar");
+  ASSERT_NE(nullptr, foo);
+  ASSERT_NE(nullptr, bar);
+
+  RuleIndexList* foo_css_rules = GetMatchedCSSRuleList(foo, rule_set, sheet);
+  ASSERT_EQ(2u, foo_css_rules->size());
+  CSSRule* foo_css_rule_1 = foo_css_rules->at(0).first;
+  EXPECT_EQ("#foo", DynamicTo<CSSStyleRule>(foo_css_rule_1)->selectorText());
+  CSSRule* foo_css_rule_2 = foo_css_rules->at(1).first;
+  EXPECT_EQ("&.a", DynamicTo<CSSStyleRule>(foo_css_rule_2)->selectorText());
+
+  RuleIndexList* bar_css_rules = GetMatchedCSSRuleList(bar, rule_set, sheet);
+  ASSERT_EQ(1u, bar_css_rules->size());
+  CSSRule* bar_css_rule_1 = bar_css_rules->at(0).first;
+  EXPECT_EQ("& > .b", DynamicTo<CSSStyleRule>(bar_css_rule_1)->selectorText());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/font_face_set.cc b/third_party/blink/renderer/core/css/font_face_set.cc
index f1ef996..b799a7b 100644
--- a/third_party/blink/renderer/core/css/font_face_set.cc
+++ b/third_party/blink/renderer/core/css/font_face_set.cc
@@ -289,17 +289,16 @@
   LoadFontCallback::Trace(visitor);
 }
 
-bool FontFaceSet::IterationSource::Next(ScriptState*,
-                                        Member<FontFace>& key,
-                                        Member<FontFace>& value,
-                                        ExceptionState&) {
+bool FontFaceSet::IterationSource::FetchNextItem(ScriptState*,
+                                                 FontFace*& value,
+                                                 ExceptionState&) {
   if (font_faces_.size() <= index_)
     return false;
-  key = value = font_faces_[index_++];
+  value = font_faces_[index_++];
   return true;
 }
 
-FontFaceSetIterable::IterationSource* FontFaceSet::StartIteration(
+FontFaceSetIterable::IterationSource* FontFaceSet::CreateIterationSource(
     ScriptState*,
     ExceptionState&) {
   // Setlike should iterate each item in insertion order, and items should
@@ -316,7 +315,7 @@
     for (const auto& font_face : non_css_connected_faces_)
       font_faces.push_back(font_face);
   }
-  return MakeGarbageCollected<IterationSource>(font_faces);
+  return MakeGarbageCollected<IterationSource>(std::move(font_faces));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/font_face_set.h b/third_party/blink/renderer/core/css/font_face_set.h
index ccae559..da85030 100644
--- a/third_party/blink/renderer/core/css/font_face_set.h
+++ b/third_party/blink/renderer/core/css/font_face_set.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_font_face_set.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/css/font_face.h"
 #include "third_party/blink/renderer/core/dom/events/event_listener.h"
@@ -25,7 +26,7 @@
 class Font;
 class FontFaceCache;
 
-using FontFaceSetIterable = SetlikeIterable<Member<FontFace>, FontFace>;
+using FontFaceSetIterable = ValueSyncIterable<FontFaceSet>;
 
 class CORE_EXPORT FontFaceSet : public EventTargetWithInlineData,
                                 public ExecutionContextClient,
@@ -104,12 +105,11 @@
 
   class IterationSource final : public FontFaceSetIterable::IterationSource {
    public:
-    explicit IterationSource(const HeapVector<Member<FontFace>>& font_faces)
-        : index_(0), font_faces_(font_faces) {}
-    bool Next(ScriptState*,
-              Member<FontFace>&,
-              Member<FontFace>&,
-              ExceptionState&) override;
+    explicit IterationSource(HeapVector<Member<FontFace>>&& font_faces)
+        : index_(0), font_faces_(std::move(font_faces)) {}
+    bool FetchNextItem(ScriptState* script_state,
+                       FontFace*& value,
+                       ExceptionState& exception_state) override;
 
     void Trace(Visitor* visitor) const override {
       visitor->Trace(font_faces_);
@@ -148,7 +148,7 @@
   };
 
  private:
-  FontFaceSetIterable::IterationSource* StartIteration(
+  FontFaceSetIterable::IterationSource* CreateIterationSource(
       ScriptState*,
       ExceptionState&) override;
 
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index 90810f5..ac70358f 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -283,8 +283,9 @@
                                     int height,
                                     MediaQueryOperator op) {
   if (value.IsRatio()) {
-    return CompareValue(static_cast<double>(width) * value.Denominator(),
-                        static_cast<double>(height) * value.Numerator(), op);
+    return CompareDoubleValue(static_cast<double>(width) * value.Denominator(),
+                              static_cast<double>(height) * value.Numerator(),
+                              op);
   }
   return false;
 }
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
index 0a67546..0c63d91 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator_test.cc
@@ -56,7 +56,10 @@
     {"screen and (color)", true},
     {"not screen and (color)", false},
     {"screen and (device-aspect-ratio: 16/9)", false},
+    {"screen and (device-aspect-ratio: 0.5/0.5)", true},
+    {"screen and (device-aspect-ratio: 1.5)", false},
     {"screen and (device-aspect-ratio: 1/1)", true},
+    {"screen and (device-aspect-ratio: calc(1/1))", true},
     {"all and (min-color: 2)", true},
     {"all and (min-color: 32)", false},
     {"all and (min-color-index: 0)", true},
@@ -96,6 +99,10 @@
     {"(display-mode: @junk browser)", false},
     {"(max-device-aspect-ratio: 4294967295/1)", true},
     {"(min-device-aspect-ratio: 1/4294967296)", true},
+    {"(max-device-aspect-ratio: 0.5)", false},
+    {"(max-device-aspect-ratio: 0.6/0.5)", true},
+    {"(min-device-aspect-ratio: 1/2)", true},
+    {"(max-device-aspect-ratio: 1.5)", true},
     {nullptr, false}  // Do not remove the terminator line.
 };
 
diff --git a/third_party/blink/renderer/core/css/media_query_exp.cc b/third_party/blink/renderer/core/css/media_query_exp.cc
index 5cce5ff9..a55c5887 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.cc
+++ b/third_party/blink/renderer/core/css/media_query_exp.cc
@@ -339,9 +339,8 @@
          "queries are currently the only case sensitive features";
 
   CSSPrimitiveValue* value =
-      css_parsing_utils::ConsumeInteger(range, context, 0);
-  if (!value && !FeatureExpectingPositiveInteger(media_feature) &&
-      !FeatureWithAspectRatio(media_feature)) {
+      css_parsing_utils::ConsumeInteger(range, context, 0 /* minimum_value */);
+  if (!value && !FeatureExpectingPositiveInteger(media_feature)) {
     value = css_parsing_utils::ConsumeNumber(
         range, context, CSSPrimitiveValue::ValueRange::kNonNegative);
   }
@@ -365,17 +364,16 @@
   // Now we have |value| as a number, length or resolution
   // Create value for media query expression that must have 1 or more values.
   if (FeatureWithAspectRatio(media_feature)) {
-    if (!value->IsInteger() || value->GetDoubleValue() == 0)
-      return absl::nullopt;
     if (!css_parsing_utils::ConsumeSlashIncludingWhitespace(range))
-      return absl::nullopt;
-    CSSPrimitiveValue* denominator =
-        css_parsing_utils::ConsumePositiveInteger(range, context);
+      return MediaQueryExpValue(value->GetDoubleValue(), 1);
+    CSSPrimitiveValue* denominator = css_parsing_utils::ConsumeNumber(
+        range, context, CSSPrimitiveValue::ValueRange::kNonNegative);
     if (!denominator)
       return absl::nullopt;
-
-    return MediaQueryExpValue(ClampTo<unsigned>(value->GetDoubleValue()),
-                              ClampTo<unsigned>(denominator->GetDoubleValue()));
+    if (value->GetDoubleValue() == 0 && denominator->GetDoubleValue() == 0)
+      return MediaQueryExpValue(1, 0);
+    return MediaQueryExpValue(value->GetDoubleValue(),
+                              denominator->GetDoubleValue());
   }
 
   if (FeatureWithValidDensity(media_feature, value)) {
diff --git a/third_party/blink/renderer/core/css/media_query_exp.h b/third_party/blink/renderer/core/css/media_query_exp.h
index 2df142a..45e1411 100644
--- a/third_party/blink/renderer/core/css/media_query_exp.h
+++ b/third_party/blink/renderer/core/css/media_query_exp.h
@@ -54,7 +54,7 @@
   explicit MediaQueryExpValue(CSSValueID id) : type_(Type::kId), id_(id) {}
   MediaQueryExpValue(double value, CSSPrimitiveValue::UnitType unit)
       : type_(Type::kNumeric), numeric_({value, unit}) {}
-  MediaQueryExpValue(unsigned numerator, unsigned denominator)
+  MediaQueryExpValue(double numerator, double denominator)
       : type_(Type::kRatio), ratio_({numerator, denominator}) {}
   explicit MediaQueryExpValue(const CSSValue& value)
       : type_(Type::kCSSValue), css_value_(&value) {}
@@ -81,12 +81,12 @@
     return numeric_.unit;
   }
 
-  unsigned Numerator() const {
+  double Numerator() const {
     DCHECK(IsRatio());
     return ratio_.numerator;
   }
 
-  unsigned Denominator() const {
+  double Denominator() const {
     DCHECK(IsRatio());
     return ratio_.denominator;
   }
@@ -158,8 +158,8 @@
       CSSPrimitiveValue::UnitType unit;
     } numeric_;
     struct {
-      unsigned numerator;
-      unsigned denominator;
+      double numerator;
+      double denominator;
     } ratio_;
   };
 };
diff --git a/third_party/blink/renderer/core/css/media_query_set_test.cc b/third_party/blink/renderer/core/css/media_query_set_test.cc
index cd5a92d..e6705114 100644
--- a/third_party/blink/renderer/core/css/media_query_set_test.cc
+++ b/third_party/blink/renderer/core/css/media_query_set_test.cc
@@ -73,7 +73,8 @@
       {"screen and (device-height: 60rem)", nullptr},
       {"screen and (device-height: 60ch)", nullptr},
       {"screen and (device-aspect-ratio: 16 / 9)", nullptr},
-      {"(device-aspect-ratio: 16.0/9.0)", "not all"},
+      {"(device-aspect-ratio: 16.1/9.0)", "(device-aspect-ratio: 16.1 / 9)"},
+      {"(device-aspect-ratio: 16.0)", "(device-aspect-ratio: 16 / 1)"},
       {"(device-aspect-ratio: 16/ 9)", "(device-aspect-ratio: 16 / 9)"},
       {"(device-aspect-ratio: 16/\r9)", "(device-aspect-ratio: 16 / 9)"},
       {"all and (color)", "(color)"},
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
index 4223dac..bf8cf41 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl.cc
@@ -943,6 +943,12 @@
   if (RuntimeEnabledFeatures::CSSNestingEnabled() &&
       parent_rule_for_nesting != nullptr) {
     // Parse the interior as if it were a style rule.
+    if (observer_) {
+      // Observe an empty rule header to ensure the observer has a new rule data
+      // on the stack for the following ConsumeDeclarationList.
+      observer_->StartRuleHeader(StyleRule::kStyle, stream.Offset());
+      observer_->EndRuleHeader(stream.Offset());
+    }
     ConsumeDeclarationList(stream, StyleRule::kStyle, parent_rule_for_nesting,
                            &rules);
     if (!parsed_properties_.empty()) {
@@ -998,6 +1004,12 @@
   if (RuntimeEnabledFeatures::CSSNestingEnabled() &&
       parent_rule_for_nesting != nullptr) {
     // Parse the interior as if it were a style rule.
+    if (observer_) {
+      // Observe an empty rule header to ensure the observer has a new rule data
+      // on the stack for the following ConsumeDeclarationList.
+      observer_->StartRuleHeader(StyleRule::kStyle, stream.Offset());
+      observer_->EndRuleHeader(stream.Offset());
+    }
     ConsumeDeclarationList(stream, StyleRule::kStyle, parent_rule_for_nesting,
                            &rules);
     if (!parsed_properties_.empty()) {
@@ -1460,6 +1472,12 @@
   if (RuntimeEnabledFeatures::CSSNestingEnabled() &&
       parent_rule_for_nesting != nullptr) {
     // Parse the interior as if it were a style rule.
+    if (observer_) {
+      // Observe an empty rule header to ensure the observer has a new rule data
+      // on the stack for the following ConsumeDeclarationList.
+      observer_->StartRuleHeader(StyleRule::kStyle, stream.Offset());
+      observer_->EndRuleHeader(stream.Offset());
+    }
     ConsumeDeclarationList(stream, StyleRule::kStyle, parent_rule_for_nesting,
                            &rules);
     if (!parsed_properties_.empty()) {
diff --git a/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc b/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc
index 86933448..7501e54a 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser_impl_test.cc
@@ -404,6 +404,31 @@
   EXPECT_EQ("& + #foo", child1->SelectorsText());
 }
 
+TEST(CSSParserImplTest, ObserveNestedMediaQuery) {
+  ScopedCSSNestingForTest enabled(true);
+  String sheet_text = R"CSS(
+    .element {
+      color: green;
+      @media (width < 1000px) {
+        color: navy;
+      }
+    }
+    )CSS";
+
+  auto* context = MakeGarbageCollected<CSSParserContext>(
+      kHTMLStandardMode, SecureContextMode::kInsecureContext);
+  auto* sheet = MakeGarbageCollected<StyleSheetContents>(context);
+  TestCSSParserObserver test_css_parser_observer;
+  CSSParserImpl::ParseStyleSheetForInspector(sheet_text, context, sheet,
+                                             test_css_parser_observer);
+
+  EXPECT_EQ(test_css_parser_observer.rule_type_, StyleRule::RuleType::kStyle);
+  EXPECT_EQ(test_css_parser_observer.rule_header_start_, 67u);
+  EXPECT_EQ(test_css_parser_observer.rule_header_end_, 67u);
+  EXPECT_EQ(test_css_parser_observer.rule_body_start_, 67u);
+  EXPECT_EQ(test_css_parser_observer.rule_body_end_, 101u);
+}
+
 TEST(CSSParserImplTest, RemoveImportantAnnotationIfPresent) {
   struct TestCase {
     String input;
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
index 54c6ade..efe79cc 100644
--- a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
@@ -80,7 +80,9 @@
     case CSSPropertyID::kAnimationTimingFunction:
       return css_parsing_utils::ConsumeAnimationTimingFunction(range, context);
     case CSSPropertyID::kAnimationTimeline:
-      return css_parsing_utils::ConsumeAnimationTimeline(range, context);
+      // New animation-* properties are  "reset only", see kAnimationDelayEnd.
+      DCHECK(RuntimeEnabledFeatures::CSSScrollTimelineEnabled());
+      return nullptr;
     default:
       NOTREACHED();
       return nullptr;
diff --git a/third_party/blink/renderer/core/css/style_recalc_context.cc b/third_party/blink/renderer/core/css/style_recalc_context.cc
index b94bd8ce..6ee6a0f4 100644
--- a/third_party/blink/renderer/core/css/style_recalc_context.cc
+++ b/third_party/blink/renderer/core/css/style_recalc_context.cc
@@ -16,7 +16,14 @@
                                            Element* stay_within = nullptr) {
   for (auto* container = &element; container && container != stay_within;
        container = container->ParentOrShadowHostElement()) {
-    if (container->ComputedStyleRef().IsContainerForSizeContainerQueries())
+    const ComputedStyle* style = container->GetComputedStyle();
+    if (!style) {
+      // TODO(crbug.com/1400631): Eliminate all invalid calls to
+      // StyleRecalcContext::From[Inclusive]Ancestors.
+      NOTREACHED();
+      return nullptr;
+    }
+    if (style->IsContainerForSizeContainerQueries())
       return container;
   }
   return nullptr;
@@ -26,7 +33,6 @@
 
 StyleRecalcContext StyleRecalcContext::FromInclusiveAncestors(
     Element& element) {
-  DCHECK(element.GetComputedStyle());
   return StyleRecalcContext{ClosestInclusiveAncestorContainer(element)};
 }
 
diff --git a/third_party/blink/renderer/core/dom/common_definitions.idl b/third_party/blink/renderer/core/dom/common_definitions.idl
index d1d20ad2..d58e3289 100644
--- a/third_party/blink/renderer/core/dom/common_definitions.idl
+++ b/third_party/blink/renderer/core/dom/common_definitions.idl
@@ -22,3 +22,13 @@
 // Blink-specific constructor type used in many places.
 
 callback NoArgumentConstructor = any ();
+
+// Blink-specific synchronous iterator object type, which corresponds to
+// "default iterator object",
+// https://webidl.spec.whatwg.org/#dfn-default-iterator-object
+// "map iterator", and
+// https://webidl.spec.whatwg.org/#create-a-map-iterator
+// "set iterator"
+// https://webidl.spec.whatwg.org/#create-a-set-iterator
+
+typedef object SyncIteratorType;
diff --git a/third_party/blink/renderer/core/dom/css_toggle_map.cc b/third_party/blink/renderer/core/dom/css_toggle_map.cc
index 945da76..7f36b06 100644
--- a/third_party/blink/renderer/core/dom/css_toggle_map.cc
+++ b/third_party/blink/renderer/core/dom/css_toggle_map.cc
@@ -100,10 +100,10 @@
 }
 
 bool CSSToggleMap::GetMapEntry(ScriptState*,
-                               const AtomicString& key,
-                               Member<CSSToggle>& value,
+                               const String& key,
+                               CSSToggle*& value,
                                ExceptionState&) {
-  auto iterator = toggles_.find(key);
+  auto iterator = toggles_.find(AtomicString(key));
   if (iterator == toggles_.end())
     return false;
 
@@ -111,7 +111,7 @@
   return true;
 }
 
-CSSToggleMapMaplike::IterationSource* CSSToggleMap::StartIteration(
+CSSToggleMapMaplike::IterationSource* CSSToggleMap::CreateIterationSource(
     ScriptState*,
     ExceptionState&) {
   return MakeGarbageCollected<IterationSource>(*this);
@@ -126,10 +126,10 @@
   }
 }
 
-bool CSSToggleMap::IterationSource::Next(ScriptState*,
-                                         AtomicString& key,
-                                         Member<CSSToggle>& value,
-                                         ExceptionState&) {
+bool CSSToggleMap::IterationSource::FetchNextItem(ScriptState*,
+                                                  String& key,
+                                                  CSSToggle*& value,
+                                                  ExceptionState&) {
   if (index_ >= toggles_snapshot_.size())
     return false;
   value = toggles_snapshot_[index_++];
diff --git a/third_party/blink/renderer/core/dom/css_toggle_map.h b/third_party/blink/renderer/core/dom/css_toggle_map.h
index a61ef091..aaa086d 100644
--- a/third_party/blink/renderer/core/dom/css_toggle_map.h
+++ b/third_party/blink/renderer/core/dom/css_toggle_map.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_CSS_TOGGLE_MAP_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_css_toggle_map.h"
 #include "third_party/blink/renderer/core/dom/css_toggle.h"
 #include "third_party/blink/renderer/core/dom/element_rare_data_field.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -24,8 +25,7 @@
 // Represents the set of toggles on an element.
 using ToggleMap = HeapHashMap<AtomicString, Member<CSSToggle>>;
 
-using CSSToggleMapMaplike =
-    Maplike<AtomicString, IDLString, Member<CSSToggle>, CSSToggle>;
+using CSSToggleMapMaplike = MaplikeReadAPIs<CSSToggleMap>;
 
 class CORE_EXPORT CSSToggleMap : public ScriptWrappable,
                                  public CSSToggleMapMaplike,
@@ -52,20 +52,21 @@
 
  private:
   bool GetMapEntry(ScriptState*,
-                   const AtomicString& key,
-                   Member<CSSToggle>& value,
+                   const String& key,
+                   CSSToggle*& value,
                    ExceptionState&) final;
-  CSSToggleMapMaplike::IterationSource* StartIteration(ScriptState*,
-                                                       ExceptionState&) final;
+  CSSToggleMapMaplike::IterationSource* CreateIterationSource(
+      ScriptState*,
+      ExceptionState&) final;
 
   class IterationSource final : public CSSToggleMapMaplike::IterationSource {
    public:
     explicit IterationSource(const CSSToggleMap& toggle_map);
 
-    bool Next(ScriptState*,
-              AtomicString&,
-              Member<CSSToggle>&,
-              ExceptionState&) override;
+    bool FetchNextItem(ScriptState*,
+                       String&,
+                       CSSToggle*&,
+                       ExceptionState&) override;
 
     void Trace(blink::Visitor*) const override;
 
diff --git a/third_party/blink/renderer/core/fetch/headers.cc b/third_party/blink/renderer/core/fetch/headers.cc
index b33b58e1..8b45168c 100644
--- a/third_party/blink/renderer/core/fetch/headers.cc
+++ b/third_party/blink/renderer/core/fetch/headers.cc
@@ -26,10 +26,10 @@
   headers_list_ = headers_->HeaderList()->SortAndCombine();
 }
 
-bool Headers::HeadersIterationSource::Next(ScriptState* script_state,
-                                           String& key,
-                                           String& value,
-                                           ExceptionState& exception) {
+bool Headers::HeadersIterationSource::FetchNextItem(ScriptState* script_state,
+                                                    String& key,
+                                                    String& value,
+                                                    ExceptionState& exception) {
   // This simply advances an index and returns the next value if any;
   if (current_ >= headers_list_.size())
     return false;
@@ -42,10 +42,10 @@
 
 void Headers::HeadersIterationSource::Trace(Visitor* visitor) const {
   visitor->Trace(headers_);
-  PairIterable::IterationSource::Trace(visitor);
+  PairSyncIterable<Headers>::IterationSource::Trace(visitor);
 }
 
-Headers::HeadersIterationSource::~HeadersIterationSource() {}
+Headers::HeadersIterationSource::~HeadersIterationSource() = default;
 
 Headers* Headers::Create(ScriptState* script_state, ExceptionState&) {
   return MakeGarbageCollected<Headers>();
@@ -369,8 +369,9 @@
   ScriptWrappable::Trace(visitor);
 }
 
-PairIterable<String, IDLString, String, IDLString>::IterationSource*
-Headers::StartIteration(ScriptState*, ExceptionState&) {
+PairSyncIterable<Headers>::IterationSource* Headers::CreateIterationSource(
+    ScriptState*,
+    ExceptionState&) {
   auto* iter = MakeGarbageCollected<HeadersIterationSource>(this);
   iterators_.insert(iter);
   return iter;
diff --git a/third_party/blink/renderer/core/fetch/headers.h b/third_party/blink/renderer/core/fetch/headers.h
index 6739c828..bdc5fc50 100644
--- a/third_party/blink/renderer/core/fetch/headers.h
+++ b/third_party/blink/renderer/core/fetch/headers.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_HEADERS_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_headers.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/fetch/fetch_header_list.h"
@@ -15,16 +16,12 @@
 
 namespace blink {
 
-class ByteStringSequenceSequenceOrByteStringByteStringRecord;
 class ExceptionState;
 class ScriptState;
 
-using HeadersInit = ByteStringSequenceSequenceOrByteStringByteStringRecord;
-
 // http://fetch.spec.whatwg.org/#headers-class
-class CORE_EXPORT Headers final
-    : public ScriptWrappable,
-      public PairIterable<String, IDLString, String, IDLString> {
+class CORE_EXPORT Headers final : public ScriptWrappable,
+                                  public PairSyncIterable<Headers> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -81,16 +78,15 @@
 
  private:
   class HeadersIterationSource final
-      : public PairIterable<String, IDLString, String, IDLString>::
-            IterationSource {
+      : public PairSyncIterable<Headers>::IterationSource {
    public:
     explicit HeadersIterationSource(Headers* headers);
     ~HeadersIterationSource() override;
 
-    bool Next(ScriptState* script_state,
-              String& key,
-              String& value,
-              ExceptionState& exception) override;
+    bool FetchNextItem(ScriptState* script_state,
+                       String& key,
+                       String& value,
+                       ExceptionState& exception) override;
 
     void Trace(Visitor*) const override;
 
@@ -115,7 +111,8 @@
   Member<FetchHeaderList> header_list_;
   Guard guard_;
 
-  IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
+  IterationSource* CreateIterationSource(ScriptState*,
+                                         ExceptionState&) override;
 
   HeapHashSet<WeakMember<HeadersIterationSource>> iterators_;
 };
diff --git a/third_party/blink/renderer/core/highlight/highlight.cc b/third_party/blink/renderer/core/highlight/highlight.cc
index 24b843c3..4d45727 100644
--- a/third_party/blink/renderer/core/highlight/highlight.cc
+++ b/third_party/blink/renderer/core/highlight/highlight.cc
@@ -119,13 +119,12 @@
   }
 }
 
-bool Highlight::IterationSource::Next(ScriptState*,
-                                      Member<AbstractRange>& key,
-                                      Member<AbstractRange>& value,
-                                      ExceptionState&) {
+bool Highlight::IterationSource::FetchNextItem(ScriptState*,
+                                               AbstractRange*& value,
+                                               ExceptionState&) {
   if (index_ >= highlight_ranges_snapshot_.size())
     return false;
-  key = value = highlight_ranges_snapshot_[index_++];
+  value = highlight_ranges_snapshot_[index_++];
   return true;
 }
 
@@ -134,7 +133,7 @@
   HighlightSetIterable::IterationSource::Trace(visitor);
 }
 
-HighlightSetIterable::IterationSource* Highlight::StartIteration(
+HighlightSetIterable::IterationSource* Highlight::CreateIterationSource(
     ScriptState*,
     ExceptionState&) {
   return MakeGarbageCollected<IterationSource>(*this);
diff --git a/third_party/blink/renderer/core/highlight/highlight.h b/third_party/blink/renderer/core/highlight/highlight.h
index b7a3c92..540c6ff 100644
--- a/third_party/blink/renderer/core/highlight/highlight.h
+++ b/third_party/blink/renderer/core/highlight/highlight.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HIGHLIGHT_HIGHLIGHT_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_highlight.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/dom/abstract_range.h"
 #include "third_party/blink/renderer/core/dom/events/event_target.h"
@@ -16,8 +17,7 @@
 
 namespace blink {
 
-using HighlightSetIterable =
-    SetlikeIterable<Member<AbstractRange>, AbstractRange>;
+using HighlightSetIterable = ValueSyncIterable<Highlight>;
 class HighlightRegistry;
 
 class CORE_EXPORT Highlight : public EventTargetWithInlineData,
@@ -55,10 +55,7 @@
    public:
     explicit IterationSource(const Highlight& highlight);
 
-    bool Next(ScriptState*,
-              Member<AbstractRange>&,
-              Member<AbstractRange>&,
-              ExceptionState&) override;
+    bool FetchNextItem(ScriptState*, AbstractRange*&, ExceptionState&) override;
 
     void Trace(blink::Visitor*) const override;
 
@@ -67,10 +64,6 @@
     HeapVector<Member<AbstractRange>> highlight_ranges_snapshot_;
   };
 
-  HighlightSetIterable::IterationSource* StartIteration(
-      ScriptState*,
-      ExceptionState&) override;
-
   const HeapLinkedHashSet<Member<AbstractRange>>& GetRanges() const {
     return highlight_ranges_;
   }
@@ -79,6 +72,10 @@
   void DeregisterFrom(HighlightRegistry* highlight_registry);
 
  private:
+  HighlightSetIterable::IterationSource* CreateIterationSource(
+      ScriptState*,
+      ExceptionState&) override;
+
   HeapLinkedHashSet<Member<AbstractRange>> highlight_ranges_;
   int32_t priority_ = 0;
   AtomicString type_ = "highlight";
diff --git a/third_party/blink/renderer/core/highlight/highlight_registry.cc b/third_party/blink/renderer/core/highlight/highlight_registry.cc
index d3196c33..e1c42ac 100644
--- a/third_party/blink/renderer/core/highlight/highlight_registry.cc
+++ b/third_party/blink/renderer/core/highlight/highlight_registry.cc
@@ -179,10 +179,10 @@
   }
 }
 
-bool HighlightRegistry::IterationSource::Next(ScriptState*,
-                                              AtomicString& key,
-                                              Member<Highlight>& value,
-                                              ExceptionState&) {
+bool HighlightRegistry::IterationSource::FetchNextItem(ScriptState*,
+                                                       String& key,
+                                                       Highlight*& value,
+                                                       ExceptionState&) {
   if (index_ >= highlights_snapshot_.size())
     return false;
   key = highlights_snapshot_[index_]->highlight_name;
@@ -196,7 +196,7 @@
 }
 
 HighlightRegistryMapIterable::IterationSource*
-HighlightRegistry::StartIteration(ScriptState*, ExceptionState&) {
+HighlightRegistry::CreateIterationSource(ScriptState*, ExceptionState&) {
   return MakeGarbageCollected<IterationSource>(*this);
 }
 
diff --git a/third_party/blink/renderer/core/highlight/highlight_registry.h b/third_party/blink/renderer/core/highlight/highlight_registry.h
index 77a2974..c902fcf3 100644
--- a/third_party/blink/renderer/core/highlight/highlight_registry.h
+++ b/third_party/blink/renderer/core/highlight/highlight_registry.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HIGHLIGHT_HIGHLIGHT_REGISTRY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_highlight_registry.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/highlight/highlight.h"
 #include "third_party/blink/renderer/core/highlight/highlight_registry_map_entry.h"
@@ -25,8 +26,7 @@
 using HighlightRegistryMap =
     HeapLinkedHashSet<Member<HighlightRegistryMapEntry>,
                       HashTraits<Member<HighlightRegistryMapEntry>>>;
-using HighlightRegistryMapIterable =
-    Maplike<AtomicString, IDLString, Member<Highlight>, Highlight>;
+using HighlightRegistryMapIterable = MaplikeReadAPIs<HighlightRegistry>;
 class LocalFrame;
 
 class CORE_EXPORT HighlightRegistry : public ScriptWrappable,
@@ -72,10 +72,10 @@
    public:
     explicit IterationSource(const HighlightRegistry& highlight_registry);
 
-    bool Next(ScriptState*,
-              AtomicString&,
-              Member<Highlight>&,
-              ExceptionState&) override;
+    bool FetchNextItem(ScriptState* script_state,
+                       String& key,
+                       Highlight*& value,
+                       ExceptionState& exception_state) override;
 
     void Trace(blink::Visitor*) const override;
 
@@ -97,10 +97,10 @@
   }
 
   bool GetMapEntry(ScriptState*,
-                   const AtomicString& key,
-                   Member<Highlight>& value,
+                   const String& key,
+                   Highlight*& value,
                    ExceptionState&) override {
-    auto iterator = GetMapIterator(key);
+    auto iterator = GetMapIterator(AtomicString(key));
     if (iterator == highlights_.end())
       return false;
 
@@ -108,7 +108,7 @@
     return true;
   }
 
-  HighlightRegistryMapIterable::IterationSource* StartIteration(
+  HighlightRegistryMapIterable::IterationSource* CreateIterationSource(
       ScriptState*,
       ExceptionState&) override;
 };
diff --git a/third_party/blink/renderer/core/html/custom/custom_state_set.cc b/third_party/blink/renderer/core/html/custom/custom_state_set.cc
index a53083c..56f299e 100644
--- a/third_party/blink/renderer/core/html/custom/custom_state_set.cc
+++ b/third_party/blink/renderer/core/html/custom/custom_state_set.cc
@@ -20,15 +20,12 @@
     CustomStateSet::IterationSource::Trace(visitor);
   }
 
-  bool Next(ScriptState*,
-            String& out_key,
-            String& out_value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState*,
+                     String& out_value,
+                     ExceptionState&) override {
     if (index_ >= states_->list_.size())
       return false;
-    String value = states_->list_[index_++];
-    out_key = value;
-    out_value = value;
+    out_value = states_->list_[index_++];
     return true;
   }
 
@@ -115,7 +112,7 @@
   return list_.Contains(value);
 }
 
-CustomStateSet::IterationSource* CustomStateSet::StartIteration(
+CustomStateSet::IterationSource* CustomStateSet::CreateIterationSource(
     ScriptState*,
     ExceptionState&) {
   auto* iterator = MakeGarbageCollected<CustomStateIterationSource>(*this);
diff --git a/third_party/blink/renderer/core/html/custom/custom_state_set.h b/third_party/blink/renderer/core/html/custom/custom_state_set.h
index b9bcc57..458dc94 100644
--- a/third_party/blink/renderer/core/html/custom/custom_state_set.h
+++ b/third_party/blink/renderer/core/html/custom/custom_state_set.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_CUSTOM_CUSTOM_STATE_SET_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_custom_state_set.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
@@ -17,7 +18,7 @@
 
 // This class is an implementation of 'CustomStateSet' IDL interface.
 class CustomStateSet final : public ScriptWrappable,
-                             public SetlikeIterable<String, IDLString> {
+                             public ValueSyncIterable<CustomStateSet> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -39,8 +40,10 @@
   bool Has(const String& value) const;
 
  private:
-  // blink::Iterable override:
-  IterationSource* StartIteration(ScriptState* state, ExceptionState&) override;
+  // blink::ValueSyncIterable override:
+  IterationSource* CreateIterationSource(
+      ScriptState* script_state,
+      ExceptionState& exception_state) override;
 
   void InvalidateStyle() const;
 
diff --git a/third_party/blink/renderer/core/html/forms/form_data.cc b/third_party/blink/renderer/core/html/forms/form_data.cc
index b0c4687..2390341 100644
--- a/third_party/blink/renderer/core/html/forms/form_data.cc
+++ b/third_party/blink/renderer/core/html/forms/form_data.cc
@@ -50,18 +50,15 @@
 namespace {
 
 class FormDataIterationSource final
-    : public PairIterable<String,
-                          IDLString,
-                          Member<V8FormDataEntryValue>,
-                          V8FormDataEntryValue>::IterationSource {
+    : public PairSyncIterable<FormData>::IterationSource {
  public:
   FormDataIterationSource(FormData* form_data)
       : form_data_(form_data), current_(0) {}
 
-  bool Next(ScriptState* script_state,
-            String& name,
-            Member<V8FormDataEntryValue>& value,
-            ExceptionState& exception_state) override {
+  bool FetchNextItem(ScriptState* script_state,
+                     String& name,
+                     V8FormDataEntryValue*& value,
+                     ExceptionState& exception_state) override {
     if (current_ >= form_data_->size())
       return false;
 
@@ -78,8 +75,7 @@
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(form_data_);
-    PairIterable<String, IDLString, Member<V8FormDataEntryValue>,
-                 V8FormDataEntryValue>::IterationSource::Trace(visitor);
+    PairSyncIterable<FormData>::IterationSource::Trace(visitor);
   }
 
  private:
@@ -323,11 +319,9 @@
   return form_data;
 }
 
-PairIterable<String,
-             IDLString,
-             Member<V8FormDataEntryValue>,
-             V8FormDataEntryValue>::IterationSource*
-FormData::StartIteration(ScriptState*, ExceptionState&) {
+PairSyncIterable<FormData>::IterationSource* FormData::CreateIterationSource(
+    ScriptState*,
+    ExceptionState&) {
   return MakeGarbageCollected<FormDataIterationSource>(this);
 }
 
diff --git a/third_party/blink/renderer/core/html/forms/form_data.h b/third_party/blink/renderer/core/html/forms/form_data.h
index 494fa4e5..b1d0d73 100644
--- a/third_party/blink/renderer/core/html/forms/form_data.h
+++ b/third_party/blink/renderer/core/html/forms/form_data.h
@@ -32,6 +32,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_FORMS_FORM_DATA_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_form_data.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -47,12 +48,8 @@
 class HTMLFormElement;
 class ScriptState;
 
-class CORE_EXPORT FormData final
-    : public ScriptWrappable,
-      public PairIterable<String,
-                          IDLString,
-                          Member<V8FormDataEntryValue>,
-                          V8FormDataEntryValue> {
+class CORE_EXPORT FormData final : public ScriptWrappable,
+                                   public PairSyncIterable<FormData> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -109,7 +106,8 @@
 
  private:
   void SetEntry(const Entry*);
-  IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
+  IterationSource* CreateIterationSource(ScriptState*,
+                                         ExceptionState&) override;
 
   WTF::TextEncoding encoding_;
   // Entry pointers in entries_ never be nullptr.
diff --git a/third_party/blink/renderer/core/html/shadow/shadow_element_names.json5 b/third_party/blink/renderer/core/html/shadow/shadow_element_names.json5
index 6d46229..e1f69b67 100644
--- a/third_party/blink/renderer/core/html/shadow/shadow_element_names.json5
+++ b/third_party/blink/renderer/core/html/shadow/shadow_element_names.json5
@@ -102,6 +102,10 @@
       Symbol: "kPseudoMediaControlsSegmentedTrack",
     },
     {
+      name: "-webkit-calendar-picker-indicator",
+      Symbol: "kPseudoCalendarPickerIndicator",
+    },
+    {
       name: "-webkit-media-slider-container",
       Symbol: "kPseudoMediaSliderContainer",
     },
diff --git a/third_party/blink/renderer/core/timing/event_counts.cc b/third_party/blink/renderer/core/timing/event_counts.cc
index 8e8ff92..29c5674 100644
--- a/third_party/blink/renderer/core/timing/event_counts.cc
+++ b/third_party/blink/renderer/core/timing/event_counts.cc
@@ -12,18 +12,15 @@
 namespace blink {
 
 class EventCountsIterationSource final
-    : public PairIterable<AtomicString,
-                          IDLString,
-                          uint64_t,
-                          IDLUnsignedLongLong>::IterationSource {
+    : public PairSyncIterable<EventCounts>::IterationSource {
  public:
   explicit EventCountsIterationSource(const EventCounts& map)
       : map_(map), iterator_(map_->Map().begin()) {}
 
-  bool Next(ScriptState* script_state,
-            AtomicString& map_key,
-            uint64_t& map_value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState* script_state,
+                     String& map_key,
+                     uint64_t& map_value,
+                     ExceptionState&) override {
     if (iterator_ == map_->Map().end())
       return false;
     map_key = iterator_->key;
@@ -34,8 +31,7 @@
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(map_);
-    PairIterable<AtomicString, IDLString, uint64_t,
-                 IDLUnsignedLongLong>::IterationSource::Trace(visitor);
+    PairSyncIterable<EventCounts>::IterationSource::Trace(visitor);
   }
 
  private:
@@ -98,17 +94,16 @@
   }
 }
 
-PairIterable<AtomicString, IDLString, uint64_t, IDLUnsignedLongLong>::
-    IterationSource*
-    EventCounts::StartIteration(ScriptState*, ExceptionState&) {
+PairSyncIterable<EventCounts>::IterationSource*
+EventCounts::CreateIterationSource(ScriptState*, ExceptionState&) {
   return MakeGarbageCollected<EventCountsIterationSource>(*this);
 }
 
 bool EventCounts::GetMapEntry(ScriptState*,
-                              const AtomicString& key,
+                              const String& key,
                               uint64_t& value,
                               ExceptionState&) {
-  auto it = event_count_map_.find(key);
+  auto it = event_count_map_.find(AtomicString(key));
   if (it == event_count_map_.end())
     return false;
 
diff --git a/third_party/blink/renderer/core/timing/event_counts.h b/third_party/blink/renderer/core/timing/event_counts.h
index 958f866..12a90ae 100644
--- a/third_party/blink/renderer/core/timing/event_counts.h
+++ b/third_party/blink/renderer/core/timing/event_counts.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_event_counts.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -14,9 +15,8 @@
 
 namespace blink {
 
-class EventCounts final
-    : public ScriptWrappable,
-      public Maplike<AtomicString, IDLString, uint64_t, IDLUnsignedLongLong> {
+class EventCounts final : public ScriptWrappable,
+                          public MaplikeReadAPIs<EventCounts> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -40,11 +40,11 @@
 
  private:
   // Maplike implementation.
-  PairIterable<AtomicString, IDLString, uint64_t, IDLUnsignedLongLong>::
-      IterationSource*
-      StartIteration(ScriptState*, ExceptionState&) override;
+  PairSyncIterable<EventCounts>::IterationSource* CreateIterationSource(
+      ScriptState*,
+      ExceptionState&) override;
   bool GetMapEntry(ScriptState*,
-                   const AtomicString& key,
+                   const String& key,
                    uint64_t& value,
                    ExceptionState&) override;
 
diff --git a/third_party/blink/renderer/core/timing/performance_observer.cc b/third_party/blink/renderer/core/timing/performance_observer.cc
index 32fb6de..30c322e 100644
--- a/third_party/blink/renderer/core/timing/performance_observer.cc
+++ b/third_party/blink/renderer/core/timing/performance_observer.cc
@@ -80,6 +80,11 @@
     supportedEntryTypes.push_back(performance_entry_names::kPaint);
   }
   supportedEntryTypes.push_back(performance_entry_names::kResource);
+  if (RuntimeEnabledFeatures::SoftNavigationHeuristicsEnabled(
+          execution_context) &&
+      execution_context->IsWindow()) {
+    supportedEntryTypes.push_back(performance_entry_names::kSoftNavigation);
+  }
   if (RuntimeEnabledFeatures::VisibilityStateEntryEnabled() &&
       execution_context->IsWindow()) {
     supportedEntryTypes.push_back(performance_entry_names::kVisibilityState);
diff --git a/third_party/blink/renderer/core/url/url_search_params.cc b/third_party/blink/renderer/core/url/url_search_params.cc
index 3e27912..67795ce 100644
--- a/third_party/blink/renderer/core/url/url_search_params.cc
+++ b/third_party/blink/renderer/core/url/url_search_params.cc
@@ -19,16 +19,15 @@
 namespace {
 
 class URLSearchParamsIterationSource final
-    : public PairIterable<String, IDLString, String, IDLString>::
-          IterationSource {
+    : public PairSyncIterable<URLSearchParams>::IterationSource {
  public:
   explicit URLSearchParamsIterationSource(URLSearchParams* params)
       : params_(params), current_(0) {}
 
-  bool Next(ScriptState*,
-            String& key,
-            String& value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState*,
+                     String& key,
+                     String& value,
+                     ExceptionState&) override {
     if (current_ >= params_->Params().size())
       return false;
 
@@ -40,8 +39,7 @@
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(params_);
-    PairIterable<String, IDLString, String, IDLString>::IterationSource::Trace(
-        visitor);
+    PairSyncIterable<URLSearchParams>::IterationSource::Trace(visitor);
   }
 
  private:
@@ -263,8 +261,8 @@
   return EncodedFormData::Create(encoded_data.data(), encoded_data.size());
 }
 
-PairIterable<String, IDLString, String, IDLString>::IterationSource*
-URLSearchParams::StartIteration(ScriptState*, ExceptionState&) {
+PairSyncIterable<URLSearchParams>::IterationSource*
+URLSearchParams::CreateIterationSource(ScriptState*, ExceptionState&) {
   return MakeGarbageCollected<URLSearchParamsIterationSource>(this);
 }
 
diff --git a/third_party/blink/renderer/core/url/url_search_params.h b/third_party/blink/renderer/core/url/url_search_params.h
index 3a9a4ae..97bda97 100644
--- a/third_party/blink/renderer/core/url/url_search_params.h
+++ b/third_party/blink/renderer/core/url/url_search_params.h
@@ -10,6 +10,7 @@
 #include "base/dcheck_is_on.h"
 #include "base/gtest_prod_util.h"
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_sync_iterator_url_search_params.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -28,7 +29,7 @@
 
 class CORE_EXPORT URLSearchParams final
     : public ScriptWrappable,
-      public PairIterable<String, IDLString, String, IDLString> {
+      public PairSyncIterable<URLSearchParams> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -72,7 +73,8 @@
   FRIEND_TEST_ALL_PREFIXES(URLSearchParamsTest, EncodedFormData);
 
   void RunUpdateSteps();
-  IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
+  IterationSource* CreateIterationSource(ScriptState*,
+                                         ExceptionState&) override;
   void EncodeAsFormData(Vector<char>&) const;
 
   void AppendWithoutUpdate(const String& name, const String& value);
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index e94b701..52cdecd 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -411,8 +411,6 @@
                           action, orientation, text_direction);
   GetNode()->DispatchEvent(*keydown);
 
-  // TODO(crbug.com/1099069): add a brief pause between keydown and keyup?
-
   // The keydown handler may have caused the node to be removed.
   if (!GetNode())
     return;
@@ -420,7 +418,17 @@
   KeyboardEvent* keyup =
       CreateKeyboardEvent(local_dom_window, WebInputEvent::Type::kKeyUp, action,
                           orientation, text_direction);
-  GetNode()->DispatchEvent(*keyup);
+
+  // Add a 100ms delay between keydown and keyup to make events look less
+  // evidently synthesized.
+  GetDocument()
+      ->GetTaskRunner(TaskType::kUserInteraction)
+      ->PostDelayedTask(
+          FROM_HERE,
+          WTF::BindOnce(
+              [](Node* node, KeyboardEvent* evt) { node->DispatchEvent(*evt); },
+              WrapWeakPersistent(GetNode()), WrapPersistent(keyup)),
+          base::Milliseconds(100));
 }
 
 AXObject* AXNodeObject::ActiveDescendant() {
@@ -1645,6 +1653,14 @@
   if (HasContentEditableAttributeSet())
     return true;
 
+  // Certain user-agent shadow DOM elements are expected to be clickable but
+  // they do not have event listeners attached or a clickable native role. We
+  // whitelist them here.
+  if (element->ShadowPseudoId() ==
+      shadow_element_names::kPseudoCalendarPickerIndicator) {
+    return true;
+  }
+
   // Only use native roles. For ARIA elements, require a click listener.
   return ui::IsClickable(native_role_);
 }
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_map.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_map.cc
index 14efd07..b7d73bc 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_map.cc
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_map.cc
@@ -10,32 +10,29 @@
 namespace blink {
 
 class BluetoothManufacturerDataMapIterationSource final
-    : public PairIterable<uint16_t,
-                          IDLUnsignedShort,
-                          Member<DOMDataView>,
-                          DOMDataView>::IterationSource {
+    : public PairSyncIterable<BluetoothManufacturerDataMap>::IterationSource {
  public:
   explicit BluetoothManufacturerDataMapIterationSource(
       const BluetoothManufacturerDataMap& map)
       : map_(map), iterator_(map_->Map().begin()) {}
 
-  bool Next(ScriptState* script_state,
-            uint16_t& map_key,
-            Member<DOMDataView>& map_value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState* script_state,
+                     uint16_t& map_key,
+                     NotShared<DOMDataView>& map_value,
+                     ExceptionState&) override {
     if (iterator_ == map_->Map().end())
       return false;
     map_key = iterator_->key;
-    map_value =
-        BluetoothRemoteGATTUtils::ConvertWTFVectorToDataView(iterator_->value);
+    map_value = NotShared<DOMDataView>(
+        BluetoothRemoteGATTUtils::ConvertWTFVectorToDataView(iterator_->value));
     ++iterator_;
     return true;
   }
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(map_);
-    PairIterable<uint16_t, IDLUnsignedShort, Member<DOMDataView>,
-                 DOMDataView>::IterationSource::Trace(visitor);
+    PairSyncIterable<BluetoothManufacturerDataMap>::IterationSource::Trace(
+        visitor);
   }
 
  private:
@@ -50,17 +47,16 @@
 
 BluetoothManufacturerDataMap::~BluetoothManufacturerDataMap() {}
 
-PairIterable<uint16_t, IDLUnsignedShort, Member<DOMDataView>, DOMDataView>::
-    IterationSource*
-    BluetoothManufacturerDataMap::StartIteration(ScriptState*,
-                                                 ExceptionState&) {
+PairSyncIterable<BluetoothManufacturerDataMap>::IterationSource*
+BluetoothManufacturerDataMap::CreateIterationSource(ScriptState*,
+                                                    ExceptionState&) {
   return MakeGarbageCollected<BluetoothManufacturerDataMapIterationSource>(
       *this);
 }
 
 bool BluetoothManufacturerDataMap::GetMapEntry(ScriptState*,
                                                const uint16_t& key,
-                                               Member<DOMDataView>& value,
+                                               NotShared<DOMDataView>& value,
                                                ExceptionState&) {
   auto it = parameter_map_.find(key);
   if (it == parameter_map_.end())
@@ -69,7 +65,7 @@
   DOMDataView* dom_data_view =
       BluetoothRemoteGATTUtils::ConvertWTFVectorToDataView(it->value);
 
-  value = dom_data_view;
+  value = NotShared<DOMDataView>(dom_data_view);
   return true;
 }
 
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_map.h b/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_map.h
index dea1bdc..e45ff39c 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_map.h
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_manufacturer_data_map.h
@@ -7,17 +7,16 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_bluetooth_manufacturer_data_map.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_data_view.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 
 namespace blink {
 
-class BluetoothManufacturerDataMap final : public ScriptWrappable,
-                                           public Maplike<uint16_t,
-                                                          IDLUnsignedShort,
-                                                          Member<DOMDataView>,
-                                                          DOMDataView> {
+class BluetoothManufacturerDataMap final
+    : public ScriptWrappable,
+      public MaplikeReadAPIs<BluetoothManufacturerDataMap> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -33,12 +32,11 @@
   uint32_t size() const { return parameter_map_.size(); }
 
  private:
-  PairIterable<uint16_t, IDLUnsignedShort, Member<DOMDataView>, DOMDataView>::
-      IterationSource*
-      StartIteration(ScriptState*, ExceptionState&) override;
+  PairSyncIterable<BluetoothManufacturerDataMap>::IterationSource*
+  CreateIterationSource(ScriptState*, ExceptionState&) override;
   bool GetMapEntry(ScriptState*,
                    const uint16_t& key,
-                   Member<DOMDataView>&,
+                   NotShared<DOMDataView>& value,
                    ExceptionState&) override;
 
   const MapType parameter_map_;
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_service_data_map.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth_service_data_map.cc
index a8946e2..71d11b6 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_service_data_map.cc
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_service_data_map.cc
@@ -10,29 +10,28 @@
 namespace blink {
 
 class BluetoothServiceDataMapIterationSource final
-    : public PairIterable<String, IDLString, Member<DOMDataView>, DOMDataView>::
-          IterationSource {
+    : public PairSyncIterable<BluetoothServiceDataMap>::IterationSource {
  public:
-  BluetoothServiceDataMapIterationSource(const BluetoothServiceDataMap& map)
+  explicit BluetoothServiceDataMapIterationSource(
+      const BluetoothServiceDataMap& map)
       : map_(map), iterator_(map_->Map().begin()) {}
 
-  bool Next(ScriptState* script_state,
-            String& map_key,
-            Member<DOMDataView>& map_value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState* script_state,
+                     String& map_key,
+                     NotShared<DOMDataView>& map_value,
+                     ExceptionState&) override {
     if (iterator_ == map_->Map().end())
       return false;
     map_key = iterator_->key;
-    map_value =
-        BluetoothRemoteGATTUtils::ConvertWTFVectorToDataView(iterator_->value);
+    map_value = NotShared<DOMDataView>(
+        BluetoothRemoteGATTUtils::ConvertWTFVectorToDataView(iterator_->value));
     ++iterator_;
     return true;
   }
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(map_);
-    PairIterable<String, IDLString, Member<DOMDataView>,
-                 DOMDataView>::IterationSource::Trace(visitor);
+    PairSyncIterable<BluetoothServiceDataMap>::IterationSource::Trace(visitor);
   }
 
  private:
@@ -47,15 +46,14 @@
 
 BluetoothServiceDataMap::~BluetoothServiceDataMap() {}
 
-PairIterable<String, IDLString, Member<DOMDataView>, DOMDataView>::
-    IterationSource*
-    BluetoothServiceDataMap::StartIteration(ScriptState*, ExceptionState&) {
+PairSyncIterable<BluetoothServiceDataMap>::IterationSource*
+BluetoothServiceDataMap::CreateIterationSource(ScriptState*, ExceptionState&) {
   return MakeGarbageCollected<BluetoothServiceDataMapIterationSource>(*this);
 }
 
 bool BluetoothServiceDataMap::GetMapEntry(ScriptState*,
                                           const String& key,
-                                          Member<DOMDataView>& value,
+                                          NotShared<DOMDataView>& value,
                                           ExceptionState&) {
   auto it = parameter_map_.find(key);
   if (it == parameter_map_.end())
@@ -64,7 +62,7 @@
   DOMDataView* dom_data_view =
       BluetoothRemoteGATTUtils::ConvertWTFVectorToDataView(it->value);
 
-  value = dom_data_view;
+  value = NotShared<DOMDataView>(dom_data_view);
   return true;
 }
 
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth_service_data_map.h b/third_party/blink/renderer/modules/bluetooth/bluetooth_service_data_map.h
index aaaa75b..e7b2d65 100644
--- a/third_party/blink/renderer/modules/bluetooth/bluetooth_service_data_map.h
+++ b/third_party/blink/renderer/modules/bluetooth/bluetooth_service_data_map.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_BLUETOOTH_BLUETOOTH_SERVICE_DATA_MAP_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_bluetooth_service_data_map.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_data_view.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
@@ -14,7 +15,7 @@
 
 class BluetoothServiceDataMap final
     : public ScriptWrappable,
-      public Maplike<String, IDLString, Member<DOMDataView>, DOMDataView> {
+      public MaplikeReadAPIs<BluetoothServiceDataMap> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -30,12 +31,11 @@
   uint32_t size() const { return parameter_map_.size(); }
 
  private:
-  PairIterable<String, IDLString, Member<DOMDataView>, DOMDataView>::
-      IterationSource*
-      StartIteration(ScriptState*, ExceptionState&) override;
+  PairSyncIterable<BluetoothServiceDataMap>::IterationSource*
+  CreateIterationSource(ScriptState*, ExceptionState&) override;
   bool GetMapEntry(ScriptState*,
                    const String& key,
-                   Member<DOMDataView>&,
+                   NotShared<DOMDataView>& value,
                    ExceptionState&) override;
 
   const MapType parameter_map_;
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc b/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc
index f6154753..feba867 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.cc
@@ -65,17 +65,14 @@
 
 // Represents an Iterator that loops through the set of MapEntrys.
 class MapIterationSource final
-    : public PairIterable<Member<V8BufferSource>,
-                          V8BufferSource,
-                          String,
-                          IDLString>::IterationSource {
+    : public PairSyncIterable<MediaKeyStatusMap>::IterationSource {
  public:
   MapIterationSource(MediaKeyStatusMap* map) : map_(map), current_(0) {}
 
-  bool Next(ScriptState* script_state,
-            Member<V8BufferSource>& key,
-            String& value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState* script_state,
+                     V8BufferSource*& key,
+                     V8MediaKeyStatus& value,
+                     ExceptionState&) override {
     // This simply advances an index and returns the next value if any,
     // so if the iterated object is mutated values may be skipped.
     if (current_ >= map_->size())
@@ -89,8 +86,7 @@
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(map_);
-    PairIterable<Member<V8BufferSource>, V8BufferSource, String,
-                 IDLString>::IterationSource::Trace(visitor);
+    PairSyncIterable<MediaKeyStatusMap>::IterationSource::Trace(visitor);
   }
 
  private:
@@ -149,7 +145,7 @@
   return ScriptValue::From(script_state, at(index).Status());
 }
 
-MediaKeyStatusMap::IterationSource* MediaKeyStatusMap::StartIteration(
+MediaKeyStatusMap::IterationSource* MediaKeyStatusMap::CreateIterationSource(
     ScriptState*,
     ExceptionState&) {
   return MakeGarbageCollected<MapIterationSource>(this);
diff --git a/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.h b/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.h
index 53e608d..693c840e 100644
--- a/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.h
+++ b/third_party/blink/renderer/modules/encryptedmedia/media_key_status_map.h
@@ -8,6 +8,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_media_key_status_map.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_array_piece.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
@@ -22,10 +23,7 @@
 // is a keychange event, iteration order and completeness is not guaranteed
 // if the event loop runs.
 class MediaKeyStatusMap final : public ScriptWrappable,
-                                public PairIterable<Member<V8BufferSource>,
-                                                    V8BufferSource,
-                                                    String,
-                                                    IDLString> {
+                                public PairSyncIterable<MediaKeyStatusMap> {
   DEFINE_WRAPPERTYPEINFO();
 
  private:
@@ -52,8 +50,9 @@
   void Trace(Visitor*) const override;
 
  private:
-  // PairIterable<> implementation.
-  IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
+  // PairSyncIterable<> implementation.
+  IterationSource* CreateIterationSource(ScriptState*,
+                                         ExceptionState&) override;
 
   uint32_t IndexOf(const DOMArrayPiece& key_id) const;
 
diff --git a/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.cc b/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.cc
index bb537655..48b79cd0 100644
--- a/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.cc
+++ b/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.cc
@@ -7,16 +7,15 @@
 namespace blink {
 
 class KeyboardLayoutMapIterationSource final
-    : public PairIterable<String, IDLString, String, IDLString>::
-          IterationSource {
+    : public PairSyncIterable<KeyboardLayoutMap>::IterationSource {
  public:
-  KeyboardLayoutMapIterationSource(const KeyboardLayoutMap& map)
+  explicit KeyboardLayoutMapIterationSource(const KeyboardLayoutMap& map)
       : map_(map), iterator_(map_->Map().begin()) {}
 
-  bool Next(ScriptState* script_state,
-            String& map_key,
-            String& map_value,
-            ExceptionState&) override {
+  bool FetchNextItem(ScriptState* script_state,
+                     String& map_key,
+                     String& map_value,
+                     ExceptionState&) override {
     if (iterator_ == map_->Map().end())
       return false;
     map_key = iterator_->key;
@@ -27,8 +26,7 @@
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(map_);
-    PairIterable<String, IDLString, String, IDLString>::IterationSource::Trace(
-        visitor);
+    PairSyncIterable<KeyboardLayoutMap>::IterationSource::Trace(visitor);
   }
 
  private:
@@ -40,8 +38,8 @@
 KeyboardLayoutMap::KeyboardLayoutMap(const HashMap<String, String>& map)
     : layout_map_(map) {}
 
-PairIterable<String, IDLString, String, IDLString>::IterationSource*
-KeyboardLayoutMap::StartIteration(ScriptState*, ExceptionState&) {
+PairSyncIterable<KeyboardLayoutMap>::IterationSource*
+KeyboardLayoutMap::CreateIterationSource(ScriptState*, ExceptionState&) {
   return MakeGarbageCollected<KeyboardLayoutMapIterationSource>(*this);
 }
 
diff --git a/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.h b/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.h
index d9431d5..5c1bfa03 100644
--- a/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.h
+++ b/third_party/blink/renderer/modules/keyboard/keyboard_layout_map.h
@@ -7,19 +7,19 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_keyboard_layout_map.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
-class KeyboardLayoutMap final
-    : public ScriptWrappable,
-      public Maplike<String, IDLString, String, IDLString> {
+class KeyboardLayoutMap final : public ScriptWrappable,
+                                public MaplikeReadAPIs<KeyboardLayoutMap> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  KeyboardLayoutMap(const HashMap<String, String>& map);
+  explicit KeyboardLayoutMap(const HashMap<String, String>& map);
 
   const HashMap<String, String>& Map() const { return layout_map_; }
 
@@ -32,8 +32,9 @@
 
  private:
   // Maplike implementation.
-  PairIterable<String, IDLString, String, IDLString>::IterationSource*
-  StartIteration(ScriptState*, ExceptionState&) override;
+  PairSyncIterable<KeyboardLayoutMap>::IterationSource* CreateIterationSource(
+      ScriptState*,
+      ExceptionState&) override;
   bool GetMapEntry(ScriptState*,
                    const String& key,
                    String& value,
diff --git a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.cc b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.cc
index 53011a3..94227b06 100644
--- a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.cc
+++ b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.cc
@@ -12,7 +12,6 @@
 #include "media/capture/video_capture_types.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_source.h"
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/region_capture_crop_id.h"
@@ -22,25 +21,6 @@
 
 namespace {
 
-// These values are persisted to logs. Entries should not be renumbered and
-// numeric values should never be reused.
-enum class CropToResult {
-  kResolved = 0,
-  kUnsupportedPlatform = 1,
-  kInvalidCropTargetFormat = 2,
-  kRejectedWithErrorGeneric = 3,
-  kRejectedWithUnsupportedCaptureDevice = 4,
-  kRejectedWithErrorUnknownDeviceId_DEPRECATED = 5,
-  kRejectedWithNotImplemented = 6,
-  kNonIncreasingCropVersion = 7,
-  kInvalidCropTarget = 8,
-  kMaxValue = kInvalidCropTarget
-};
-
-void RecordUma(CropToResult result) {
-  base::UmaHistogramEnumeration("Media.RegionCapture.CropTo.Result", result);
-}
-
 #if !BUILDFLAG(IS_ANDROID)
 
 // TODO(crbug.com/1332628): Remove this flag once it's clear it's not necessary.
@@ -63,15 +43,21 @@
                          : absl::nullopt;
 }
 
-void RaiseCropException(ScriptPromiseResolver* resolver,
-                        DOMExceptionCode exception_code,
-                        const WTF::String& exception_text) {
+void RaiseCropException(
+    ScriptPromiseResolverWithTracker<
+        BrowserCaptureMediaStreamTrack::CropToResult>* resolver,
+    DOMExceptionCode exception_code,
+    const WTF::String& exception_text,
+    BrowserCaptureMediaStreamTrack::CropToResult result) {
   resolver->Reject(
-      MakeGarbageCollected<DOMException>(exception_code, exception_text));
+      MakeGarbageCollected<DOMException>(exception_code, exception_text),
+      result);
 }
 
-void ResolveCropPromiseHelper(ScriptPromiseResolver* resolver,
-                              media::mojom::CropRequestResult result) {
+void ResolveCropPromiseHelper(
+    ScriptPromiseResolverWithTracker<
+        BrowserCaptureMediaStreamTrack::CropToResult>* resolver,
+    media::mojom::CropRequestResult result) {
   DCHECK(IsMainThread());
 
   if (!resolver) {
@@ -80,30 +66,32 @@
 
   switch (result) {
     case media::mojom::CropRequestResult::kSuccess:
-      RecordUma(CropToResult::kResolved);
       // TODO(crbug.com/1264849): Delay reporting success to the Web-application
       // until "seeing" the last frame cropped to the previous crop-target.
       resolver->Resolve();
       return;
     case media::mojom::CropRequestResult::kErrorGeneric:
-      RecordUma(CropToResult::kRejectedWithErrorGeneric);
       RaiseCropException(resolver, DOMExceptionCode::kAbortError,
-                         "Unknown error.");
+                         "Unknown error.",
+                         BrowserCaptureMediaStreamTrack::CropToResult::
+                             kRejectedWithErrorGeneric);
       return;
     case media::mojom::CropRequestResult::kUnsupportedCaptureDevice:
       // Note that this is an unsupported device; not an unsupported Element.
       // This should essentially not happen. If it happens, it indicates
       // something in the capture pipeline has been changed.
-      RecordUma(CropToResult::kRejectedWithUnsupportedCaptureDevice);
       RaiseCropException(resolver, DOMExceptionCode::kAbortError,
-                         "Unsupported device.");
+                         "Unsupported device.",
+                         BrowserCaptureMediaStreamTrack::CropToResult::
+                             kRejectedWithUnsupportedCaptureDevice);
       return;
     case media::mojom::CropRequestResult::kNotImplemented:
       // Unimplemented codepath reached, OTHER than lacking support for
       // a specific Element subtype.
-      RecordUma(CropToResult::kRejectedWithNotImplemented);
       RaiseCropException(resolver, DOMExceptionCode::kOperationError,
-                         "Not implemented.");
+                         "Not implemented.",
+                         BrowserCaptureMediaStreamTrack::CropToResult::
+                             kRejectedWithNotImplemented);
       return;
     case media::mojom::CropRequestResult::kNonIncreasingCropVersion:
       // This should rarely happen, as the browser process would issue
@@ -111,14 +99,15 @@
       // the IO thread to the UI thread, it could theoretically happen
       // that Blink receives this callback before being killed, so we
       // can't quite DCHECK this.
-      RecordUma(CropToResult::kNonIncreasingCropVersion);
       RaiseCropException(resolver, DOMExceptionCode::kAbortError,
-                         "Non-increasing crop version.");
+                         "Non-increasing crop version.",
+                         BrowserCaptureMediaStreamTrack::CropToResult::
+                             kNonIncreasingCropVersion);
       return;
     case media::mojom::CropRequestResult::kInvalidCropTarget:
-      RecordUma(CropToResult::kNonIncreasingCropVersion);
-      RaiseCropException(resolver, DOMExceptionCode::kNotAllowedError,
-                         "Invalid CropTarget.");
+      RaiseCropException(
+          resolver, DOMExceptionCode::kNotAllowedError, "Invalid CropTarget.",
+          BrowserCaptureMediaStreamTrack::CropToResult::kInvalidCropTarget);
       return;
   }
 
@@ -162,22 +151,28 @@
 
   const String crop_id(crop_target ? crop_target->GetCropId() : String());
 
-  auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  // If the promise is not resolved within the |timeout_interval|, a
+  // CropToResult::kTimedOut response will be recorded in the UMA.
+  auto* resolver =
+      MakeGarbageCollected<ScriptPromiseResolverWithTracker<CropToResult>>(
+          script_state, /*metric_name_prefix=*/"Media.RegionCapture.CropTo",
+          /*timeout_interval=*/base::Seconds(10));
   ScriptPromise promise = resolver->Promise();
 
 #if BUILDFLAG(IS_ANDROID)
-  RecordUma(CropToResult::kUnsupportedPlatform);
-  resolver->Reject(MakeGarbageCollected<DOMException>(
-      DOMExceptionCode::kUnknownError, "Not supported on Android."));
+  resolver->Reject(
+      MakeGarbageCollected<DOMException>(DOMExceptionCode::kUnknownError,
+                                         "Not supported on Android."),
+      CropToResult::kUnsupportedPlatform);
   return promise;
 #else
 
   const absl::optional<base::Token> crop_id_token =
       CropIdStringToToken(crop_id);
   if (!crop_id_token.has_value()) {
-    RecordUma(CropToResult::kInvalidCropTargetFormat);
     resolver->Reject(MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kUnknownError, "Invalid crop-ID."));
+                         DOMExceptionCode::kUnknownError, "Invalid crop-ID."),
+                     CropToResult::kInvalidCropTargetFormat);
     return promise;
   }
 
@@ -196,10 +191,10 @@
   MediaStreamTrackPlatform* const native_track =
       MediaStreamTrackPlatform::GetTrack(WebMediaStreamTrack(component));
   if (!native_source || !native_track) {
-    // TODO(crbug.com/1266378): Use dedicate UMA values.
-    RecordUma(CropToResult::kRejectedWithErrorGeneric);
-    resolver->Reject(MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kUnknownError, "Native/platform track missing."));
+    resolver->Reject(
+        MakeGarbageCollected<DOMException>(DOMExceptionCode::kUnknownError,
+                                           "Native/platform track missing."),
+        CropToResult::kRejectedWithErrorGeneric);
     return promise;
   }
 
@@ -209,8 +204,9 @@
       native_source->GetNextCropVersion();
   if (!optional_crop_version.has_value()) {
     resolver->Reject(MakeGarbageCollected<DOMException>(
-        DOMExceptionCode::kOperationError,
-        "Can't change crop-target while clones exist."));
+                         DOMExceptionCode::kOperationError,
+                         "Can't change crop-target while clones exist."),
+                     CropToResult::kInvalidCropTarget);
     return promise;
   }
   const uint32_t crop_version = optional_crop_version.value();
@@ -320,7 +316,8 @@
     }
   }
 
-  ScriptPromiseResolver* const resolver = info->promise_resolver;
+  ScriptPromiseResolverWithTracker<CropToResult>* const resolver =
+      info->promise_resolver;
   pending_promises_.erase(iter);
   ResolveCropPromiseHelper(resolver, result);
 }
diff --git a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.h b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.h
index 2668867..21e404e 100644
--- a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.h
+++ b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.h
@@ -5,7 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_BROWSER_CAPTURE_MEDIA_STREAM_TRACK_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_MEDIASTREAM_BROWSER_CAPTURE_MEDIA_STREAM_TRACK_H_
 
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver_with_tracker.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/modules/mediastream/crop_target.h"
 #include "third_party/blink/renderer/modules/mediastream/media_stream_track_impl.h"
@@ -44,15 +44,33 @@
 
   BrowserCaptureMediaStreamTrack* clone(ExecutionContext*) override;
 
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum class CropToResult {
+    kOk = 0,
+    kUnsupportedPlatform = 1,
+    kInvalidCropTargetFormat = 2,
+    kRejectedWithErrorGeneric = 3,
+    kRejectedWithUnsupportedCaptureDevice = 4,
+    kRejectedWithErrorUnknownDeviceId_DEPRECATED = 5,
+    kRejectedWithNotImplemented = 6,
+    kNonIncreasingCropVersion = 7,
+    kInvalidCropTarget = 8,
+    kTimedOut = 9,
+    kMaxValue = kTimedOut
+  };
+
  private:
 #if !BUILDFLAG(IS_ANDROID)
   struct CropPromiseInfo : GarbageCollected<CropPromiseInfo> {
-    explicit CropPromiseInfo(ScriptPromiseResolver* promise_resolver)
+    explicit CropPromiseInfo(
+        ScriptPromiseResolverWithTracker<CropToResult>* promise_resolver)
         : promise_resolver(promise_resolver) {}
 
     void Trace(Visitor* visitor) const { visitor->Trace(promise_resolver); }
 
-    const Member<ScriptPromiseResolver> promise_resolver;
+    const Member<ScriptPromiseResolverWithTracker<CropToResult>>
+        promise_resolver;
     absl::optional<media::mojom::CropRequestResult> crop_result;
     bool crop_version_observed = false;
   };
diff --git a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track_test.cc b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track_test.cc
index 47d075cd..cd9448083 100644
--- a/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track_test.cc
+++ b/third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track_test.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/mediastream/browser_capture_media_stream_track.h"
 
 #include "base/guid.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/web/web_heap.h"
@@ -64,9 +65,21 @@
  public:
   ~BrowserCaptureMediaStreamTrackTest() override = default;
 
+  void CheckHistograms(
+      int expected_count,
+      BrowserCaptureMediaStreamTrack::CropToResult expected_result) {
+    histogram_tester_.ExpectTotalCount("Media.RegionCapture.CropTo.Result",
+                                       expected_count);
+    histogram_tester_.ExpectUniqueSample("Media.RegionCapture.CropTo.Result",
+                                         expected_result, expected_count);
+    histogram_tester_.ExpectTotalCount("Media.RegionCapture.CropTo.Latency",
+                                       expected_count);
+  }
+
   void TearDown() override { WebHeap::CollectAllGarbageForTesting(); }
 
  protected:
+  base::HistogramTester histogram_tester_;
   ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
 };
 
@@ -104,6 +117,8 @@
   ScriptPromiseTester script_promise_tester(v8_scope.GetScriptState(), promise);
   script_promise_tester.WaitUntilSettled();
   EXPECT_TRUE(script_promise_tester.IsFulfilled());
+  CheckHistograms(
+      /*expected_count=*/1, BrowserCaptureMediaStreamTrack::CropToResult::kOk);
 }
 
 TEST_F(BrowserCaptureMediaStreamTrackTest,
@@ -140,6 +155,9 @@
   ScriptPromiseTester script_promise_tester(v8_scope.GetScriptState(), promise);
   script_promise_tester.WaitUntilSettled();
   EXPECT_TRUE(script_promise_tester.IsRejected());
+  CheckHistograms(
+      /*expected_count=*/1,
+      BrowserCaptureMediaStreamTrack::CropToResult::kRejectedWithErrorGeneric);
 }
 
 TEST_F(BrowserCaptureMediaStreamTrackTest,
@@ -170,6 +188,9 @@
   ScriptPromiseTester script_promise_tester(v8_scope.GetScriptState(), promise);
   script_promise_tester.WaitUntilSettled();
   EXPECT_TRUE(script_promise_tester.IsRejected());
+  CheckHistograms(
+      /*expected_count=*/1,
+      BrowserCaptureMediaStreamTrack::CropToResult::kInvalidCropTarget);
 }
 
 #else
@@ -196,6 +217,9 @@
   ScriptPromiseTester script_promise_tester(v8_scope.GetScriptState(), promise);
   script_promise_tester.WaitUntilSettled();
   EXPECT_TRUE(script_promise_tester.IsRejected());
+  CheckHistograms(
+      /*expected_count=*/1,
+      BrowserCaptureMediaStreamTrack::CropToResult::kUnsupportedPlatform);
 }
 #endif
 
diff --git a/third_party/blink/renderer/modules/mediastream/navigator_media_stream.cc b/third_party/blink/renderer/modules/mediastream/navigator_media_stream.cc
index 1b56ca24..898aea87 100644
--- a/third_party/blink/renderer/modules/mediastream/navigator_media_stream.cc
+++ b/third_party/blink/renderer/modules/mediastream/navigator_media_stream.cc
@@ -126,7 +126,9 @@
 
   String error_message;
   if (!request->IsSecureContextUse(error_message)) {
-    request->Fail(UserMediaRequest::Error::kSecurityError, error_message);
+    request->Fail(
+        mojom::blink::MediaStreamRequestResult::INVALID_SECURITY_ORIGIN,
+        error_message);
     RecordIdentifiabilityMetric(
         surface, navigator.GetExecutionContext(),
         IdentifiabilityBenignStringToken(error_message));
diff --git a/third_party/blink/renderer/modules/mediastream/track_audio_renderer.cc b/third_party/blink/renderer/modules/mediastream/track_audio_renderer.cc
index 2176dbd..f9c3c22 100644
--- a/third_party/blink/renderer/modules/mediastream/track_audio_renderer.cc
+++ b/third_party/blink/renderer/modules/mediastream/track_audio_renderer.cc
@@ -99,7 +99,7 @@
 // media::AudioRendererSink::RenderCallback implementation
 int TrackAudioRenderer::Render(base::TimeDelta delay,
                                base::TimeTicks delay_timestamp,
-                               int prior_frames_skipped,
+                               const media::AudioGlitchInfo& glitch_info,
                                media::AudioBus* audio_bus) {
   TRACE_EVENT2("audio", "TrackAudioRenderer::Render", "delay (ms)",
                delay.InMillisecondsF(), "delay_timestamp (ms)",
diff --git a/third_party/blink/renderer/modules/mediastream/track_audio_renderer.h b/third_party/blink/renderer/modules/mediastream/track_audio_renderer.h
index 5e56c4e..434d8e5 100644
--- a/third_party/blink/renderer/modules/mediastream/track_audio_renderer.h
+++ b/third_party/blink/renderer/modules/mediastream/track_audio_renderer.h
@@ -131,7 +131,7 @@
   // on the IO thread.
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const media::AudioGlitchInfo& glitch_info,
              media::AudioBus* audio_bus) override;
   void OnRenderError() override;
 
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
index 30055f72..a662c07 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_processor.cc
@@ -253,6 +253,44 @@
   return capabilities;
 }
 
+String ErrorCodeToString(MediaStreamRequestResult result) {
+  switch (result) {
+    case MediaStreamRequestResult::PERMISSION_DENIED:
+      return "Permission denied";
+    case MediaStreamRequestResult::PERMISSION_DISMISSED:
+      return "Permission dismissed";
+    case MediaStreamRequestResult::INVALID_STATE:
+      return "Invalid state";
+    case MediaStreamRequestResult::NO_HARDWARE:
+      return "Requested device not found";
+    case MediaStreamRequestResult::INVALID_SECURITY_ORIGIN:
+      return "Invalid security origin";
+    case MediaStreamRequestResult::TAB_CAPTURE_FAILURE:
+      return "Error starting tab capture";
+    case MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE:
+      return "Error starting screen capture";
+    case MediaStreamRequestResult::CAPTURE_FAILURE:
+      return "Error starting capture";
+    case MediaStreamRequestResult::TRACK_START_FAILURE_AUDIO:
+      return "Could not start audio source";
+    case MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO:
+      return "Could not start video source";
+    case MediaStreamRequestResult::NOT_SUPPORTED:
+      return "Not supported";
+    case MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN:
+      return "Failed due to shutdown";
+    case MediaStreamRequestResult::KILL_SWITCH_ON:
+      return "";
+    case MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED:
+      return "Permission denied by system";
+    case MediaStreamRequestResult::DEVICE_IN_USE:
+      return "Device in use";
+    default:
+      NOTREACHED();
+      return "";
+  }
+}
+
 }  // namespace
 
 // Class for storing state of the the processing of getUserMedia requests.
@@ -1735,71 +1773,13 @@
     case MediaStreamRequestResult::NUM_MEDIA_REQUEST_RESULTS:
       NOTREACHED();
       return;
-    case MediaStreamRequestResult::PERMISSION_DENIED:
-      user_media_request->Fail(UserMediaRequest::Error::kPermissionDenied,
-                               "Permission denied");
-      return;
-    case MediaStreamRequestResult::PERMISSION_DISMISSED:
-      user_media_request->Fail(UserMediaRequest::Error::kPermissionDismissed,
-                               "Permission dismissed");
-      return;
-    case MediaStreamRequestResult::INVALID_STATE:
-      user_media_request->Fail(UserMediaRequest::Error::kInvalidState,
-                               "Invalid state");
-      return;
-    case MediaStreamRequestResult::NO_HARDWARE:
-      user_media_request->Fail(UserMediaRequest::Error::kDevicesNotFound,
-                               "Requested device not found");
-      return;
-    case MediaStreamRequestResult::INVALID_SECURITY_ORIGIN:
-      user_media_request->Fail(UserMediaRequest::Error::kSecurityError,
-                               "Invalid security origin");
-      return;
-    case MediaStreamRequestResult::TAB_CAPTURE_FAILURE:
-      user_media_request->Fail(UserMediaRequest::Error::kTabCapture,
-                               "Error starting tab capture");
-      return;
-    case MediaStreamRequestResult::SCREEN_CAPTURE_FAILURE:
-      user_media_request->Fail(UserMediaRequest::Error::kScreenCapture,
-                               "Error starting screen capture");
-      return;
-    case MediaStreamRequestResult::CAPTURE_FAILURE:
-      user_media_request->Fail(UserMediaRequest::Error::kCapture,
-                               "Error starting capture");
-      return;
     case MediaStreamRequestResult::CONSTRAINT_NOT_SATISFIED:
       user_media_request->FailConstraint(constraint_name, "");
       return;
-    case MediaStreamRequestResult::TRACK_START_FAILURE_AUDIO:
-      user_media_request->Fail(UserMediaRequest::Error::kTrackStart,
-                               "Could not start audio source");
-      return;
-    case MediaStreamRequestResult::TRACK_START_FAILURE_VIDEO:
-      user_media_request->Fail(UserMediaRequest::Error::kTrackStart,
-                               "Could not start video source");
-      return;
-    case MediaStreamRequestResult::NOT_SUPPORTED:
-      user_media_request->Fail(UserMediaRequest::Error::kNotSupported,
-                               "Not supported");
-      return;
-    case MediaStreamRequestResult::FAILED_DUE_TO_SHUTDOWN:
-      user_media_request->Fail(UserMediaRequest::Error::kFailedDueToShutdown,
-                               "Failed due to shutdown");
-      return;
-    case MediaStreamRequestResult::KILL_SWITCH_ON:
-      user_media_request->Fail(UserMediaRequest::Error::kKillSwitchOn, "");
-      return;
-    case MediaStreamRequestResult::SYSTEM_PERMISSION_DENIED:
-      user_media_request->Fail(UserMediaRequest::Error::kSystemPermissionDenied,
-                               "Permission denied by system");
-      return;
-    case MediaStreamRequestResult::DEVICE_IN_USE:
-      user_media_request->Fail(UserMediaRequest::Error::kDeviceInUse,
-                               "Device in use");
+    default:
+      user_media_request->Fail(result, ErrorCodeToString(result));
       return;
   }
-  NOTREACHED();
-  user_media_request->Fail(UserMediaRequest::Error::kPermissionDenied, "");
 }
 
 MediaStreamSource* UserMediaProcessor::FindLocalSource(
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_request.cc b/third_party/blink/renderer/modules/mediastream/user_media_request.cc
index 57032c4..bd05990 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_request.cc
+++ b/third_party/blink/renderer/modules/mediastream/user_media_request.cc
@@ -66,6 +66,7 @@
 namespace blink {
 
 using mojom::blink::MediaStreamType;
+using Result = mojom::blink::MediaStreamRequestResult;
 
 namespace {
 
@@ -821,37 +822,37 @@
   is_resolved_ = true;
 }
 
-void UserMediaRequest::Fail(Error name, const String& message) {
+void UserMediaRequest::Fail(Result error, const String& message) {
   DCHECK(!is_resolved_);
   if (!GetExecutionContext())
     return;
-
   DOMExceptionCode exception_code = DOMExceptionCode::kNotSupportedError;
-  switch (name) {
-    case Error::kPermissionDenied:
-    case Error::kPermissionDismissed:
-    case Error::kInvalidState:
-    case Error::kFailedDueToShutdown:
-    case Error::kKillSwitchOn:
-    case Error::kSystemPermissionDenied:
+  switch (error) {
+    case Result::PERMISSION_DENIED:
+    case Result::PERMISSION_DISMISSED:
+    case Result::INVALID_STATE:
+    case Result::FAILED_DUE_TO_SHUTDOWN:
+    case Result::KILL_SWITCH_ON:
+    case Result::SYSTEM_PERMISSION_DENIED:
       exception_code = DOMExceptionCode::kNotAllowedError;
       break;
-    case Error::kDevicesNotFound:
+    case Result::NO_HARDWARE:
       exception_code = DOMExceptionCode::kNotFoundError;
       break;
-    case Error::kTabCapture:
-    case Error::kScreenCapture:
-    case Error::kCapture:
+    case Result::TAB_CAPTURE_FAILURE:
+    case Result::SCREEN_CAPTURE_FAILURE:
+    case Result::CAPTURE_FAILURE:
       exception_code = DOMExceptionCode::kAbortError;
       break;
-    case Error::kTrackStart:
-    case Error::kDeviceInUse:
+    case Result::TRACK_START_FAILURE_AUDIO:
+    case Result::TRACK_START_FAILURE_VIDEO:
+    case Result::DEVICE_IN_USE:
       exception_code = DOMExceptionCode::kNotReadableError;
       break;
-    case Error::kNotSupported:
+    case Result::NOT_SUPPORTED:
       exception_code = DOMExceptionCode::kNotSupportedError;
       break;
-    case Error::kSecurityError:
+    case Result::INVALID_SECURITY_ORIGIN:
       exception_code = DOMExceptionCode::kSecurityError;
       break;
     default:
diff --git a/third_party/blink/renderer/modules/mediastream/user_media_request.h b/third_party/blink/renderer/modules/mediastream/user_media_request.h
index dbb04ae5..c39f0a52 100644
--- a/third_party/blink/renderer/modules/mediastream/user_media_request.h
+++ b/third_party/blink/renderer/modules/mediastream/user_media_request.h
@@ -57,23 +57,6 @@
     : public GarbageCollected<UserMediaRequest>,
       public ExecutionContextLifecycleObserver {
  public:
-  enum class Error {
-    kNotSupported,
-    kSecurityError,
-    kPermissionDenied,
-    kPermissionDismissed,
-    kInvalidState,
-    kDevicesNotFound,
-    kTabCapture,
-    kScreenCapture,
-    kCapture,
-    kTrackStart,
-    kFailedDueToShutdown,
-    kKillSwitchOn,
-    kSystemPermissionDenied,
-    kDeviceInUse
-  };
-
   class Callbacks : public GarbageCollected<Callbacks> {
    public:
     virtual ~Callbacks() = default;
@@ -122,7 +105,8 @@
   void OnMediaStreamInitialized(MediaStream* stream);
   void OnMediaStreamsInitialized(MediaStreamVector streams);
   void FailConstraint(const String& constraint_name, const String& message);
-  void Fail(Error name, const String& message);
+  void Fail(mojom::blink::MediaStreamRequestResult error,
+            const String& message);
 
   UserMediaRequestType MediaRequestType() const;
   bool Audio() const;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
index 20f4d40..0d1f62e 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.cc
@@ -130,21 +130,22 @@
 }
 
 class RTCStatsReportIterationSource final
-    : public PairIterable<String, IDLString, v8::Local<v8::Object>, IDLObject>::
-          IterationSource {
+    : public PairSyncIterable<RTCStatsReport>::IterationSource {
  public:
-  RTCStatsReportIterationSource(std::unique_ptr<RTCStatsReportPlatform> report)
+  explicit RTCStatsReportIterationSource(
+      std::unique_ptr<RTCStatsReportPlatform> report)
       : report_(std::move(report)) {}
 
-  bool Next(ScriptState* script_state,
-            String& key,
-            v8::Local<v8::Object>& value,
-            ExceptionState& exception_state) override {
+  bool FetchNextItem(ScriptState* script_state,
+                     String& key,
+                     ScriptValue& value,
+                     ExceptionState& exception_state) override {
     std::unique_ptr<RTCStats> stats = report_->Next();
     if (!stats)
       return false;
     key = stats->Id();
-    value = RTCStatsToV8Object(script_state, stats.get());
+    value = ScriptValue(script_state->GetIsolate(),
+                        RTCStatsToV8Object(script_state, stats.get()));
     return true;
   }
 
@@ -178,21 +179,21 @@
   return base::saturated_cast<uint32_t>(report_->Size());
 }
 
-PairIterable<String, IDLString, v8::Local<v8::Object>, IDLObject>::
-    IterationSource*
-    RTCStatsReport::StartIteration(ScriptState*, ExceptionState&) {
+PairSyncIterable<RTCStatsReport>::IterationSource*
+RTCStatsReport::CreateIterationSource(ScriptState*, ExceptionState&) {
   return MakeGarbageCollected<RTCStatsReportIterationSource>(
       report_->CopyHandle());
 }
 
 bool RTCStatsReport::GetMapEntry(ScriptState* script_state,
                                  const String& key,
-                                 v8::Local<v8::Object>& value,
+                                 ScriptValue& value,
                                  ExceptionState&) {
   std::unique_ptr<RTCStats> stats = report_->GetStats(key);
   if (!stats)
     return false;
-  value = RTCStatsToV8Object(script_state, stats.get());
+  value = ScriptValue(script_state->GetIsolate(),
+                      RTCStatsToV8Object(script_state, stats.get()));
   return true;
 }
 
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h
index 6ae1cb7..39b4b5c 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_stats_report.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/public/platform/web_vector.h"
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_rtc_stats_report.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/peerconnection/rtc_stats.h"
@@ -21,23 +22,22 @@
     const ScriptState* script_state);
 
 // https://w3c.github.io/webrtc-pc/#rtcstatsreport-object
-class RTCStatsReport final
-    : public ScriptWrappable,
-      public Maplike<String, IDLString, v8::Local<v8::Object>, IDLObject> {
+class RTCStatsReport final : public ScriptWrappable,
+                             public MaplikeReadAPIs<RTCStatsReport> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  RTCStatsReport(std::unique_ptr<RTCStatsReportPlatform>);
+  explicit RTCStatsReport(std::unique_ptr<RTCStatsReportPlatform>);
 
   uint32_t size() const;
 
   // Maplike<String, v8::Local<v8::Value>>
-  PairIterable<String, IDLString, v8::Local<v8::Object>, IDLObject>::
-      IterationSource*
-      StartIteration(ScriptState*, ExceptionState&) override;
+  PairSyncIterable<RTCStatsReport>::IterationSource* CreateIterationSource(
+      ScriptState*,
+      ExceptionState&) override;
   bool GetMapEntry(ScriptState*,
                    const String& key,
-                   v8::Local<v8::Object>&,
+                   ScriptValue&,
                    ExceptionState&) override;
 
  private:
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_map.cc b/third_party/blink/renderer/modules/webaudio/audio_param_map.cc
index f9fff92..ee338d6 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_map.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_map.cc
@@ -7,22 +7,23 @@
 namespace blink {
 
 class AudioParamMapIterationSource final
-    : public PairIterable<String, IDLString, AudioParam*, AudioParam>::
-          IterationSource {
+    : public PairSyncIterable<AudioParamMap>::IterationSource {
  public:
   explicit AudioParamMapIterationSource(
       const HeapHashMap<String, Member<AudioParam>>& map) {
-    for (const auto& name : map.Keys()) {
-      parameter_names_.push_back(name);
-      parameter_objects_.push_back(map.at(name));
+    parameter_names_.ReserveInitialCapacity(map.size());
+    parameter_objects_.ReserveInitialCapacity(map.size());
+    for (const auto& item : map) {
+      parameter_names_.push_back(item.key);
+      parameter_objects_.push_back(item.value);
     }
   }
 
-  bool Next(ScriptState* scrip_state,
-            String& key,
-            AudioParam*& audio_param,
-            ExceptionState&) override {
-    if (current_index_ == parameter_names_.size()) {
+  bool FetchNextItem(ScriptState* scrip_state,
+                     String& key,
+                     AudioParam*& audio_param,
+                     ExceptionState&) override {
+    if (current_index_ >= parameter_names_.size()) {
       return false;
     }
     key = parameter_names_[current_index_];
@@ -33,12 +34,10 @@
 
   void Trace(Visitor* visitor) const override {
     visitor->Trace(parameter_objects_);
-    PairIterable<String, IDLString, AudioParam*,
-                 AudioParam>::IterationSource::Trace(visitor);
+    PairSyncIterable<AudioParamMap>::IterationSource::Trace(visitor);
   }
 
  private:
-  // For sequential iteration (e.g. Next()).
   Vector<String> parameter_names_;
   HeapVector<Member<AudioParam>> parameter_objects_;
   unsigned current_index_;
@@ -48,21 +47,20 @@
     const HeapHashMap<String, Member<AudioParam>>& parameter_map)
     : parameter_map_(parameter_map) {}
 
-PairIterable<String, IDLString, AudioParam*, AudioParam>::IterationSource*
-AudioParamMap::StartIteration(ScriptState*, ExceptionState&) {
+PairSyncIterable<AudioParamMap>::IterationSource*
+AudioParamMap::CreateIterationSource(ScriptState*, ExceptionState&) {
   return MakeGarbageCollected<AudioParamMapIterationSource>(parameter_map_);
 }
 
 bool AudioParamMap::GetMapEntry(ScriptState*,
                                 const String& key,
-                                AudioParam*& audio_param,
+                                AudioParam*& value,
                                 ExceptionState&) {
-  if (parameter_map_.Contains(key)) {
-    audio_param = parameter_map_.at(key);
-    return true;
-  }
-
-  return false;
+  auto it = parameter_map_.find(key);
+  if (it == parameter_map_.end())
+    return false;
+  value = it->value;
+  return true;
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_param_map.h b/third_party/blink/renderer/modules/webaudio/audio_param_map.h
index 542ae39a..5716443 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_param_map.h
+++ b/third_party/blink/renderer/modules/webaudio/audio_param_map.h
@@ -7,6 +7,7 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/maplike.h"
 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_audio_param_map.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_param.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -18,9 +19,8 @@
 
 class AudioParam;
 
-class AudioParamMap final
-    : public ScriptWrappable,
-      public Maplike<String, IDLString, AudioParam*, AudioParam> {
+class AudioParamMap final : public ScriptWrappable,
+                            public MaplikeReadAPIs<AudioParamMap> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -39,11 +39,12 @@
   }
 
  private:
-  PairIterable<String, IDLString, AudioParam*, AudioParam>::IterationSource*
-  StartIteration(ScriptState*, ExceptionState&) override;
+  PairSyncIterable<AudioParamMap>::IterationSource* CreateIterationSource(
+      ScriptState*,
+      ExceptionState&) override;
   bool GetMapEntry(ScriptState*,
                    const String& key,
-                   AudioParam*&,
+                   AudioParam*& value,
                    ExceptionState&) override;
 
   const MapType parameter_map_;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc
index bcece8e..65adfae 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc
+++ b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.cc
@@ -39,16 +39,15 @@
   iter_ = features_.begin();
 }
 
-bool GPUSupportedFeatures::IterationSource::Next(
+bool GPUSupportedFeatures::IterationSource::FetchNextItem(
     ScriptState* script_state,
-    String& key,
     String& value,
     ExceptionState& exception_state) {
   if (iter_ == features_.end()) {
     return false;
   }
 
-  key = value = *iter_;
+  value = *iter_;
   ++iter_;
 
   return true;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.h b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.h
index 8e597f3..386b041 100644
--- a/third_party/blink/renderer/modules/webgpu/gpu_supported_features.h
+++ b/third_party/blink/renderer/modules/webgpu/gpu_supported_features.h
@@ -6,13 +6,14 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBGPU_GPU_SUPPORTED_FEATURES_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_gpu_supported_features.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 
 namespace blink {
 
 class GPUSupportedFeatures : public ScriptWrappable,
-                             public SetlikeIterable<String, IDLString> {
+                             public ValueSyncIterable<GPUSupportedFeatures> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -34,14 +35,13 @@
   HashSet<String> features_;
 
   class IterationSource final
-      : public SetlikeIterable<String, IDLString>::IterationSource {
+      : public ValueSyncIterable<GPUSupportedFeatures>::IterationSource {
    public:
     explicit IterationSource(const HashSet<String>& features);
 
-    bool Next(ScriptState* script_state,
-              String& key,
-              String& value,
-              ExceptionState& exception_state) override;
+    bool FetchNextItem(ScriptState* script_state,
+                       String& value,
+                       ExceptionState& exception_state) override;
 
    private:
     HashSet<String> features_;
@@ -49,8 +49,8 @@
   };
 
   // Starts iteration over the Setlike.
-  // Needed for SetlikeIterable to work properly.
-  GPUSupportedFeatures::IterationSource* StartIteration(
+  // Needed for ValueSyncIterable to work properly.
+  GPUSupportedFeatures::IterationSource* CreateIterationSource(
       ScriptState* script_state,
       ExceptionState& exception_state) override {
     return MakeGarbageCollected<GPUSupportedFeatures::IterationSource>(
diff --git a/third_party/blink/renderer/modules/webmidi/midi_input_map.cc b/third_party/blink/renderer/modules/webmidi/midi_input_map.cc
index d87dac48..d1bf983 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_input_map.cc
+++ b/third_party/blink/renderer/modules/webmidi/midi_input_map.cc
@@ -9,6 +9,6 @@
 namespace blink {
 
 MIDIInputMap::MIDIInputMap(const HeapVector<Member<MIDIInput>>& entries)
-    : MIDIPortMap<MIDIInput>(entries) {}
+    : MIDIPortMap<MIDIInputMap, MIDIInput>(entries) {}
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webmidi/midi_input_map.h b/third_party/blink/renderer/modules/webmidi/midi_input_map.h
index 9611819..574233d 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_input_map.h
+++ b/third_party/blink/renderer/modules/webmidi/midi_input_map.h
@@ -5,12 +5,13 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBMIDI_MIDI_INPUT_MAP_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBMIDI_MIDI_INPUT_MAP_H_
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_midi_input_map.h"
 #include "third_party/blink/renderer/modules/webmidi/midi_input.h"
 #include "third_party/blink/renderer/modules/webmidi/midi_port_map.h"
 
 namespace blink {
 
-class MIDIInputMap : public MIDIPortMap<MIDIInput> {
+class MIDIInputMap : public MIDIPortMap<MIDIInputMap, MIDIInput> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/modules/webmidi/midi_output_map.cc b/third_party/blink/renderer/modules/webmidi/midi_output_map.cc
index cebcc09..941989d 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_output_map.cc
+++ b/third_party/blink/renderer/modules/webmidi/midi_output_map.cc
@@ -9,6 +9,6 @@
 namespace blink {
 
 MIDIOutputMap::MIDIOutputMap(HeapVector<Member<MIDIOutput>>& entries)
-    : MIDIPortMap<MIDIOutput>(entries) {}
+    : MIDIPortMap<MIDIOutputMap, MIDIOutput>(entries) {}
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webmidi/midi_output_map.h b/third_party/blink/renderer/modules/webmidi/midi_output_map.h
index 44ca6b8..7fa67c3 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_output_map.h
+++ b/third_party/blink/renderer/modules/webmidi/midi_output_map.h
@@ -5,12 +5,13 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_WEBMIDI_MIDI_OUTPUT_MAP_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_WEBMIDI_MIDI_OUTPUT_MAP_H_
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_midi_output_map.h"
 #include "third_party/blink/renderer/modules/webmidi/midi_output.h"
 #include "third_party/blink/renderer/modules/webmidi/midi_port_map.h"
 
 namespace blink {
 
-class MIDIOutputMap : public MIDIPortMap<MIDIOutput> {
+class MIDIOutputMap : public MIDIPortMap<MIDIOutputMap, MIDIOutput> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/modules/webmidi/midi_port_map.h b/third_party/blink/renderer/modules/webmidi/midi_port_map.h
index 02ce363..c8ebc30 100644
--- a/third_party/blink/renderer/modules/webmidi/midi_port_map.h
+++ b/third_party/blink/renderer/modules/webmidi/midi_port_map.h
@@ -14,11 +14,11 @@
 
 namespace blink {
 
-template <typename T>
+template <typename InterfaceType, typename ValueType>
 class MIDIPortMap : public ScriptWrappable,
-                    public Maplike<String, IDLString, T*, T> {
+                    public MaplikeReadAPIs<InterfaceType> {
  public:
-  explicit MIDIPortMap(const HeapVector<Member<T>>& entries)
+  explicit MIDIPortMap(const HeapVector<Member<ValueType>>& entries)
       : entries_(entries) {}
 
   // IDL attributes / methods
@@ -31,18 +31,18 @@
 
  private:
   // We use HeapVector here to keep the entry order.
-  using Entries = HeapVector<Member<T>>;
+  using Entries = HeapVector<Member<ValueType>>;
   using IteratorType = typename Entries::const_iterator;
 
-  typename PairIterable<String, IDLString, T*, T>::IterationSource*
-  StartIteration(ScriptState*, ExceptionState&) override {
+  typename PairSyncIterable<InterfaceType>::IterationSource*
+  CreateIterationSource(ScriptState*, ExceptionState&) override {
     return MakeGarbageCollected<MapIterationSource>(this, entries_.begin(),
                                                     entries_.end());
   }
 
   bool GetMapEntry(ScriptState*,
                    const String& key,
-                   T*& value,
+                   ValueType*& value,
                    ExceptionState&) override {
     // FIXME: This function is not O(1). Perhaps it's OK because in typical
     // cases not so many ports are connected.
@@ -58,17 +58,17 @@
   // Note: This template class relies on the fact that m_map.m_entries will
   // never be modified once it is created.
   class MapIterationSource final
-      : public PairIterable<String, IDLString, T*, T>::IterationSource {
+      : public PairSyncIterable<InterfaceType>::IterationSource {
    public:
-    MapIterationSource(MIDIPortMap<T>* map,
+    MapIterationSource(MIDIPortMap<InterfaceType, ValueType>* map,
                        IteratorType iterator,
                        IteratorType end)
         : map_(map), iterator_(iterator), end_(end) {}
 
-    bool Next(ScriptState* script_state,
-              String& key,
-              T*& value,
-              ExceptionState&) override {
+    bool FetchNextItem(ScriptState* script_state,
+                       String& key,
+                       ValueType*& value,
+                       ExceptionState&) override {
       if (iterator_ == end_)
         return false;
       key = (*iterator_)->id();
@@ -79,13 +79,13 @@
 
     void Trace(Visitor* visitor) const override {
       visitor->Trace(map_);
-      PairIterable<String, IDLString, T*, T>::IterationSource::Trace(visitor);
+      PairSyncIterable<InterfaceType>::IterationSource::Trace(visitor);
     }
 
    private:
-    // m_map is stored just for keeping it alive. It needs to be kept
+    // map_ is stored just for keeping it alive. It needs to be kept
     // alive while JavaScript holds the iterator to it.
-    const Member<const MIDIPortMap<T>> map_;
+    const Member<const MIDIPortMap<InterfaceType, ValueType>> map_;
     IteratorType iterator_;
     const IteratorType end_;
   };
diff --git a/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc b/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc
index c7caf62..612351a 100644
--- a/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc
+++ b/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.cc
@@ -608,7 +608,7 @@
 
 int WebRtcAudioRenderer::Render(base::TimeDelta delay,
                                 base::TimeTicks delay_timestamp,
-                                int prior_frames_skipped,
+                                const media::AudioGlitchInfo& glitch_info,
                                 media::AudioBus* audio_bus) {
   DCHECK(sink_->CurrentThreadIsRenderingThread());
   DCHECK_LE(sink_params_.channels(), 8);
diff --git a/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.h b/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.h
index 5543e72..6fe4e68 100644
--- a/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.h
+++ b/third_party/blink/renderer/modules/webrtc/webrtc_audio_renderer.h
@@ -267,7 +267,7 @@
   // These two methods are called on the AudioOutputDevice worker thread.
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const media::AudioGlitchInfo& glitch_info,
              media::AudioBus* audio_bus) override;
   void OnRenderError() override;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_anchor_set.h b/third_party/blink/renderer/modules/xr/xr_anchor_set.h
index 72912e6..c46b9f5b 100644
--- a/third_party/blink/renderer/modules/xr/xr_anchor_set.h
+++ b/third_party/blink/renderer/modules/xr/xr_anchor_set.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_ANCHOR_SET_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_xr_anchor_set.h"
 #include "third_party/blink/renderer/modules/xr/xr_anchor.h"
 #include "third_party/blink/renderer/modules/xr/xr_setlike.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -13,7 +14,8 @@
 
 namespace blink {
 
-class XRAnchorSet : public ScriptWrappable, public XRSetlike<XRAnchor> {
+class XRAnchorSet : public ScriptWrappable,
+                    public XRSetlike<XRAnchorSet, XRAnchor> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/modules/xr/xr_hand.cc b/third_party/blink/renderer/modules/xr/xr_hand.cc
index 38335c8..51470a33 100644
--- a/third_party/blink/renderer/modules/xr/xr_hand.cc
+++ b/third_party/blink/renderer/modules/xr/xr_hand.cc
@@ -14,44 +14,45 @@
 namespace blink {
 
 class XRHandIterationSource final
-    : public PairIterable<String,
-                          IDLString,
-                          Member<XRJointSpace>,
-                          XRJointSpace>::IterationSource {
+    : public PairSyncIterable<XRHand>::IterationSource {
  public:
-  explicit XRHandIterationSource(HeapVector<Member<XRJointSpace>>& joints)
-      : index_(0), joints_(joints) {}
+  explicit XRHandIterationSource(HeapVector<Member<XRJointSpace>>& joints,
+                                 XRHand* xr_hand)
+      : index_(0), joints_(joints), xr_hand_(xr_hand) {}
 
-  bool Next(ScriptState*,
-            String& key,
-            Member<XRJointSpace>& value,
-            ExceptionState&) override {
-    if (index_ >= joints_.size())
+  bool FetchNextItem(ScriptState*,
+                     V8XRHandJoint& key,
+                     XRJointSpace*& value,
+                     ExceptionState&) override {
+    if (index_ >= V8XRHandJoint::kEnumSize)
       return false;
 
-    key = MojomHandJointToString(
-        static_cast<device::mojom::blink::XRHandJoint>(index_));
+    key = V8XRHandJoint(static_cast<V8XRHandJoint::Enum>(index_));
     value = joints_.at(index_);
     index_++;
     return true;
   }
 
   void Trace(Visitor* visitor) const override {
-    PairIterable<String, IDLString, Member<XRJointSpace>,
-                 XRJointSpace>::IterationSource::Trace(visitor);
+    visitor->Trace(xr_hand_);
+    PairSyncIterable<XRHand>::IterationSource::Trace(visitor);
   }
 
  private:
   wtf_size_t index_;
   const HeapVector<Member<XRJointSpace>>& joints_;
+  Member<XRHand> xr_hand_;  // Owner object of `joints_`
 };
 
 XRHand::XRHand(const device::mojom::blink::XRHandTrackingData* state,
                XRInputSource* input_source)
     : joints_(kNumJoints) {
+  DCHECK_EQ(kNumJoints, V8XRHandJoint::kEnumSize);
   for (unsigned i = 0; i < kNumJoints; ++i) {
     device::mojom::blink::XRHandJoint joint =
         static_cast<device::mojom::blink::XRHandJoint>(i);
+    DCHECK_EQ(MojomHandJointToString(joint),
+              V8XRHandJoint(static_cast<V8XRHandJoint::Enum>(i)).AsString());
     joints_[i] = MakeGarbageCollected<XRJointSpace>(
         this, input_source->session(), nullptr, joint, 0.0f,
         input_source->xr_handedness());
@@ -60,9 +61,8 @@
   updateFromHandTrackingData(state, input_source);
 }
 
-XRJointSpace* XRHand::get(const String& key) {
-  device::mojom::blink::XRHandJoint joint = StringToMojomHandJoint(key);
-  unsigned index = static_cast<unsigned>(joint);
+XRJointSpace* XRHand::get(const V8XRHandJoint& key) const {
+  wtf_size_t index = static_cast<wtf_size_t>(key.AsEnum());
   return joints_[index];
 }
 
@@ -98,10 +98,10 @@
   }
 }
 
-XRHand::IterationSource* XRHand::StartIteration(
+XRHand::IterationSource* XRHand::CreateIterationSource(
     ScriptState* script_state,
     ExceptionState& exception_state) {
-  return MakeGarbageCollected<XRHandIterationSource>(joints_);
+  return MakeGarbageCollected<XRHandIterationSource>(joints_, this);
 }
 
 void XRHand::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/modules/xr/xr_hand.h b/third_party/blink/renderer/modules/xr/xr_hand.h
index cb22290e..9a22fdd1a 100644
--- a/third_party/blink/renderer/modules/xr/xr_hand.h
+++ b/third_party/blink/renderer/modules/xr/xr_hand.h
@@ -7,19 +7,17 @@
 
 #include "device/vr/public/mojom/vr_service.mojom-blink.h"
 #include "third_party/blink/renderer/bindings/core/v8/iterable.h"
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_xr_hand.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
 namespace blink {
 
+class V8XRHandJoint;
 class XRInputSource;
 class XRJointSpace;
 
-class XRHand : public ScriptWrappable,
-               public PairIterable<String,
-                                   IDLString,
-                                   Member<XRJointSpace>,
-                                   XRJointSpace> {
+class XRHand : public ScriptWrappable, public PairSyncIterable<XRHand> {
   DEFINE_WRAPPERTYPEINFO();
 
   static const unsigned kNumJoints =
@@ -32,7 +30,7 @@
 
   size_t size() const { return joints_.size(); }
 
-  XRJointSpace* get(const String& key);
+  XRJointSpace* get(const V8XRHandJoint& key) const;
 
   void updateFromHandTrackingData(
       const device::mojom::blink::XRHandTrackingData* state,
@@ -43,7 +41,8 @@
   void Trace(Visitor*) const override;
 
  private:
-  IterationSource* StartIteration(ScriptState*, ExceptionState&) override;
+  IterationSource* CreateIterationSource(ScriptState*,
+                                         ExceptionState&) override;
 
   HeapVector<Member<XRJointSpace>> joints_;
   bool has_missing_poses_ = true;
diff --git a/third_party/blink/renderer/modules/xr/xr_plane_set.h b/third_party/blink/renderer/modules/xr/xr_plane_set.h
index 7404975..bec8af3 100644
--- a/third_party/blink/renderer/modules/xr/xr_plane_set.h
+++ b/third_party/blink/renderer/modules/xr/xr_plane_set.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PLANE_SET_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_PLANE_SET_H_
 
+#include "third_party/blink/renderer/bindings/modules/v8/v8_sync_iterator_xr_plane_set.h"
 #include "third_party/blink/renderer/modules/xr/xr_plane.h"
 #include "third_party/blink/renderer/modules/xr/xr_setlike.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -12,7 +13,8 @@
 
 namespace blink {
 
-class XRPlaneSet : public ScriptWrappable, public XRSetlike<XRPlane> {
+class XRPlaneSet : public ScriptWrappable,
+                   public XRSetlike<XRPlaneSet, XRPlane> {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/modules/xr/xr_setlike.h b/third_party/blink/renderer/modules/xr/xr_setlike.h
index 0b59943..16d2a2f 100644
--- a/third_party/blink/renderer/modules/xr/xr_setlike.h
+++ b/third_party/blink/renderer/modules/xr/xr_setlike.h
@@ -15,8 +15,8 @@
 // The consumer of the class needs only to implement the |elements()| method -
 // everything else should be provided by this class. For examples, see
 // `XRPlaneSet` and `XRAnchorSet`.
-template <typename ElementType>
-class XRSetlike : public SetlikeIterable<Member<ElementType>, ElementType> {
+template <typename InterfaceType, typename ElementType>
+class XRSetlike : public ValueSyncIterable<InterfaceType> {
  public:
   unsigned size() const { return elements().size(); }
 
@@ -34,8 +34,7 @@
 
  private:
   class IterationSource final
-      : public SetlikeIterable<Member<ElementType>,
-                               ElementType>::IterationSource {
+      : public ValueSyncIterable<InterfaceType>::IterationSource {
    public:
     explicit IterationSource(const HeapHashSet<Member<ElementType>>& elements)
         : index_(0) {
@@ -45,15 +44,14 @@
       }
     }
 
-    bool Next(ScriptState* script_state,
-              Member<ElementType>& key,
-              Member<ElementType>& value,
-              ExceptionState& exception_state) override {
+    bool FetchNextItem(ScriptState* script_state,
+                       ElementType*& value,
+                       ExceptionState& exception_state) override {
       if (index_ >= elements_.size()) {
         return false;
       }
 
-      key = value = elements_[index_];
+      value = elements_[index_];
       ++index_;
 
       return true;
@@ -61,8 +59,7 @@
 
     void Trace(Visitor* visitor) const override {
       visitor->Trace(elements_);
-      SetlikeIterable<Member<ElementType>, ElementType>::IterationSource::Trace(
-          visitor);
+      ValueSyncIterable<InterfaceType>::IterationSource::Trace(visitor);
     }
 
    private:
@@ -72,8 +69,8 @@
   };
 
   // Starts iteration over XRSetlike.
-  // Needed for SetlikeIterable to work properly.
-  XRSetlike::IterationSource* StartIteration(
+  // Needed for ValueSyncIterable to work properly.
+  XRSetlike::IterationSource* CreateIterationSource(
       ScriptState* script_state,
       ExceptionState& exception_state) override {
     return MakeGarbageCollected<XRSetlike::IterationSource>(elements());
diff --git a/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl.cc b/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl.cc
index c059f070..0f7726c1 100644
--- a/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl.cc
+++ b/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl.cc
@@ -14,6 +14,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/notreached.h"
 #include "base/thread_annotations.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_timestamp_helper.h"
 #include "media/base/bind_to_current_loop.h"
 #include "media/base/media_log.h"
@@ -47,13 +48,13 @@
   // get a copy of the rendered audio by SetCopyAudioCallback().
   int Render(base::TimeDelta delay,
              base::TimeTicks delay_timestamp,
-             int prior_frames_skipped,
+             const media::AudioGlitchInfo& glitch_info,
              media::AudioBus* audio_bus) override {
     DCHECK(initialized());
     DCHECK_EQ(audio_bus->channels(), channels_);
 
-    const int num_rendered_frames = renderer_->Render(
-        delay, delay_timestamp, prior_frames_skipped, audio_bus);
+    const int num_rendered_frames =
+        renderer_->Render(delay, delay_timestamp, glitch_info, audio_bus);
 
     // Avoid taking the copy lock for the vast majority of cases.
     if (copy_required_) {
@@ -68,6 +69,9 @@
           bus_copy->Zero();
         else
           audio_bus->CopyTo(bus_copy.get());
+
+        // TODO(fhernqvist): Propagate glitch info through here if the callback
+        // needs it.
         copy_audio_bus_callback_.Run(std::move(bus_copy),
                                      static_cast<uint32_t>(frames_delayed),
                                      sample_rate_);
@@ -202,8 +206,10 @@
     return;
   }
 
+  // TODO(fhernqvist): If we need glitches propagated through WebAudio, plumb
+  // them through here.
   const int frames = tee_filter_->Render(
-      base::TimeDelta(), base::TimeTicks::Now(), 0, bus_wrapper_.get());
+      base::TimeDelta(), base::TimeTicks::Now(), {}, bus_wrapper_.get());
 
   // Zero out frames after rendering for tainted origins.
   if (tee_filter_->is_tainted()) {
@@ -336,7 +342,7 @@
 }
 
 int WebAudioSourceProviderImpl::RenderForTesting(media::AudioBus* audio_bus) {
-  return tee_filter_->Render(base::TimeDelta(), base::TimeTicks::Now(), 0,
+  return tee_filter_->Render(base::TimeDelta(), base::TimeTicks::Now(), {},
                              audio_bus);
 }
 
diff --git a/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl_test.cc b/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl_test.cc
index 228fb8d6..11dcc8f 100644
--- a/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl_test.cc
+++ b/third_party/blink/renderer/platform/media/webaudiosourceprovider_impl_test.cc
@@ -7,6 +7,7 @@
 #include "base/bind.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
+#include "media/base/audio_glitch_info.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/fake_audio_render_callback.h"
 #include "media/base/media_util.h"
@@ -239,7 +240,7 @@
 
   // Ensure volume adjustment is working.
   fake_callback_.reset();
-  fake_callback_.Render(base::TimeDelta(), base::TimeTicks::Now(), 0,
+  fake_callback_.Render(base::TimeDelta(), base::TimeTicks::Now(), {},
                         bus2.get());
   bus2->Scale(kTestVolume);
 
@@ -259,10 +260,10 @@
   // configuring the fake callback to return half the data.  After these calls
   // bus1 is full of junk data, and bus2 is partially filled.
   wasp_impl_->SetVolume(1);
-  fake_callback_.Render(base::TimeDelta(), base::TimeTicks::Now(), 0,
+  fake_callback_.Render(base::TimeDelta(), base::TimeTicks::Now(), {},
                         bus1.get());
   fake_callback_.reset();
-  fake_callback_.Render(base::TimeDelta(), base::TimeTicks::Now(), 0,
+  fake_callback_.Render(base::TimeDelta(), base::TimeTicks::Now(), {},
                         bus2.get());
   bus2->ZeroFramesPartial(bus2->frames() / 2,
                           bus2->frames() - bus2->frames() / 2);
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 264f217..95156ba2 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -243,6 +243,9 @@
 
 crbug.com/1343590 virtual/prefetch/external/wpt/speculation-rules/prefetch/redirect-url.https.html [ Failure ]
 
+# Test might be flaky on Mac12-arm64 as the test server causes network errors probably becauuse of lack of resources on the bot.
+crbug.com/1382361 [ Mac ] external/wpt/speculation-rules/prerender/csp-script-src-self.html [ Failure Pass ]
+
 # These tests should be revisited after the eagerness api for speculation rules is implemented.
 crbug.com/1374023 virtual/prefetch/external/wpt/speculation-rules/prefetch/document-rules.https.html* [ Failure ]
 crbug.com/1374023 virtual/prefetch/external/wpt/speculation-rules/prefetch/document-rules.https.html?include=invalidPredicate [ Pass ]
@@ -6988,4 +6991,4 @@
 crbug.com/1372007 http/tests/devtools/application-panel/resources-panel-on-navigation.js [ Failure Pass ]
 crbug.com/1372007 http/tests/devtools/application-panel/resources-panel-resource-preview.js [ Failure Pass ]
 crbug.com/1372007 http/tests/devtools/application-panel/resources-panel-selection-on-reload.js [ Failure Pass ]
-crbug.com/1372007 http/tests/devtools/application-panel/resources-panel-websql.js [ Failure Pass ]
\ No newline at end of file
+crbug.com/1372007 http/tests/devtools/application-panel/resources-panel-websql.js [ Failure Pass ]
diff --git a/third_party/blink/web_tests/accessibility/aom-decrement-action.html b/third_party/blink/web_tests/accessibility/aom-decrement-action.html
index ceefabd..8e75bb3 100644
--- a/third_party/blink/web_tests/accessibility/aom-decrement-action.html
+++ b/third_party/blink/web_tests/accessibility/aom-decrement-action.html
@@ -30,11 +30,16 @@
     target.accessibleNode.onaccessibledecrement = function() {
       gotAccessibleEvent = true;
     };
+
+    // Finish the test after the synthesized keyup event, which is triggered
+    // 100ms later, to prevent leaks.
+    target.addEventListener("keyup",
+        t.step_func_done((event) => {}), { once: true });
+
     axTarget.decrement();
 
     assert_true(gotAccessibleEvent);
     assert_equals(target.value, "2");
-    t.done();
   });
 }, "AccessibleNode.onaccessibledecrement");
 </script>
diff --git a/third_party/blink/web_tests/accessibility/aom-increment-action.html b/third_party/blink/web_tests/accessibility/aom-increment-action.html
index 2f15097..193e10d 100644
--- a/third_party/blink/web_tests/accessibility/aom-increment-action.html
+++ b/third_party/blink/web_tests/accessibility/aom-increment-action.html
@@ -30,11 +30,16 @@
     target.accessibleNode.onaccessibleincrement = function() {
       gotAccessibleEvent = true;
     };
+
+    // Finish the test after the synthesized keyup event, which is triggered
+    // 100ms later, to prevent leaks.
+    target.addEventListener("keyup",
+        t.step_func_done((event) => {}), { once: true });
+
     axTarget.increment();
 
     assert_true(gotAccessibleEvent);
     assert_equals(target.value, "4");
-    t.done();
   });
 }, "AccessibleNode.onaccessibleincrement");
 </script>
diff --git a/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html b/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html
index 93a3bd94..f158e0e 100644
--- a/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html
+++ b/third_party/blink/web_tests/accessibility/aria-slider-increment-decrement.html
@@ -15,9 +15,21 @@
        aria-valuemin="0"
        aria-valuemax="10"
        aria-valuenow="5"></div>
+  <div id="slider-rtl"
+       class="rtl"
+       role="slider"
+       aria-valuemin="0"
+       aria-valuemax="10"
+       aria-valuenow="5"></div>
+  <div id="slider-vertical"
+       role="slider"
+       aria-orientation="vertical"
+       aria-valuemin="0"
+       aria-valuemax="10"
+       aria-valuenow="5"></div>
 
 <script>
-  function checkEvent(event, expected_key) {
+  function checkEvent(event, target, expected_key) {
     if (expected_key == "ArrowUp") {
       assert_equals(event.code, "ArrowUp", "event.code on " + event.type);
       assert_equals(event.key, "ArrowUp", "event.key on " + event.type);
@@ -42,132 +54,123 @@
 
     assert_true(event.isTrusted, "event.isTrusted on " + event.type);
     assert_equals(event.charCode, 0, "event.charCode on " + event.type);
-    assert_equals(event.target, slider, "event.target on " + event.type);
-    assert_equals(event.srcElement, slider, "event.srcElement on " + event.type);
+    assert_equals(event.target, target, "event.target on " + event.type);
+    assert_equals(event.srcElement, target, "event.srcElement on " + event.type);
     assert_true(event.bubbles, "event.bubbles on " + event.type);
     assert_false(event.defaultPrevented, "event.defaultPrevented on " + event.type);
   }
 
-  var slider = document.getElementById("slider");
-  var axSlider = accessibilityController.accessibleElementById("slider");
+  // Test horizontal, left-to-right slider
 
-  // Tests for horizontal, left-to-right slider
+  async_test(function(t) {
+    var slider = document.getElementById("slider");
+    var axSlider = accessibilityController.accessibleElementById("slider");
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowRight");
-    });
-  }, "check that sending an increment event to an ARIA slider generates a right arrow keydown event");
+    // Listener for keydown event after increment action.
+    slider.addEventListener("keydown", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowRight");
+    }), { once: true });
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowRight");
-    });
-  }, "check that sending an increment event to an ARIA slider generates a right arrow keyup event");
+    // Listener for keyup event after increment action.
+    // It sets up a new set of listeners and runs the decrement action.
+    slider.addEventListener("keyup", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowRight");
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowLeft");
-    });
-  }, "check that sending a decrement event to an ARIA slider generates a left arrow keydown event");
+      // Listener for keydown event after decrement action.
+      slider.addEventListener("keydown", t.step_func((event) => {
+        checkEvent(event, slider, "ArrowLeft");
+      }), { once: true });
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowLeft");
-    });
-  }, "check that sending a decrement event to an ARIA slider generates a left arrow keyup event");
+      // Listener for keyup event after decrement action.
+      slider.addEventListener("keyup", t.step_func_done((event) => {
+        checkEvent(event, slider, "ArrowLeft");
+      }), { once: true });
 
-  // Tests for horizontal, right-to-left slider
+      window.setTimeout(() => {
+        axSlider.decrement();
+      }, 0);
+    }), { once: true });
 
-  promise_test(function(t) {
-    slider.classList.toggle("rtl");
+    window.setTimeout(
+        t.unreached_func("didn't get all key events within 1000ms"), 1000);
 
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowLeft");
-    });
-  }, "check that sending an increment event to a RTL ARIA slider generates a left arrow keydown event");
+    axSlider.increment();
+  }, "Check that running increment and decrement actions on an ARIA slider generates the corresponding keydown and keyup events");
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowLeft");
-    });
-  }, "check that sending an increment event to a RTL ARIA slider generates a left arrow keyup event");
+  // Test horizontal, right-to-left slider
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowRight");
-    });
-  }, "check that sending a decrement event to a RTL ARIA slider generates a right arrow keydown event");
+  async_test(function(t) {
+    var slider = document.getElementById("slider-rtl");
+    var axSlider = accessibilityController.accessibleElementById("slider-rtl");
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowRight");
-    });
-  }, "check that sending a decrement event to a RTL ARIA slider generates a right arrow keyup event");
+    // Listener for keydown event after increment action.
+    slider.addEventListener("keydown", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowLeft");
+    }), { once: true });
 
-  // Tests for vertical slider
+    // Listener for keyup event after increment action.
+    // It sets up a new set of listeners and runs the decrement action.
+    slider.addEventListener("keyup", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowLeft");
 
-  promise_test(function(t) {
-    slider.setAttribute('aria-orientation', 'vertical');
+      // Listener for keydown event after decrement action.
+      slider.addEventListener("keydown", t.step_func((event) => {
+        checkEvent(event, slider, "ArrowRight");
+      }), { once: true });
 
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowUp");
-    });
-  }, "check that sending an increment event to a vertical ARIA slider generates an up arrow keydown event");
+      // Listener for keyup event after decrement action.
+      slider.addEventListener("keyup", t.step_func_done((event) => {
+        checkEvent(event, slider, "ArrowRight");
+        t.done();
+      }), { once: true });
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowUp");
-    });
-  }, "check that sending an increment event to a vertical ARIA slider generates an up arrow keyup event");
+      window.setTimeout(() => {
+        axSlider.decrement();
+      }, 0);
+    }), { once: true });
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowDown");
-    });
-  }, "check that sending a decrement event to a vertical ARIA slider generates a down arrow keydown event");
+    window.setTimeout(
+        t.unreached_func("didn't get all key events within 1000ms"), 1000);
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowDown");
-    });
-  }, "check that sending a decrement event to a vertical ARIA slider generates a down arrow keyup event");
+    axSlider.increment();
+  }, "Check that running increment and decrement actions on a RTL ARIA slider generates the corresponding keydown and keyup events");
+
+  // Test vertical slider
+
+  async_test(function(t) {
+    var slider = document.getElementById("slider-vertical");
+    var axSlider = accessibilityController.accessibleElementById("slider-vertical");
+
+    // Listener for keydown event after increment action.
+    slider.addEventListener("keydown", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowUp");
+    }), { once: true });
+
+    // Listener for keyup event after increment action.
+    // It sets up a new set of listeners and runs the decrement action.
+    slider.addEventListener("keyup", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowUp");
+
+      // Listener for keydown event after decrement action.
+      slider.addEventListener("keydown", t.step_func((event) => {
+        checkEvent(event, slider, "ArrowDown");
+      }), { once: true });
+
+      // Listener for keyup event after decrement action.
+      slider.addEventListener("keyup", t.step_func_done((event) => {
+        checkEvent(event, slider, "ArrowDown");
+      }), { once: true });
+
+      window.setTimeout(() => {
+        axSlider.decrement();
+      }, 0);
+    }), { once: true });
+
+    window.setTimeout(
+        t.unreached_func("didn't get all key events within 1000ms"), 1000);
+
+    axSlider.increment();
+  }, "Check that running increment and decrement actions on a vertical ARIA slider generates the corresponding keydown and keyup events");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/accessibility/input-date-time-default-action.html b/third_party/blink/web_tests/accessibility/input-date-time-default-action.html
new file mode 100644
index 0000000..ab1310e0
--- /dev/null
+++ b/third_party/blink/web_tests/accessibility/input-date-time-default-action.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<script src="../resources/gc.js"></script>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+
+<input id="dateInput" type="date">
+<input id="timeInput" type="time">
+
+<script>
+test(function(t) {
+  const axInputElement =
+      accessibilityController.accessibleElementById("dateInput");
+  const axButton = axInputElement.childAtIndex(1);
+  assert_true(axButton.hasDefaultAction());
+
+}, "Test there is a default action for the button in input type=date");
+
+test(function(t) {
+  const axInputElement =
+      accessibilityController.accessibleElementById("timeInput");
+  const axButton = axInputElement.childAtIndex(1);
+  assert_true(axButton.hasDefaultAction());
+
+}, "Test there is a default action for the button in input type=time");
+</script>
diff --git a/third_party/blink/web_tests/accessibility/input-type-range-value-change-expected.txt b/third_party/blink/web_tests/accessibility/input-type-range-value-change-expected.txt
index fdd4553..8f13b99 100644
--- a/third_party/blink/web_tests/accessibility/input-type-range-value-change-expected.txt
+++ b/third_party/blink/web_tests/accessibility/input-type-range-value-change-expected.txt
@@ -17,6 +17,7 @@
 PASS range3.intValue is 20
 PASS range3.intValue is 15
 PASS range3.intValue is 20
+PASS eventsTriggered became 12
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/accessibility/input-type-range-value-change.html b/third_party/blink/web_tests/accessibility/input-type-range-value-change.html
index 54d6fc6..2b5d80a 100644
--- a/third_party/blink/web_tests/accessibility/input-type-range-value-change.html
+++ b/third_party/blink/web_tests/accessibility/input-type-range-value-change.html
@@ -10,12 +10,17 @@
 <input id="range3" type="range" min="0" max="50" value="10" step="5">
 
 <script>
+var jsTestIsAsync = true;
 
 description("Check whether changing slider's value without step attribute works properly");
 
 if (window.accessibilityController) {
     // Checking whether step is greater than one
     var range1 = accessibilityController.accessibleElementById("range1");
+    var eventsTriggered = 0;
+    document.getElementById("range1").addEventListener("keyup", (event) => {
+      eventsTriggered++;
+    });
     shouldBe("range1.intValue", "50");
     range1.increment();
     shouldBe("range1.intValue", "51");
@@ -27,6 +32,9 @@
     shouldBe("range1.intValue", "52");
 
     var range2 = accessibilityController.accessibleElementById("range2");
+    document.getElementById("range2").addEventListener("keyup", (event) => {
+      eventsTriggered++;
+    });
     shouldBe("range2.intValue", "5");
     range2.increment();
     shouldBe("range2.intValue", "6");
@@ -38,6 +46,9 @@
     shouldBe("range2.intValue", "7");
 
     var range3 = accessibilityController.accessibleElementById("range3");
+    document.getElementById("range3").addEventListener("keyup", (event) => {
+      eventsTriggered++;
+    });
     shouldBe("range3.intValue", "10");
     range3.increment();
     shouldBe("range3.intValue", "15");
@@ -47,6 +58,10 @@
     shouldBe("range3.intValue", "15");
     range3.increment();
     shouldBe("range3.intValue", "20");
+
+    // Make sure all synthesized keydown/up events have already passed, so the
+    // test doesn't leak.
+    shouldBecomeEqual("eventsTriggered", "12", finishJSTest, 1000);
 }
 </script>
 </body>
diff --git a/third_party/blink/web_tests/accessibility/native-slider-synthesized-events-on-action.html b/third_party/blink/web_tests/accessibility/native-slider-synthesized-events-on-action.html
index a53bc0c..ae2d215 100644
--- a/third_party/blink/web_tests/accessibility/native-slider-synthesized-events-on-action.html
+++ b/third_party/blink/web_tests/accessibility/native-slider-synthesized-events-on-action.html
@@ -11,9 +11,10 @@
 </head>
 <body>
   <input id="slider" type="range" value="0" step="1"/>
+  <input id="slider-rtl" class="rtl" type="range" value="0" step="1"/>
 
 <script>
-  function checkEvent(event, expected_key) {
+  function checkEvent(event, target, expected_key) {
     if (expected_key == "ArrowUp") {
       assert_equals(event.code, "ArrowUp", "event.code on " + event.type);
       assert_equals(event.key, "ArrowUp", "event.key on " + event.type);
@@ -38,104 +39,94 @@
 
     assert_true(event.isTrusted, "event.isTrusted on " + event.type);
     assert_equals(event.charCode, 0, "event.charCode on " + event.type);
-    assert_equals(event.target, slider, "event.target on " + event.type);
-    assert_equals(event.srcElement, slider, "event.srcElement on " + event.type);
+    assert_equals(event.target, target, "event.target on " + event.type);
+    assert_equals(event.srcElement, target, "event.srcElement on " + event.type);
     assert_true(event.bubbles, "event.bubbles on " + event.type);
     assert_false(event.defaultPrevented, "event.defaultPrevented on " + event.type);
   }
 
-  var slider = document.getElementById("slider");
-  var axSlider = accessibilityController.accessibleElementById("slider");
-
   test(function(t) {
     assert_true(internals.runtimeFlags.synthesizedKeyboardEventsForAccessibilityActionsEnabled);
   }, "Make sure that keyboard event synthesis is enabled");
 
-  // Tests for left-to-right slider
+  // Test left-to-right slider
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowRight");
+  async_test(function(t) {
+    var slider = document.getElementById("slider");
+    var axSlider = accessibilityController.accessibleElementById("slider");
+
+    // Listener for keydown event after increment action.
+    slider.addEventListener("keydown", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowRight");
+    }), { once: true });
+
+    // Listener for keyup event after increment action.
+    // It sets up a new set of listeners and runs the decrement action.
+    slider.addEventListener("keyup", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowRight");
       assert_equals(slider.value, "1", "slider value after increase");
-    });
-  }, "check that sending an increment event to a native slider generates a right arrow keydown event");
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowRight");
-      assert_equals(slider.value, "2", "slider value after increase");
-    });
-  }, "check that sending an increment event to a native slider generates a right arrow keyup event");
+      // Listener for keydown event after decrement action.
+      slider.addEventListener("keydown", t.step_func((event) => {
+        checkEvent(event, slider, "ArrowLeft");
+      }), { once: true });
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowLeft");
-      assert_equals(slider.value, "1", "slider value after decrease");
-    });
-  }, "check that sending a decrement event to a native slider generates a left arrow keydown event");
+      // Listener for keyup event after decrement action.
+      slider.addEventListener("keyup", t.step_func_done((event) => {
+        checkEvent(event, slider, "ArrowLeft");
+        assert_equals(slider.value, "0", "slider value after decrease");
+      }), { once: true });
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowLeft");
-      assert_equals(slider.value, "0", "slider value after decrease");
-    });
-  }, "check that sending a decrement event to a native slider generates a left arrow keyup event");
+      window.setTimeout(() => {
+        axSlider.decrement();
+      }, 0);
+    }), { once: true });
 
-  // Tests for right-to-left slider
+    window.setTimeout(
+        t.unreached_func("didn't get all key events within 1000ms"), 1000);
 
-  promise_test(function(t) {
-    slider.classList.toggle("rtl");
+    axSlider.increment();
+  }, "Check that running increment and decrement actions on a native slider generates the corresponding keydown and keyup events");
 
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowLeft");
+  // Test right-to-left slider
+
+  async_test(function(t) {
+    var slider = document.getElementById("slider-rtl");
+    var axSlider = accessibilityController.accessibleElementById("slider-rtl");
+
+    // Listener for keydown event after increment action.
+    slider.addEventListener("keydown", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowLeft");
+    }), { once: true });
+
+    // Listener for keyup event after increment action.
+    // It sets up a new set of listeners and runs the decrement action.
+    slider.addEventListener("keyup", t.step_func((event) => {
+      checkEvent(event, slider, "ArrowLeft");
       assert_equals(slider.value, "1", "slider value after increase");
-    });
-  }, "check that sending an increment event to a RTL native slider generates a left arrow keydown event");
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.increment();
-    }).then(event => {
-      checkEvent(event, "ArrowLeft");
-      assert_equals(slider.value, "2", "slider value after increase");
-    });
-  }, "check that sending an increment event to a RTL native slider generates a left arrow keyup event");
+      // Listener for keydown event after decrement action.
+      slider.addEventListener("keydown", t.step_func((event) => {
+        checkEvent(event, slider, "ArrowRight");
+      }), { once: true });
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keydown", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowRight");
-      assert_equals(slider.value, "1", "slider value after decrease");
-    });
-  }, "check that sending a decrement event to a RTL native slider generates a right arrow keydown event");
+      // Listener for keyup event after decrement action.
+      slider.addEventListener("keyup", t.step_func_done((event) => {
+        checkEvent(event, slider, "ArrowRight");
+        assert_equals(slider.value, "0", "slider value after decrease");
+        t.done();
+      }), { once: true });
 
-  promise_test(function(t) {
-    return new Promise(resolve => {
-      slider.addEventListener("keyup", resolve);
-      axSlider.decrement();
-    }).then(event => {
-      checkEvent(event, "ArrowRight");
-      assert_equals(slider.value, "0", "slider value after decrease");
-    });
-  }, "check that sending a decrement event to a RTL native slider generates a right arrow keyup event");
+      window.setTimeout(() => {
+        axSlider.decrement();
+      }, 0);
+    }), { once: true });
+
+    window.setTimeout(
+        t.unreached_func("didn't get all key events within 1000ms"), 1000);
+
+    axSlider.increment();
+  }, "Check that running increment and decrement actions on a RTL native slider generates the corresponding keydown and keyup events");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/css/mediaqueries/test_media_queries-expected.txt b/third_party/blink/web_tests/external/wpt/css/mediaqueries/test_media_queries-expected.txt
index 9cb52362..940379e7 100644
--- a/third_party/blink/web_tests/external/wpt/css/mediaqueries/test_media_queries-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/mediaqueries/test_media_queries-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 1410 tests; 1379 PASS, 31 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 1410 tests; 1395 PASS, 15 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS query_should_be_parseable: (orientation)
 PASS query_should_be_parseable: not (orientation)
 PASS expression_should_be_known: (orientation)
@@ -1116,14 +1116,14 @@
 PASS expression_should_be_known: max-aspect-ratio: 1  / 	
 1
 PASS expression_should_be_known: max-aspect-ratio: 1/\r1
-FAIL expression_should_be_known: max-aspect-ratio: 1 assert_true: expected true got false
-FAIL expression_should_be_known: max-aspect-ratio: 0.5 assert_true: expected true got false
-FAIL expression_should_be_known: max-aspect-ratio: 1.0/1 assert_true: expected true got false
-FAIL expression_should_be_known: max-aspect-ratio: 1/1.0 assert_true: expected true got false
-FAIL expression_should_be_known: max-aspect-ratio: 1.0/1.0 assert_true: expected true got false
-FAIL expression_should_be_known: max-aspect-ratio: 0/1 assert_true: expected true got false
-FAIL expression_should_be_known: max-aspect-ratio: 1/0 assert_true: expected true got false
-FAIL expression_should_be_known: max-aspect-ratio: 0/0 assert_true: expected true got false
+PASS expression_should_be_known: max-aspect-ratio: 1
+PASS expression_should_be_known: max-aspect-ratio: 0.5
+PASS expression_should_be_known: max-aspect-ratio: 1.0/1
+PASS expression_should_be_known: max-aspect-ratio: 1/1.0
+PASS expression_should_be_known: max-aspect-ratio: 1.0/1.0
+PASS expression_should_be_known: max-aspect-ratio: 0/1
+PASS expression_should_be_known: max-aspect-ratio: 1/0
+PASS expression_should_be_known: max-aspect-ratio: 0/0
 PASS expression_should_be_parseable: max-aspect-ratio: -1/1
 PASS expression_should_be_unknown: max-aspect-ratio: -1/1
 PASS expression_should_be_parseable: max-aspect-ratio: 1/-1
@@ -1141,14 +1141,14 @@
 PASS expression_should_be_known: device-aspect-ratio: 1  / 	
 1
 PASS expression_should_be_known: device-aspect-ratio: 1/\r1
-FAIL expression_should_be_known: device-aspect-ratio: 1 assert_true: expected true got false
-FAIL expression_should_be_known: device-aspect-ratio: 0.5 assert_true: expected true got false
-FAIL expression_should_be_known: device-aspect-ratio: 1.0/1 assert_true: expected true got false
-FAIL expression_should_be_known: device-aspect-ratio: 1/1.0 assert_true: expected true got false
-FAIL expression_should_be_known: device-aspect-ratio: 1.0/1.0 assert_true: expected true got false
-FAIL expression_should_be_known: device-aspect-ratio: 0/1 assert_true: expected true got false
-FAIL expression_should_be_known: device-aspect-ratio: 1/0 assert_true: expected true got false
-FAIL expression_should_be_known: device-aspect-ratio: 0/0 assert_true: expected true got false
+PASS expression_should_be_known: device-aspect-ratio: 1
+PASS expression_should_be_known: device-aspect-ratio: 0.5
+PASS expression_should_be_known: device-aspect-ratio: 1.0/1
+PASS expression_should_be_known: device-aspect-ratio: 1/1.0
+PASS expression_should_be_known: device-aspect-ratio: 1.0/1.0
+PASS expression_should_be_known: device-aspect-ratio: 0/1
+PASS expression_should_be_known: device-aspect-ratio: 1/0
+PASS expression_should_be_known: device-aspect-ratio: 0/0
 PASS expression_should_be_parseable: device-aspect-ratio: -1/1
 PASS expression_should_be_unknown: device-aspect-ratio: -1/1
 PASS expression_should_be_parseable: device-aspect-ratio: 1/-1
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-shorthand.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-shorthand.html
index efb7d2c..832b3e0 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-shorthand.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-shorthand.html
@@ -10,45 +10,23 @@
 <script>
 test_valid_value('animation',
   '1s linear 1s 2 reverse forwards paused anim');
-test_valid_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim timeline');
-test_valid_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim none');
-test_valid_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim "initial"');
-test_valid_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim auto',
-  '1s linear 1s 2 reverse forwards paused anim');
-test_valid_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim scroll()');
 
 test_invalid_value('animation',
   '1s linear 1s 2 reverse forwards paused anim initial');
 test_invalid_value('animation',
   '1s linear 1s 2 reverse forwards paused anim 2000');
 test_invalid_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim scroll(abc block)');
+  '1s linear 1s 2 reverse forwards paused anim scroll()');
 test_invalid_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim scroll(inline abc)');
-test_invalid_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim scroll(abc)');
-test_invalid_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim scroll("string")');
+  '1s linear 1s 2 reverse forwards paused anim timeline');
 
 test_computed_value('animation',
   '1s linear 1s 2 reverse forwards paused anim');
-test_computed_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim timeline');
-test_computed_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim none');
-test_computed_value('animation',
-  '1s linear 1s 2 reverse forwards paused anim auto',
-  '1s linear 1s 2 reverse forwards paused anim');
 
 test_shorthand_value('animation',
-  `1s linear 1s 2 reverse forwards paused anim1 timeline,
-   1s linear 1s 2 reverse forwards paused anim2 none,
-   1s linear 1s 2 reverse forwards paused anim3 auto`,
+  `1s linear 1s 2 reverse forwards paused anim1,
+   1s linear 1s 2 reverse forwards paused anim2,
+   1s linear 1s 2 reverse forwards paused anim3`,
 {
   'animation-duration': '1s, 1s, 1s',
   'animation-timing-function': 'linear, linear, linear',
@@ -58,6 +36,6 @@
   'animation-fill-mode': 'forwards, forwards, forwards',
   'animation-play-state': 'paused, paused, paused',
   'animation-name': 'anim1, anim2, anim3',
-  'animation-timeline': 'timeline, none, auto'
+  'animation-timeline': 'auto, auto, auto'
 });
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html
index 6b7bab9..9f55e2d2 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html
@@ -39,6 +39,8 @@
 <script>
 "use strict";
 
+setup(assert_implements_animation_timeline);
+
 function createScroller(t, scrollerSizeClass) {
   let scroller = document.createElement('div');
   let className = scrollerSizeClass || 'square';
@@ -89,7 +91,8 @@
   target.appendChild(content);
 
   target.style.scrollTimelineName = 'timeline';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   target.scrollTop = 50; // 50%, in [0, 100].
   await waitForNextFrame();
@@ -111,7 +114,8 @@
   parent.insertBefore(target, parent.firstElementChild);
 
   parent.style.scrollTimelineName = 'timeline';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   parent.scrollTop = 100; // 50%, in [0, 200].
   await waitForNextFrame();
@@ -128,7 +132,8 @@
   document.body.appendChild(target);
 
   sibling.style.scrollTimelineName = 'timeline';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   sibling.scrollTop = 50; // 50%, in [0, 100].
   await waitForNextFrame();
@@ -149,7 +154,8 @@
   parent.appendChild(target);
 
   sibling.style.scrollTimelineName = 'timeline';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   sibling.scrollTop = 50; // 50%, in [0, 100].
   await waitForNextFrame();
@@ -170,7 +176,8 @@
   document.body.appendChild(sibling);
 
   sibling.style.scrollTimelineName = 'timeline';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   sibling.scrollTop = 50; // 50%, in [0, 100].
   await waitForNextFrame();
@@ -199,7 +206,8 @@
   parent.style.scrollTimelineName = 'timeline';
   parent.style.scrollTimelineAxis = 'inline';
   sibling.style.scrollTimelineName = 'timeline';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   parent.scrollTop = 50; // 25%, in [0, 200].
   sibling.scrollTop = 50; // 50%, in [0, 100].
@@ -224,7 +232,8 @@
   document.body.appendChild(target);
 
   sibling.style.scrollTimelineName = 'timeline';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   sibling.scrollTop = 50; // 50%, in [0, 100].
   await waitForNextFrame();
@@ -248,7 +257,8 @@
   main.appendChild(sibling);
   main.appendChild(target);
 
-  target.style.animation = 'anim 10s linear timeline';
+  target.style.animation = 'anim 10s linear';
+  target.style.animationTimeline = 'timeline';
   sibling.scrollTop = 50; // 50%, in [50, 150].
   await waitForNextFrame();
 
@@ -270,7 +280,8 @@
   // <div id='target'></div>
   document.body.appendChild(target);
 
-  target.style.animation = 'anim 10s linear timeline';
+  target.style.animation = 'anim 10s linear';
+  target.style.animationTimeline = 'timeline';
 
   // Unknown animation-timeline, current time held at zero.
   assert_equals(getComputedStyle(target).translate, '50px');
@@ -302,7 +313,8 @@
   await waitForNextFrame();
 
   scroller.style.scrollTimelineName = 'timeline';
-  target.style.animation = 'anim 10s linear timeline';
+  target.style.animation = 'anim 10s linear';
+  target.style.animationTimeline = 'timeline';
   await waitForCSSScrollTimelineStyle();
 
   assert_equals(getComputedStyle(target).translate, '100px');
@@ -329,7 +341,8 @@
   document.body.appendChild(target);
 
   scroller.style.scrollTimelineName = 'timeline';
-  target.style.animation = 'anim 10s linear timeline';
+  target.style.animation = 'anim 10s linear';
+  target.style.animationTimeline = 'timeline';
 
   // Unknown animation-timeline, current time held at zero.
   assert_equals(getComputedStyle(target).translate, '50px');
@@ -353,7 +366,8 @@
   await waitForNextFrame();
 
   scroller.style.scrollTimelineName = 'timeline';
-  target.style.animation = 'anim 10s linear timeline';
+  target.style.animation = 'anim 10s linear';
+  target.style.animationTimeline = 'timeline';
   await waitForCSSScrollTimelineStyle();
 
   assert_equals(getComputedStyle(target).translate, '100px');
@@ -377,7 +391,8 @@
 
   scroller.style.scrollTimelineName = 'timeline';
   scroller.style.display = 'none';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   await waitForNextFrame();
 
@@ -407,7 +422,8 @@
 
   scroller.style.scrollTimelineName = 'timeline-A';
   scroller.scrollTop = 50;
-  target.style.animation = "anim 10s linear timeline-B";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline-B';
 
   await waitForNextFrame();
 
@@ -436,7 +452,8 @@
 
   scroller.style.scrollTimelineName = 'timeline-A';
   scroller.scrollTop = 50;
-  target.style.animation = "anim 10s linear timeline-A";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline-A';
 
   await waitForNextFrame();
 
@@ -458,7 +475,8 @@
   let scroller1 = createScroller(t);
   let scroller2 = createScroller(t);
 
-  target.style.animation = 'anim 10s linear timeline';
+  target.style.animation = 'anim 10s linear';
+  target.style.animationTimeline = 'timeline';
   scroller1.style.scrollTimelineName = 'timeline';
   scroller2.style.scrollTimelineName = 'timeline';
   scroller1.id = 'A';
@@ -496,7 +514,8 @@
   let target = createTarget(t);
   let scroller1 = createScroller(t);
 
-  target.style.animation = 'anim 10s linear timeline';
+  target.style.animation = 'anim 10s linear';
+  target.style.animationTimeline = 'timeline';
   scroller1.style.scrollTimelineName = 'timeline';
   scroller1.id = 'A';
 
@@ -541,7 +560,8 @@
 
   // <div id='target'></div>
   document.body.append(target);
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   await waitForNextFrame();
 
@@ -579,7 +599,8 @@
   document.body.appendChild(target);
 
   scroller.style.scrollTimeline = 'timeline block';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   scroller.scrollLeft = 50;
   await waitForNextFrame();
@@ -594,7 +615,8 @@
   document.body.appendChild(target);
 
   scroller.style.scrollTimeline = 'timeline inline';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   scroller.scrollTop = 50;
   await waitForNextFrame();
@@ -609,7 +631,8 @@
   document.body.appendChild(target);
 
   scroller.style.scrollTimeline = 'timeline horizontal';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   scroller.scrollLeft = 50;
   await waitForNextFrame();
@@ -624,7 +647,8 @@
   document.body.appendChild(target);
 
   scroller.style.scrollTimeline = 'timeline vertical';
-  target.style.animation = "anim 10s linear timeline";
+  target.style.animation = "anim 10s linear";
+  target.style.animationTimeline = 'timeline';
 
   scroller.scrollTop = 50;
   await waitForNextFrame();
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-scroll-functional-notation.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-scroll-functional-notation.tentative.html
index a721c66e..e28e852 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-scroll-functional-notation.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-scroll-functional-notation.tentative.html
@@ -6,6 +6,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes anim {
     from { translate: 50px; }
@@ -43,6 +44,8 @@
 <script>
 "use strict";
 
+setup(assert_implements_animation_timeline);
+
 const root = document.scrollingElement;
 const createTargetWithStuff = function(t, contentClass) {
   let container = document.createElement('div');
@@ -83,7 +86,8 @@
 
 promise_test(async t => {
   let [container, div] = createTargetWithStuff(t, 'block-content');
-  div.style.animation = "anim 10s linear scroll(nearest)";
+  div.style.animation = "anim 10s linear";
+  div.style.animationTimeline = "scroll(nearest)";
 
   await scrollTop(root, 50);
   assert_equals(getComputedStyle(div).translate, '50px');
@@ -96,7 +100,8 @@
 
 promise_test(async t => {
   let [container, div] = createTargetWithStuff(t, 'block-content');
-  div.style.animation = "anim 10s linear scroll(root)";
+  div.style.animation = "anim 10s linear";
+  div.style.animationTimeline = "scroll(root)";
 
   await scrollTop(container, 50);
   assert_equals(getComputedStyle(div).translate, '50px');
@@ -109,7 +114,8 @@
 
 promise_test(async t => {
   let [container, div] = createTargetWithStuff(t, 'inline-content');
-  div.style.animation = "anim 10s linear scroll(inline)";
+  div.style.animation = "anim 10s linear";
+  div.style.animationTimeline = "scroll(inline)";
 
   await scrollLeft(container, 50);
   assert_equals(getComputedStyle(div).translate, '100px');
@@ -118,7 +124,8 @@
 promise_test(async t => {
   let [container, div] = createTargetWithStuff(t, 'block-content');
   container.style.writingMode = 'vertical-lr';
-  div.style.animation = "anim 10s linear scroll(horizontal)";
+  div.style.animation = "anim 10s linear";
+  div.style.animationTimeline = "scroll(horizontal)";
 
   await scrollLeft(container, 50);
   assert_equals(getComputedStyle(div).translate, '100px');
@@ -127,7 +134,8 @@
 promise_test(async t => {
   let [container, div] = createTargetWithStuff(t, 'inline-content');
   container.style.writingMode = 'vertical-lr';
-  div.style.animation = "anim 10s linear scroll(vertical)";
+  div.style.animation = "anim 10s linear";
+  div.style.animationTimeline = "scroll(vertical)";
 
   await scrollTop(container, 50);
   assert_equals(getComputedStyle(div).translate, '100px');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/get-animations-inactive-timeline.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/get-animations-inactive-timeline.html
index 71a66cae..10bf00f 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/get-animations-inactive-timeline.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/get-animations-inactive-timeline.html
@@ -6,6 +6,7 @@
  href="https://www.w3.org/TR/web-animations-1/#animation-effect-phases-and-states">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes slide {
     from { transform: translateX(100px);  }
@@ -26,7 +27,8 @@
     background-color:  green;
     height:  100px;
     width:  100px;
-    animation:  slide 1s linear timeline;
+    animation:  slide 1s linear;
+    animation-timeline: timeline;
   }
 </style>
 <body>
@@ -36,6 +38,8 @@
   </div>
 </body>
 <script type="text/javascript">
+  setup(assert_implements_animation_timeline);
+
   promise_test(async t => {
     // Newly created timeline is inactive,
     let animations = document.getAnimations();
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html
index f91be8d..f4f9a66 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/progress-based-animation-animation-longhand-properties.tentative.html
@@ -28,6 +28,8 @@
 <script>
 "use strict";
 
+setup(assert_implements_animation_timeline);
+
 const createTargetAndScroller = function(t) {
   let container = document.createElement('div');
   container.id = 'container';
@@ -69,7 +71,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim scroll(nearest)';
+  target.style.animation = '10s linear anim';
+  target.style.animationTimeline = 'scroll(nearest)';
 
   await scrollTop(scroller, 25); // [0, 100].
   assert_equals(getComputedStyle(target).translate, '25px');
@@ -77,7 +80,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '0s linear anim forwards scroll(nearest)';
+  target.style.animation = '0s linear anim forwards';
+  target.style.animationTimeline = 'scroll(nearest)';
 
   await scrollTop(scroller, 25); // [0, 100].
   assert_equals(getComputedStyle(target).translate, '100px');
@@ -90,7 +94,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim scroll(nearest)';
+  target.style.animation = '10s linear anim';
+  target.style.animationTimeline = 'scroll(nearest)';
 
   await scrollTop(scroller, 25); // [0, 100].
   assert_equals(getComputedStyle(target).translate, '25px');
@@ -108,7 +113,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim forwards scroll(nearest)';
+  target.style.animation = '10s linear anim forwards';
+  target.style.animationTimeline = 'scroll(nearest)';
   target.style.animationIterationCount = '0';
 
   await scrollTop(scroller, 25); // [0, 100].
@@ -117,7 +123,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim forwards scroll(nearest)';
+  target.style.animation = '10s linear anim forwards';
+  target.style.animationTimeline = 'scroll(nearest)';
   target.style.animationIterationCount = 'infinite';
 
   await scrollTop(scroller, 25); // [0, 100].
@@ -131,7 +138,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim scroll(nearest)';
+  target.style.animation = '10s linear anim';
+  target.style.animationTimeline = 'scroll(nearest)';
 
   await scrollTop(scroller, 25) // [0, 100].
   assert_equals(getComputedStyle(target).translate, '25px');
@@ -139,7 +147,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim scroll(nearest)';
+  target.style.animation = '10s linear anim';
+  target.style.animationTimeline = 'scroll(nearest)';
   target.style.animationDirection = 'reverse';
 
   await scrollTop(scroller, 25); // 25% in the reversing direction.
@@ -148,7 +157,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim scroll(nearest)';
+  target.style.animation = '10s linear anim';
+  target.style.animationTimeline = 'scroll(nearest)';
   target.style.animationIterationCount = '2';
   target.style.animationDirection = 'alternate';
 
@@ -161,7 +171,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim scroll(nearest)';
+  target.style.animation = '10s linear anim';
+  target.style.animationTimeline = 'scroll(nearest)';
   target.style.animationIterationCount = '2';
   target.style.animationDirection = 'alternate-reverse';
 
@@ -179,7 +190,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim scroll(nearest)';
+  target.style.animation = '10s linear anim';
+  target.style.animationTimeline = 'scroll(nearest)';
 
   await scrollTop(scroller, 25); // [0, 100].
   assert_equals(getComputedStyle(target).translate, '25px');
@@ -204,7 +216,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim scroll(nearest)';
+  target.style.animation = '10s linear anim';
+  target.style.animationTimeline = 'scroll(nearest)';
 
   //         active
   // |--------------------|
@@ -225,7 +238,8 @@
 
 promise_test(async t => {
   let [target, scroller] = createTargetAndScroller(t);
-  target.style.animation = '10s linear anim scroll(nearest)';
+  target.style.animation = '10s linear anim';
+  target.style.animationTimeline = 'scroll(nearest)';
   target.style.animationDelay = '10s';
   target.style.animationDelayStart = '10s'; // crbug.com/1375994
 
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-document-scroller-quirks.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-document-scroller-quirks.html
index 1a9e6a2b..809a658a 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-document-scroller-quirks.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-document-scroller-quirks.html
@@ -14,7 +14,8 @@
     to { z-index: 100; }
   }
   #element {
-    animation: anim forwards scroll(root);
+    animation: anim forwards;
+    animation-timeline: scroll(root);
   }
   #spacer {
     height:  200vh;
@@ -26,6 +27,8 @@
 <script>
 'use strict';
 
+setup(assert_implements_animation_timeline);
+
 promise_test(async () => {
   await waitForCSSScrollTimelineStyle();
   assert_equals(getComputedStyle(element).zIndex, "100");
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-in-container-query.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-in-container-query.html
index 59e85bf5..d6a8ba8 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-in-container-query.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-in-container-query.html
@@ -39,7 +39,8 @@
   #element {
     height: 10px;
     width: 10px;
-    animation: expand 10s linear timeline;
+    animation: expand 10s linear;
+    animation-timeline: timeline;
     background-color: rgb(0, 0, 0);
   }
 </style>
@@ -53,6 +54,8 @@
   </div>
 </div>
 <script>
+  setup(assert_implements_animation_timeline);
+
   promise_test(async (t) => {
     element.offsetTop;
     scroller.scrollTop = 50;
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-multi-pass.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-multi-pass.tentative.html
index 0122391..91668ada 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-multi-pass.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-multi-pass.tentative.html
@@ -5,6 +5,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes expand_width {
     from { width: 100px; }
@@ -27,11 +28,13 @@
   }
   #element1 {
     width: 1px;
-    animation: expand_width 10s timeline1;
+    animation: expand_width 10s;
+    animation-timeline: timeline1;
   }
   #element2 {
     height: 1px;
-    animation: expand_height 10s timeline2;
+    animation: expand_height 10s;
+    animation-timeline: timeline2;
   }
 </style>
 <main id=main>
@@ -41,6 +44,8 @@
   </div>
 </main>
 <script>
+    setup(assert_implements_animation_timeline);
+
     function insertScroller(timeline_name) {
       let scroller = document.createElement('div');
       scroller.classList.add('scroller');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-nearest-dirty.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-nearest-dirty.html
index 6914fc6..1a79c9b 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-nearest-dirty.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-nearest-dirty.html
@@ -17,7 +17,8 @@
     height: 100px;
   }
   #element {
-    animation: anim forwards scroll();
+    animation: anim forwards;
+    animation-timeline: scroll();
   }
   #spacer {
     height: 200px;
@@ -29,6 +30,8 @@
 </div>
 
 <script>
+setup(assert_implements_animation_timeline);
+
 promise_test(async () => {
   await waitForCSSScrollTimelineStyle();
   assert_equals(getComputedStyle(element).zIndex, '100');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-paused-animations.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-paused-animations.html
index 3ae2e75e..54518a5 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-paused-animations.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-paused-animations.html
@@ -24,6 +24,8 @@
 <script>
 'use strict';
 
+setup(assert_implements_animation_timeline);
+
 async function resetScrollPosition() {
   // Reset to 0 so we don't affect following tests.
   document.scrollingElement.scrollTop = 0;
@@ -36,7 +38,8 @@
   const scroller = document.scrollingElement;
   t.add_cleanup(resetScrollPosition);
 
-  div.style.animation = 'anim 100s linear paused scroll(root)';
+  div.style.animation = 'anim 100s linear paused';
+  div.style.animationTimeline = 'scroll(root)';
   await waitForCSSScrollTimelineStyle();
 
   const anim = div.getAnimations()[0];
@@ -57,7 +60,8 @@
   const scroller = document.scrollingElement;
   await waitForNextFrame();
 
-  div.style.animation = 'anim 100s linear forwards scroll(root)';
+  div.style.animation = 'anim 100s linear forwards';
+  div.style.animationTimeline = 'scroll(root)';
   await waitForCSSScrollTimelineStyle();
 
   const anim = div.getAnimations()[0];
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-responsiveness-from-endpoint.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-responsiveness-from-endpoint.html
index ed6e291..71d36990 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-responsiveness-from-endpoint.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-responsiveness-from-endpoint.html
@@ -25,6 +25,8 @@
 <script>
 'use strict';
 
+setup(assert_implements_animation_timeline);
+
 promise_test(async t => {
   const div = addDiv(t, { style: 'width: 50px; height: 100px;' });
   const filling = addDiv(t, { class: 'fill-vh' });
@@ -32,7 +34,8 @@
   scroller.scrollTop = 0;
   await waitForNextFrame();
 
-  div.style.animation = 'anim 100s linear scroll(root)';
+  div.style.animation = 'anim 100s linear';
+  div.style.animationTimeline = 'scroll(root)';
   await waitForCSSScrollTimelineStyle();
 
   const anim = div.getAnimations()[0];
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-root-dirty.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-root-dirty.html
index 873f18c..1c0b73a 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-root-dirty.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-root-dirty.html
@@ -12,7 +12,8 @@
     to { z-index: 100; }
   }
   #element {
-    animation: anim forwards scroll(root);
+    animation: anim forwards;
+    animation-timeline: scroll(root);
   }
   #spacer {
     height: 200vh;
@@ -22,6 +23,8 @@
 <div id=spacer></div>
 
 <script>
+setup(assert_implements_animation_timeline);
+
 promise_test(async () => {
   await waitForCSSScrollTimelineStyle();
   assert_equals(getComputedStyle(element).zIndex, '100');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-sibling-gcs.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-sibling-gcs.html
index a1ba3b7..6062e795 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-sibling-gcs.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-sibling-gcs.html
@@ -23,7 +23,8 @@
   #element {
     height: 10px;
     width: 10px;
-    animation: anim 10s linear timeline;
+    animation: anim 10s linear;
+    animation-timeline: timeline;
     background-color: rgb(0, 0, 0);
   }
 </style>
@@ -36,6 +37,8 @@
   </div>
 </div>
 <script>
+  setup(assert_implements_animation_timeline);
+
   promise_test(async (t) => {
     element.offsetTop;
     scroller.scrollTop = 50;
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-specified-scroller-print.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-specified-scroller-print.html
index 0d262c2..ba97bd8 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-specified-scroller-print.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-specified-scroller-print.html
@@ -28,7 +28,14 @@
     width: 100px;
     height: 100px;
     background-color: green;
-    animation: anim 1s linear timeline;
+    animation: anim 1s linear;
+    animation-timeline: timeline;
+  }
+
+  @supports not (animation-timeline:timeline) {
+    #box {
+      animation-play-state: paused;
+    }
   }
 </style>
 
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/support/testcommon.js b/third_party/blink/web_tests/external/wpt/scroll-animations/css/support/testcommon.js
index 9f71af1..66bc27b 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/support/testcommon.js
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/support/testcommon.js
@@ -12,3 +12,8 @@
   await waitForNextFrame();
   await waitForNextFrame();
 }
+
+function assert_implements_animation_timeline() {
+  assert_implements(CSS.supports('animation-timeline:foo'),
+      'animation-timeline not supported');
+}
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/timeline-range-name-offset-in-keyframes.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/timeline-range-name-offset-in-keyframes.tentative.html
similarity index 94%
rename from third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/timeline-range-name-offset-in-keyframes.tentative.html
rename to third_party/blink/web_tests/external/wpt/scroll-animations/css/timeline-range-name-offset-in-keyframes.tentative.html
index 38eecd9..993046c5 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/timeline-range-name-offset-in-keyframes.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/timeline-range-name-offset-in-keyframes.tentative.html
@@ -6,6 +6,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes fade-in-out-animation {
     enter 0%, exit 100% { opacity: 0 }
@@ -17,7 +18,8 @@
     height: 200px;
     width: 200px;
     view-timeline-name: foo;
-    animation: linear 1s both fade-in-out-animation foo;
+    animation: linear 1s both fade-in-out-animation;
+    animation-timeline: foo;
   }
 
   #container {
@@ -42,6 +44,8 @@
 </body>
 
 <script type="text/javascript">
+  setup(assert_implements_animation_timeline);
+
   function runTests() {
     promise_test(async t => {
       // scrollTop=200 to 400 is the enter range
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-animation.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-animation.html
index 3ba474a..b816bb6 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-animation.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-animation.html
@@ -4,6 +4,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes anim {
     from { z-index: 0; }
@@ -31,6 +32,8 @@
 </style>
 <main id=main></main>
 <script>
+  setup(assert_implements_animation_timeline);
+
   function inflate(t, template) {
     t.add_cleanup(() => main.replaceChildren());
     main.append(template.content.cloneNode(true));
@@ -49,7 +52,8 @@
   <style>
     #target {
       view-timeline: t1;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=vertical-scroller>
@@ -83,7 +87,8 @@
   <style>
     #target {
       view-timeline: t1 horizontal;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=horizontal-scroller>
@@ -132,8 +137,14 @@
       width: 50px;
       height: 50px;
     }
-    #target_v { animation: anim 1s linear tv; }
-    #target_h { animation: anim 1s linear th; }
+    #target_v {
+      animation: anim 1s linear;
+      animation-timeline: tv;
+    }
+    #target_h {
+      animation: anim 1s linear;
+      animation-timeline: th;
+    }
   </style>
   <div id=scroller>
     <!-- Created dynamically -->
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-delay-animation.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-delay-animation.html
index 9124df6b..dfb0e59 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-delay-animation.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-delay-animation.html
@@ -4,6 +4,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes anim {
     from { z-index: 0; background-color: skyblue;}
@@ -31,6 +32,8 @@
   </div>
 </template>
 <script>
+  setup(assert_implements_animation_timeline);
+
   function inflate(t, template) {
     t.add_cleanup(() => main.replaceChildren());
     main.append(template.content.cloneNode(true));
@@ -59,7 +62,8 @@
       // work around a bug.
       await waitForNextFrame();
 
-      target.style.animation = 'anim 10s linear t1';
+      target.style.animation = 'anim 10s linear';
+      target.style.animationTimeline = 't1';
       target.style.animationDelayStart = options.startDelay;
       target.style.animationDelayEnd = options.endDelay;
 
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html
index b9908b1..74da8850 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html
@@ -25,6 +25,8 @@
 </style>
 <main id=main></main>
 <script>
+  setup(assert_implements_animation_timeline);
+
   function inflate(t, template) {
     t.add_cleanup(() => main.replaceChildren());
     main.append(template.content.cloneNode(true));
@@ -46,7 +48,8 @@
       view-timeline-name: t1;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -98,7 +101,8 @@
       view-timeline-name: t1;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -129,7 +133,8 @@
       view-timeline-name: t1;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -156,7 +161,8 @@
       view-timeline-name: t1;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-inset-animation.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-inset-animation.html
index 1f33c6642..a95086b6 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-inset-animation.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-inset-animation.html
@@ -4,6 +4,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes anim {
     from { z-index: 0; }
@@ -23,6 +24,8 @@
 </style>
 <main id=main></main>
 <script>
+  setup(assert_implements_animation_timeline);
+
   function inflate(t, template) {
     t.add_cleanup(() => main.replaceChildren());
     main.append(template.content.cloneNode(true));
@@ -89,7 +92,8 @@
     #target {
       view-timeline: t1;
       view-timeline-inset: 10px;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=vertical>
@@ -112,7 +116,8 @@
     #target {
       view-timeline: t1;
       view-timeline-inset: 10px 20px;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=vertical>
@@ -136,7 +141,8 @@
       font-size: 10px;
       view-timeline: t1;
       view-timeline-inset: 10px 2em;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=vertical>
@@ -160,7 +166,8 @@
       font-size: 10px;
       view-timeline: t1;
       view-timeline-inset: calc(5px + max(1%, 5%)) 20%;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=vertical>
@@ -183,7 +190,8 @@
     #target {
       view-timeline: t1;
       view-timeline-inset: -10px -20px;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=vertical>
@@ -206,7 +214,8 @@
     #target {
       view-timeline: t1 horizontal;
       view-timeline-inset: 10px 20px;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -229,7 +238,8 @@
     #target {
       view-timeline: t1 block;
       view-timeline-inset: 10px 20px;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -252,7 +262,8 @@
     #target {
       view-timeline: t1 inline;
       view-timeline-inset: 10px 20px;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -278,7 +289,8 @@
     #target {
       view-timeline: t1 block;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -305,7 +317,8 @@
     #target {
       view-timeline: t1 block;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -332,7 +345,8 @@
     #target {
       view-timeline: t1 block;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -359,7 +373,8 @@
     #target {
       view-timeline: t1 inline;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -386,7 +401,8 @@
     #target {
       view-timeline: t1 inline;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -413,7 +429,8 @@
     #target {
       view-timeline: t1 inline;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -440,7 +457,8 @@
     #target {
       view-timeline: t1 inline;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -468,7 +486,8 @@
     #target {
       view-timeline: t1 inline;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -496,7 +515,8 @@
     #target {
       view-timeline: t1 inline;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -522,7 +542,8 @@
     #target {
       view-timeline: t1 vertical;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -549,7 +570,8 @@
     #target {
       view-timeline: t1 vertical;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -577,7 +599,8 @@
     #target {
       view-timeline: t1 vertical;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -603,7 +626,8 @@
     #target {
       view-timeline: t1 horizontal;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -630,7 +654,8 @@
     #target {
       view-timeline: t1 horizontal;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -657,7 +682,8 @@
     #target {
       view-timeline: t1 horizontal;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -684,7 +710,8 @@
     #target {
       view-timeline: t1 horizontal;
       view-timeline-inset: auto auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
@@ -711,7 +738,8 @@
     #target {
       view-timeline: t1;
       view-timeline-inset: 10% auto;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-lookup.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-lookup.html
index 2dd8cf9..c1797c7b 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-lookup.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-lookup.html
@@ -4,6 +4,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes anim {
     from { z-index: 0; }
@@ -33,7 +34,8 @@
     #target {
       height: 0px;
       view-timeline: t1;
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -60,7 +62,8 @@
       view-timeline: t1;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -88,7 +91,8 @@
       view-timeline: t1;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -119,7 +123,8 @@
       view-timeline: t1;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -150,7 +155,8 @@
       view-timeline: t1;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -186,7 +192,8 @@
       view-timeline: t2;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -225,7 +232,8 @@
       height: 50px;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
@@ -265,7 +273,8 @@
       height: 50px;
     }
     #target {
-      animation: anim 1s linear t1;
+      animation: anim 1s linear;
+      animation-timeline: t1;
     }
   </style>
   <div id=scroller class=scroller>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-used-values.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-used-values.html
index cf32777..6627eeb 100644
--- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-used-values.html
+++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-used-values.html
@@ -5,6 +5,7 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/web-animations/testcommon.js"></script>
+<script src="support/testcommon.js"></script>
 <style>
   @keyframes anim {
     from { z-index: 0; }
@@ -23,6 +24,8 @@
 </style>
 <main id=main></main>
 <script>
+  setup(assert_implements_animation_timeline);
+
   function inflate(t, template) {
     t.add_cleanup(() => main.replaceChildren());
     main.append(template.content.cloneNode(true));
@@ -42,7 +45,8 @@
     #target {
       view-timeline-name: t1, t2; /* Two items */
       view-timeline-axis: inline; /* One item */
-      animation: anim 1s linear t2;
+      animation: anim 1s linear;
+      animation-timeline: t2;
     }
   </style>
   <div id=scroller class=scroller>
@@ -74,7 +78,8 @@
     #target {
       view-timeline-name: t1, t2; /* Two items */
       view-timeline-inset: 100px; /* One item */
-      animation: anim 1s linear t2;
+      animation: anim 1s linear;
+      animation-timeline: t2;
     }
   </style>
   <div id=scroller class=scroller>
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/supported-entry-types.tentative.html b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/supported-entry-types.tentative.html
new file mode 100644
index 0000000..4ab408e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/supported-entry-types.tentative.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Soft navigations are a supported entry type</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  promise_test(async () => {
+    assert_true(PerformanceObserver.supportedEntryTypes.includes(
+      "soft-navigation"));
+  }, "Soft navigations are a supported entry type");
+</script>
+
+
diff --git a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/class-string-iterator-prototype-object.any-expected.txt b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/class-string-iterator-prototype-object.any-expected.txt
deleted file mode 100644
index 5c7e1f9..0000000
--- a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/class-string-iterator-prototype-object.any-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-FAIL @@toStringTag exists with the appropriate descriptor assert_equals: value expected "URLSearchParams Iterator" but got "Iterator"
-FAIL Object.prototype.toString assert_equals: expected "[object URLSearchParams Iterator]" but got "[object Iterator]"
-PASS Object.prototype.toString applied after modifying @@toStringTag
-PASS Object.prototype.toString applied to a null-prototype instance
-PASS Object.prototype.toString applied after nulling the prototype
-PASS Object.prototype.toString applied after deleting @@toStringTag
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/class-string-iterator-prototype-object.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/class-string-iterator-prototype-object.any.worker-expected.txt
deleted file mode 100644
index 5c7e1f9..0000000
--- a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/class-string-iterator-prototype-object.any.worker-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-FAIL @@toStringTag exists with the appropriate descriptor assert_equals: value expected "URLSearchParams Iterator" but got "Iterator"
-FAIL Object.prototype.toString assert_equals: expected "[object URLSearchParams Iterator]" but got "[object Iterator]"
-PASS Object.prototype.toString applied after modifying @@toStringTag
-PASS Object.prototype.toString applied to a null-prototype instance
-PASS Object.prototype.toString applied after nulling the prototype
-PASS Object.prototype.toString applied after deleting @@toStringTag
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/default-iterator-object-expected.txt b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/default-iterator-object-expected.txt
deleted file mode 100644
index 40d379a7..0000000
--- a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/default-iterator-object-expected.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-This is a testharness.js-based test.
-PASS Default iterator objects for an interface have the same prototype
-FAIL Object.prototype.toString returns correct value assert_equals: expected "[object URLSearchParams Iterator]" but got "[object Iterator]"
-FAIL @@toStringTag has correct value from prototype assert_equals: expected "URLSearchParams Iterator" but got "Iterator"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/iterator-prototype-object-expected.txt b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/iterator-prototype-object-expected.txt
deleted file mode 100644
index e825987..0000000
--- a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/iterator-prototype-object-expected.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-This is a testharness.js-based test.
-PASS Has %IteratorPrototype% as prototype
-PASS next() exists and is writable, enumerable, and configurable
-FAIL next() throws TypeError when called on ineligible receiver assert_throws_js: function "() => {
-    iteratorProto.next.call(new Headers().entries());
-  }" did not throw
-FAIL Is specific to an interface assert_not_equals: got disallowed value object "[object Iterator]"
-Harness: the test ran to completion.
-
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium
index 6110baa..994b558 100644
--- a/third_party/freetype/README.chromium
+++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@
 Name: FreeType
 URL: http://www.freetype.org/
-Version: VER-2-12-1-141-gb405fc5c1
-Revision: b405fc5c1dbbe8163bf199b921ccfc5f902eaa4f
+Version: VER-2-12-1-142-g26e9028f1
+Revision: 26e9028f10657acefc801bb7d5276419963e60cb
 CPEPrefix: cpe:/a:freetype:freetype:2.12.1
 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent
          JPEG Group) licenses"
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index d6c986f..d9eb0b1 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -20739,6 +20739,12 @@
   <description>User exited the iOS tab grid (by any means).</description>
 </action>
 
+<action name="MobileTabGridMoveToExistingTab">
+  <owner>ewannpv@chromium.org</owner>
+  <owner>gambard@chromium.org</owner>
+  <description>User moves from a Tab to an existing other Tab.</description>
+</action>
+
 <action name="MobileTabGridOpenIncognitoTab">
   <owner>edchin@chromium.org</owner>
   <owner>marq@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index db9c707..32d86d6b 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -21574,7 +21574,7 @@
 </enum>
 
 <enum name="CropToResult">
-  <int value="0" label="Resolved"/>
+  <int value="0" label="Ok"/>
   <int value="1" label="UnsupportedPlatform"/>
   <int value="2" label="InvalidCropTargetFormat"/>
   <int value="3" label="RejectedWithErrorGeneric"/>
@@ -21583,6 +21583,7 @@
   <int value="6" label="RejectedWithNotImplemented"/>
   <int value="7" label="NonIncreasingCropVersion"/>
   <int value="8" label="InvalidCropTarget"/>
+  <int value="9" label="TimedOut"/>
 </enum>
 
 <enum name="CrOSActionRecorderEvent">
@@ -30845,6 +30846,15 @@
   <int value="9" label="FD close error"/>
 </enum>
 
+<enum name="EnterpriseDlpPolicyCrOSDaemonInitError">
+  <summary>
+    Type of errors triggered when performing initialization operations in the
+    ChromeOS Data Leak Prevention daemon.
+  </summary>
+  <int value="0" label="Unknown error"/>
+  <int value="1" label="Primary username retrieval error"/>
+</enum>
+
 <enum name="EnterpriseDlpPolicyCrOSFanotifyError">
   <summary>
     Type of errors triggered by fanotify usage in the ChromeOS Data Leak
@@ -30860,6 +30870,7 @@
   <int value="7" label="Invalid file descriptor error"/>
   <int value="8" label="Unexpected file handle type error"/>
   <int value="9" label="Unexpected event info type error"/>
+  <int value="10" label="fanotify_init error"/>
 </enum>
 
 <enum name="EnterpriseDlpPolicyCrOSFileDatabaseError">
@@ -30874,6 +30885,8 @@
   <int value="4" label="Select error"/>
   <int value="5" label="Query error"/>
   <int value="6" label="Multiple entries for same inode error"/>
+  <int value="7" label="Create directory error"/>
+  <int value="8" label="Set ownership error"/>
 </enum>
 
 <enum name="EnterpriseDlpPolicyFileAction">
@@ -35491,6 +35504,7 @@
   <int value="1739" label="DEVELOPERPRIVATE_UPDATESITEACCESS"/>
   <int value="1740" label="AUTOTESTPRIVATE_REFRESHREMOTECOMMANDS"/>
   <int value="1741" label="FILESYSTEMPROVIDERINTERNAL_RESPONDTOMOUNTREQUEST"/>
+  <int value="1742" label="OS_DIAGNOSTICS_RUNEMMCLIFETIMEROUTINE"/>
 </enum>
 
 <enum name="ExtensionIconState">
@@ -82652,6 +82666,7 @@
   <int value="49" label="kSameSiteCrossOriginNavigationNotOptIn"/>
   <int value="50" label="kActivationNavigationParameterMismatch"/>
   <int value="51" label="kActivatedInBackground"/>
+  <int value="52" label="kEmbedderHostDisallowed"/>
 </enum>
 
 <enum name="PrerenderHoverEvent">
diff --git a/tools/metrics/histograms/metadata/android/histograms.xml b/tools/metrics/histograms/metadata/android/histograms.xml
index c57c263..ff38e39 100644
--- a/tools/metrics/histograms/metadata/android/histograms.xml
+++ b/tools/metrics/histograms/metadata/android/histograms.xml
@@ -1523,7 +1523,7 @@
 </histogram>
 
 <histogram name="Android.FontLookup.MatchLocalFontByUniqueName.Time" units="ms"
-    expires_after="2023-03-19">
+    expires_after="2023-06-11">
   <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -2386,7 +2386,7 @@
 </histogram>
 
 <histogram name="Android.Omnibox.SetGeolocationHeadersTime" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>spelchat@chromium.org</owner>
   <owner>chrome-brapp-loading@google.com</owner>
   <summary>
@@ -2427,7 +2427,7 @@
 </histogram>
 
 <histogram name="Android.Omnibox.SuggestionList.LayoutTime2"
-    units="microseconds" expires_after="2023-04-09">
+    units="microseconds" expires_after="2023-06-11">
   <owner>ender@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
@@ -2485,7 +2485,7 @@
 </histogram>
 
 <histogram name="Android.Omnibox.SuggestionList.MeasureTime2"
-    units="microseconds" expires_after="2023-04-09">
+    units="microseconds" expires_after="2023-06-11">
   <owner>ender@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
@@ -2582,7 +2582,7 @@
 </histogram>
 
 <histogram name="Android.Omnibox.SuggestionView.CreateTime2"
-    units="microseconds" expires_after="2023-04-09">
+    units="microseconds" expires_after="2023-06-11">
   <owner>ender@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
@@ -3391,7 +3391,7 @@
 </histogram>
 
 <histogram name="Android.Survey.SurveyFilteringResults"
-    enum="SurveyFilteringResult" expires_after="2023-04-09">
+    enum="SurveyFilteringResult" expires_after="2023-06-11">
   <owner>twellington@chromium.org</owner>
   <owner>wenyufu@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index 9c7f5e0..946a620 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -850,7 +850,7 @@
 </histogram>
 
 <histogram name="Apps.AppList.Search.Error" enum="AppListUserEventError"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>wrong@chromium.org</owner>
   <owner>thanhdng@chromium.org</owner>
   <owner>tby@chromium.org</owner>
@@ -1355,7 +1355,7 @@
 </histogram>
 
 <histogram name="Apps.AppListBubbleShowSource" enum="AppListShowSource"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>tbarzic@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <owner>chromeos-launcher@google.com</owner>
@@ -1706,7 +1706,7 @@
 </histogram>
 
 <histogram name="Apps.AppListShowSource" enum="AppListShowSource"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>tbarzic@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/arc/histograms.xml b/tools/metrics/histograms/metadata/arc/histograms.xml
index 1925365..6907b1e 100644
--- a/tools/metrics/histograms/metadata/arc/histograms.xml
+++ b/tools/metrics/histograms/metadata/arc/histograms.xml
@@ -1286,7 +1286,7 @@
 </histogram>
 
 <histogram name="Arc.InputOverlay.Customized" enum="BooleanEnabled"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>cuicuiruan@google.com</owner>
   <owner>arc-gaming@google.com</owner>
   <summary>
@@ -1489,7 +1489,7 @@
 </histogram>
 
 <histogram name="Arc.Net.ArcNetworkError" enum="ArcNetworkError"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hugobenichi@google.com</owner>
   <owner>cros-networking@google.com</owner>
   <summary>
@@ -1594,7 +1594,7 @@
 </histogram>
 
 <histogram name="Arc.OptInNetworkErrorAction" enum="ArcOptInNetworkErrorAction"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mhasank@google.com</owner>
   <owner>arc-core@google.com</owner>
   <summary>
@@ -1797,7 +1797,7 @@
 </histogram>
 
 <histogram name="Arc.PrintPreview.RendererError" enum="PrintPreviewFailureType"
-    expires_after="2023-04-04">
+    expires_after="2023-06-11">
   <owner>bmgordon@chromium.org</owner>
   <owner>project-bolton@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/ash/histograms.xml b/tools/metrics/histograms/metadata/ash/histograms.xml
index edb97eeb..ff2e753 100644
--- a/tools/metrics/histograms/metadata/ash/histograms.xml
+++ b/tools/metrics/histograms/metadata/ash/histograms.xml
@@ -1295,7 +1295,7 @@
 </histogram>
 
 <histogram name="Ash.CaptureModeController.ScreenshotsPerWeek" units="int"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>afakhry@chromium.org</owner>
   <owner>gzadina@google.com</owner>
   <summary>
@@ -1913,7 +1913,7 @@
 </histogram>
 
 <histogram name="Ash.DeskTemplate.AddOrUpdateTemplateStatus"
-    enum="DeskModelAddOrUpdateEntryStatus" expires_after="2023-04-09">
+    enum="DeskModelAddOrUpdateEntryStatus" expires_after="2023-06-11">
   <owner>yzd@google.com</owner>
   <owner>janetmac@chromium.org</owner>
   <summary>
@@ -2236,7 +2236,7 @@
 </histogram>
 
 <histogram name="Ash.DeviceActivity.MethodCalled" enum="DeviceActivityMethod"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hirthanan@google.com</owner>
   <owner>chromeos-data-team@google.com</owner>
   <summary>
@@ -3419,7 +3419,7 @@
 </histogram>
 
 <histogram base="true" name="Ash.Overview.AnimationSmoothness.Close" units="%"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
 <!-- Name completed by histogram_suffixes
      name="TabletOrClamshellMode" -->
 
@@ -4149,7 +4149,7 @@
 </histogram>
 
 <histogram name="Ash.Shelf.Menu.NumItemsEnabledUponSelection" units="Count"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>anasalazar@chromium.org</owner>
   <owner>mmourgos@google.com</owner>
   <summary>
@@ -5584,7 +5584,7 @@
 </histogram>
 
 <histogram name="Ash.WindowManager.Lock.Success" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>antrim@chromium.org</owner>
   <owner>cros-lurs@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/assistant/histograms.xml b/tools/metrics/histograms/metadata/assistant/histograms.xml
index 696ffd03..88cc8d6 100644
--- a/tools/metrics/histograms/metadata/assistant/histograms.xml
+++ b/tools/metrics/histograms/metadata/assistant/histograms.xml
@@ -147,7 +147,7 @@
 </histogram>
 
 <histogram name="Assistant.OptInFlow.LoadingTimeoutCount" units="timeouts"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>updowndota@chromium.org</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>
@@ -158,7 +158,7 @@
 </histogram>
 
 <histogram name="Assistant.OptInFlowStatus" enum="AssistantOptInFlowStatus"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>updowndota@chromium.org</owner>
   <owner>croissant-eng@chromium.org</owner>
   <summary>Record the status of the Assistant opt-in flow.</summary>
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml
index 5125d56..46201ff4 100644
--- a/tools/metrics/histograms/metadata/autofill/histograms.xml
+++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -316,7 +316,7 @@
 </histogram>
 
 <histogram name="Autofill.Address.IsEnabled.PageLoad" enum="BooleanEnabled"
-    expires_after="2023-02-12">
+    expires_after="2023-06-11">
   <owner>jsaul@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
@@ -936,7 +936,7 @@
 </histogram>
 
 <histogram name="Autofill.CardUploadEnabled" enum="AutofillCardUploadEnabled"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jsaul@google.com</owner>
   <owner>aneeshali@google.com</owner>
   <summary>
@@ -3197,7 +3197,7 @@
 </histogram>
 
 <histogram name="Autofill.ProfileImport.UpdateProfileEditedType"
-    enum="AutofillSettingsVisibleTypes" expires_after="2023-04-09">
+    enum="AutofillSettingsVisibleTypes" expires_after="2023-06-11">
   <owner>koerber@google.com</owner>
   <owner>src/components/autofill/OWNERS</owner>
   <summary>
@@ -3764,7 +3764,7 @@
 </histogram>
 
 <histogram name="Autofill.StoredProfileWithoutCountryCount" units="units"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>fleimgruber@google.com</owner>
   <owner>chrome-autofill-alerts@google.com</owner>
   <summary>
@@ -3904,7 +3904,7 @@
 </histogram>
 
 <histogram name="Autofill.SubmittedCardState" enum="AutofillSubmittedCardState"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jsaul@google.com</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -3915,7 +3915,7 @@
 
 <histogram name="Autofill.SubmittedServerCardExpirationStatus"
     enum="AutofillSubmittedServerCardExpirationStatus"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jsaul@google.com</owner>
   <owner>payments-autofill-team@google.com</owner>
   <summary>
@@ -4097,7 +4097,7 @@
 </histogram>
 
 <histogram name="Autofill.UnmaskPrompt.GetRealPanResult"
-    enum="AutofillGetRealPanResult" expires_after="2023-06-04">
+    enum="AutofillGetRealPanResult" expires_after="2023-06-11">
   <owner>jsaul@google.com</owner>
   <owner>siyua@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index 04eb7e9..dba5c02 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -888,7 +888,7 @@
 </histogram>
 
 <histogram name="Blink.EventPath.CalculateTime" units="microseconds"
-    expires_after="2023-04-01">
+    expires_after="2023-06-11">
   <owner>dizhangg@chromium.org</owner>
   <owner>dom-dev@chromium.org</owner>
   <summary>
@@ -991,7 +991,7 @@
 </histogram>
 
 <histogram name="Blink.FedCm.IsSignInUser" enum="Boolean"
-    expires_after="2023-03-12">
+    expires_after="2023-06-11">
   <owner>yigu@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
   <summary>
@@ -1060,7 +1060,7 @@
 </histogram>
 
 <histogram name="Blink.FedCm.Status.SignInStateMatch"
-    enum="FedCmSignInStateMatchStatus" expires_after="2023-03-12">
+    enum="FedCmSignInStateMatchStatus" expires_after="2023-06-11">
   <owner>tanzachary@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
   <summary>
@@ -1084,7 +1084,7 @@
 </histogram>
 
 <histogram name="Blink.FedCm.Timing.ContinueOnDialog" units="ms"
-    expires_after="2023-03-12">
+    expires_after="2023-06-11">
   <owner>yigu@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
   <summary>
@@ -1116,7 +1116,7 @@
 </histogram>
 
 <histogram name="Blink.FedCm.Timing.TurnaroundTime" units="ms"
-    expires_after="2023-03-12">
+    expires_after="2023-06-11">
   <owner>yigu@chromium.org</owner>
   <owner>web-identity-eng@google.com</owner>
   <summary>
@@ -2117,7 +2117,7 @@
 </histogram>
 
 <histogram name="Blink.MemoryCache.RevalidationPolicy.AsyncScript"
-    enum="RevalidationPolicy" expires_after="2023-04-01">
+    enum="RevalidationPolicy" expires_after="2023-06-11">
   <owner>hiroshige@chromium.org</owner>
   <owner>lizeb@chromium.org</owner>
   <owner>gjc@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/bluetooth/histograms.xml b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
index 68ebbca..54aaf62a 100644
--- a/tools/metrics/histograms/metadata/bluetooth/histograms.xml
+++ b/tools/metrics/histograms/metadata/bluetooth/histograms.xml
@@ -1063,7 +1063,7 @@
 </histogram>
 
 <histogram name="Bluetooth.ChromeOS.FastPair.RetroactiveEngagementFunnel.Steps"
-    enum="FastPairRetroactiveEngagementFlowEvent" expires_after="2023-06-04">
+    enum="FastPairRetroactiveEngagementFlowEvent" expires_after="2023-06-11">
   <owner>shanefitz@google.com</owner>
   <owner>julietlevesque@google.com</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/bookmarks/histograms.xml b/tools/metrics/histograms/metadata/bookmarks/histograms.xml
index 4416915..5158115 100644
--- a/tools/metrics/histograms/metadata/bookmarks/histograms.xml
+++ b/tools/metrics/histograms/metadata/bookmarks/histograms.xml
@@ -59,7 +59,7 @@
 </histogram>
 
 <histogram name="BookmarkManager.ResultsRenderedTime" units="ms"
-    expires_after="2023-03-05">
+    expires_after="2023-06-11">
   <owner>johntlee@chromium.org</owner>
   <owner>dbeam@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/borealis/histograms.xml b/tools/metrics/histograms/metadata/borealis/histograms.xml
index 6f4bde9..3fa6f53e 100644
--- a/tools/metrics/histograms/metadata/borealis/histograms.xml
+++ b/tools/metrics/histograms/metadata/borealis/histograms.xml
@@ -256,7 +256,7 @@
 </histogram>
 
 <histogram name="Borealis.Install.Result" enum="BorealisInstallResult"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>danielng@google.com</owner>
   <owner>src/chrome/browser/ash/borealis/OWNERS</owner>
   <summary>
@@ -289,7 +289,7 @@
 </histogram>
 
 <histogram name="Borealis.Stability" enum="GuestOsFailureClasses"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>cpelling@google.com</owner>
   <owner>src/chrome/browser/ash/borealis/OWNERS</owner>
   <summary>
@@ -320,14 +320,14 @@
 </histogram>
 
 <histogram name="Borealis.Startup.NumAttempts" enum="BooleanAttempted"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>danielng@google.com</owner>
   <owner>src/chrome/browser/ash/borealis/OWNERS</owner>
   <summary>Recording every attempt to start Borealis (via the UI).</summary>
 </histogram>
 
 <histogram name="Borealis.Startup.OverallTime" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>danielng@google.com</owner>
   <owner>src/chrome/browser/ash/borealis/OWNERS</owner>
   <summary>
@@ -337,7 +337,7 @@
 </histogram>
 
 <histogram name="Borealis.Startup.Result" enum="BorealisStartupResult"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>danielng@google.com</owner>
   <owner>src/chrome/browser/ash/borealis/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/chromeos/histograms.xml b/tools/metrics/histograms/metadata/chromeos/histograms.xml
index b9d45e7..5b18899 100644
--- a/tools/metrics/histograms/metadata/chromeos/histograms.xml
+++ b/tools/metrics/histograms/metadata/chromeos/histograms.xml
@@ -174,7 +174,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Apps.IntentPickerDestinationPlatform"
-    enum="ArcIntentHandlerDestinationPlatform" expires_after="2023-04-12">
+    enum="ArcIntentHandlerDestinationPlatform" expires_after="2023-06-11">
   <owner>tsergeant@chromium.org</owner>
   <owner>chromeos-apps-foundation-team@google.com</owner>
   <summary>
@@ -642,7 +642,7 @@
 </histogram>
 
 <histogram name="ChromeOS.CWP.ParseLacrosPath" enum="ChromeOSParseLacrosPath"
-    expires_after="2023-04-06">
+    expires_after="2023-06-11">
   <owner>shantuo@google.com</owner>
   <owner>cwp-team@google.com</owner>
   <summary>
@@ -1183,7 +1183,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Intents.IntentPickerAction"
-    enum="IntentPickerDialogAction" expires_after="2023-06-04">
+    enum="IntentPickerDialogAction" expires_after="2023-06-11">
   <owner>tsergeant@chromium.org</owner>
   <owner>chromeos-apps-foundation-team@google.com</owner>
   <summary>
@@ -1254,7 +1254,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Intents.LinkCapturingEvent2"
-    enum="LinkCapturingEvent" expires_after="2023-06-04">
+    enum="LinkCapturingEvent" expires_after="2023-06-11">
   <owner>vpao@google.com</owner>
   <owner>chromeos-apps-foundation-team@google.com</owner>
   <summary>
@@ -1452,7 +1452,7 @@
 </histogram>
 
 <histogram name="ChromeOS.LanguagePacks.Mojo.PackStateResponse"
-    enum="LanguagePackMojoPackState" expires_after="2023-01-30">
+    enum="LanguagePackMojoPackState" expires_after="2023-06-11">
   <owner>mlcui@google.com</owner>
   <owner>cros-borders-eng@google.com</owner>
   <summary>
@@ -1500,7 +1500,7 @@
 </histogram>
 
 <histogram name="ChromeOS.Liveness.PingResult" enum="BooleanSuccess"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>iby@google.com</owner>
   <owner>xiyuan@google.com</owner>
   <summary>
@@ -2214,7 +2214,7 @@
 </histogram>
 
 <histogram name="ChromeOS.TypeC.WrongConfiguration"
-    enum="WrongConfigurationMetric" expires_after="2023-04-09">
+    enum="WrongConfigurationMetric" expires_after="2023-06-11">
   <owner>pmalani@chromium.org</owner>
   <owner>chromeos-power@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/compositing/histograms.xml b/tools/metrics/histograms/metadata/compositing/histograms.xml
index 2594f3f..29540ff 100644
--- a/tools/metrics/histograms/metadata/compositing/histograms.xml
+++ b/tools/metrics/histograms/metadata/compositing/histograms.xml
@@ -257,7 +257,7 @@
 </histogram>
 
 <histogram name="Compositing.Display.DrawToSwapUs" units="microseconds"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>backer@chromium.org</owner>
   <owner>rjkroege@chromium.org</owner>
   <summary>
@@ -715,7 +715,7 @@
 </histogram>
 
 <histogram name="Compositing.SkiaRenderer.DrawTileDrawQuad.UsingRawDraw"
-    units="boolean" expires_after="2023-04-09">
+    units="boolean" expires_after="2023-06-11">
   <owner>backer@chromium.org</owner>
   <owner>penghuang@chromium.org</owner>
   <summary>Records if the TileDrawQuad is rendered with RawDraw.</summary>
@@ -1432,7 +1432,7 @@
 
 <histogram
     name="Graphics.Smoothness.PerSession.MaxPercentDroppedFrames_1sWindow"
-    units="%" expires_after="2023-04-09">
+    units="%" expires_after="2023-06-11">
   <owner>jonross@chromium.org</owner>
   <owner>graphics-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/content/histograms.xml b/tools/metrics/histograms/metadata/content/histograms.xml
index da0dc32..16f622b 100644
--- a/tools/metrics/histograms/metadata/content/histograms.xml
+++ b/tools/metrics/histograms/metadata/content/histograms.xml
@@ -1626,7 +1626,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.UploadVisibilityLog" enum="Boolean"
-    expires_after="2023-06-04">
+    expires_after="2023-06-11">
   <owner>freedjm@google.com</owner>
   <owner>feed@chromium.org</owner>
   <summary>
@@ -1823,7 +1823,7 @@
 </histogram>
 
 <histogram name="ContentSuggestions.Feed.WebFeed.FollowCount.Engaged"
-    units="follows" expires_after="2023-06-04">
+    units="follows" expires_after="2023-06-11">
   <owner>harringtond@chromium.org</owner>
   <owner>feed@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cookie/histograms.xml b/tools/metrics/histograms/metadata/cookie/histograms.xml
index 44593d0..b7650284 100644
--- a/tools/metrics/histograms/metadata/cookie/histograms.xml
+++ b/tools/metrics/histograms/metadata/cookie/histograms.xml
@@ -530,7 +530,7 @@
 </histogram>
 
 <histogram name="Cookie.IsPartitionedValid" enum="BooleanValid"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>dylancutler@google.com</owner>
   <owner>bingler@chromium.org</owner>
   <summary>
@@ -551,7 +551,7 @@
 </histogram>
 
 <histogram name="Cookie.MaxSameSiteNoneCookiesPerKey" units="units"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>dylancutler@google.com</owner>
   <owner>src/net/cookies/OWNERS</owner>
   <summary>
@@ -769,7 +769,7 @@
 </histogram>
 
 <histogram name="Cookie.SameSiteNoneSizeBytes" units="bytes"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>dylancutler@google.com</owner>
   <owner>src/net/cookies/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cras/histograms.xml b/tools/metrics/histograms/metadata/cras/histograms.xml
index 0634dbf..5f82620 100644
--- a/tools/metrics/histograms/metadata/cras/histograms.xml
+++ b/tools/metrics/histograms/metadata/cras/histograms.xml
@@ -649,7 +649,7 @@
   </summary>
 </histogram>
 
-<histogram name="Cras.StreamFlags" units="value" expires_after="2023-04-09">
+<histogram name="Cras.StreamFlags" units="value" expires_after="2023-06-11">
 <!-- Name completed by histogram_suffixes
      name="Cras.Direction" and
      name="Cras.ClientType" -->
diff --git a/tools/metrics/histograms/metadata/cros/histograms.xml b/tools/metrics/histograms/metadata/cros/histograms.xml
index 78583a6..587dae5 100644
--- a/tools/metrics/histograms/metadata/cros/histograms.xml
+++ b/tools/metrics/histograms/metadata/cros/histograms.xml
@@ -174,7 +174,7 @@
 </histogram>
 
 <histogram name="CrosDisksClient.FormatCompletedError"
-    enum="CrosDisksClientFormatError" expires_after="2023-04-09">
+    enum="CrosDisksClientFormatError" expires_after="2023-06-11">
   <owner>austinct@chromium.org</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cros_ml/histograms.xml b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
index 1db42721..a8d0710 100644
--- a/tools/metrics/histograms/metadata/cros_ml/histograms.xml
+++ b/tools/metrics/histograms/metadata/cros_ml/histograms.xml
@@ -263,7 +263,7 @@
 </histogram>
 
 <histogram name="MachineLearningService.TextSuggester.Suggest.Event"
-    enum="BooleanError" expires_after="2023-04-09">
+    enum="BooleanError" expires_after="2023-06-11">
   <owner>curtismcmullan@chromium.org</owner>
   <owner>amoylan@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cross_device/histograms.xml b/tools/metrics/histograms/metadata/cross_device/histograms.xml
index 3efb7b4..5c49831 100644
--- a/tools/metrics/histograms/metadata/cross_device/histograms.xml
+++ b/tools/metrics/histograms/metadata/cross_device/histograms.xml
@@ -798,7 +798,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.Result.DidDeviceRegistryChange"
-    enum="BooleanChanged" expires_after="2023-04-09">
+    enum="BooleanChanged" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -819,7 +819,7 @@
 </histogram>
 
 <histogram name="CryptAuth.DeviceSyncV2.Result.ResultType"
-    enum="CryptAuthV2DeviceSyncResultType" expires_after="2023-04-09">
+    enum="CryptAuthV2DeviceSyncResultType" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1062,7 +1062,7 @@
 </histogram>
 
 <histogram name="CryptAuth.GetLocalDeviceMetadata.IsReady" enum="BooleanReady"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1075,7 +1075,7 @@
 </histogram>
 
 <histogram name="CryptAuth.GetLocalDeviceMetadata.Result" enum="BooleanSuccess"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1249,7 +1249,7 @@
 </histogram>
 
 <histogram name="InstantTethering.BluetoothDiscoverySessionStarted"
-    enum="BooleanSuccess" expires_after="2023-04-09">
+    enum="BooleanSuccess" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1258,7 +1258,7 @@
 </histogram>
 
 <histogram name="InstantTethering.BluetoothDiscoverySessionStopped"
-    enum="BooleanSuccess" expires_after="2023-04-09">
+    enum="BooleanSuccess" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1359,7 +1359,7 @@
 <histogram
     name="InstantTethering.ConnectionToHostResult.SuccessRate.Background"
     enum="InstantTethering_ConnectionToHostResult_SuccessRate"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1376,7 +1376,7 @@
 </histogram>
 
 <histogram name="InstantTethering.FeatureState"
-    enum="InstantTethering_FeatureState" expires_after="2023-04-09">
+    enum="InstantTethering_FeatureState" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1408,7 +1408,7 @@
 </histogram>
 
 <histogram name="InstantTethering.HostScanResult"
-    enum="InstantTethering_HostScanResult" expires_after="2023-04-09">
+    enum="InstantTethering_HostScanResult" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1426,7 +1426,7 @@
 </histogram>
 
 <histogram name="InstantTethering.HotspotUsageDuration" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1447,7 +1447,7 @@
 </histogram>
 
 <histogram name="InstantTethering.MultiDeviceFeatureState"
-    enum="MultiDevice_FeatureState" expires_after="2023-04-09">
+    enum="MultiDevice_FeatureState" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1459,7 +1459,7 @@
 
 <histogram name="InstantTethering.NotificationInteractionType"
     enum="InstantTethering_NotificationInteractionType"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1479,7 +1479,7 @@
 </histogram>
 
 <histogram name="InstantTethering.Performance.ConnectToHostDuration.Background"
-    units="ms" expires_after="2023-04-09">
+    units="ms" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1533,7 +1533,7 @@
 </histogram>
 
 <histogram name="InstantTethering.SessionCompletionReason"
-    enum="InstantTethering_SessionCompletionReason" expires_after="2023-04-09">
+    enum="InstantTethering_SessionCompletionReason" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1579,7 +1579,7 @@
 
 <histogram
     name="MultiDevice.BetterTogetherSuite.MultiDeviceFeatureState.MojoClient"
-    enum="MultiDevice_FeatureState" expires_after="2023-04-09">
+    enum="MultiDevice_FeatureState" expires_after="2023-06-11">
   <owner>julietlevesque@google.com</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -1719,7 +1719,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.Performance.ReceiveAdvertisementToConnectionDuration.Background"
-    units="ms" expires_after="2023-04-09">
+    units="ms" expires_after="2023-06-11">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1752,7 +1752,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.Performance.StartScanToConnectionDuration.Background"
-    units="ms" expires_after="2023-04-09">
+    units="ms" expires_after="2023-06-11">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1796,7 +1796,7 @@
 
 <histogram
     name="MultiDevice.SecureChannel.BLE.ReceiveAdvertisementToGattConnection.EffectiveSuccessRateWithRetries"
-    enum="BooleanSuccess" expires_after="2023-04-09">
+    enum="BooleanSuccess" expires_after="2023-06-11">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1835,7 +1835,7 @@
 </histogram>
 
 <histogram name="MultiDevice.SecureChannel.Nearby.DisconnectionReason"
-    enum="MultiDeviceNearbyDisconnectionReason" expires_after="2023-04-09">
+    enum="MultiDeviceNearbyDisconnectionReason" expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1866,7 +1866,7 @@
 </histogram>
 
 <histogram name="MultiDevice.SecureChannel.Nearby.FileAction"
-    enum="MultiDeviceNearbyFileAction" expires_after="2023-04-09">
+    enum="MultiDeviceNearbyFileAction" expires_after="2023-06-11">
   <owner>jasonsun@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1877,7 +1877,7 @@
 </histogram>
 
 <histogram name="MultiDevice.SecureChannel.Nearby.FileTransferResult"
-    enum="MultiDeviceNearbyFileTransferResult" expires_after="2023-04-09">
+    enum="MultiDeviceNearbyFileTransferResult" expires_after="2023-06-11">
   <owner>jasonsun@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1932,7 +1932,7 @@
 </histogram>
 
 <histogram name="MultiDevice.SecureChannel.Nearby.WebRtcUpgradeDuration"
-    units="ms" expires_after="2023-04-09">
+    units="ms" expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1946,7 +1946,7 @@
 </histogram>
 
 <histogram name="MultiDevice.Setup.HasDuplicateEligibleHostDeviceNames"
-    enum="BooleanDuplicate" expires_after="2023-04-09">
+    enum="BooleanDuplicate" expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1984,7 +1984,7 @@
 
 <histogram name="MultiDevice.VerifyButtonClicked"
     enum="MultiDevice_VerifyAndForgetHostConfirmationState"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -1993,7 +1993,7 @@
 </histogram>
 
 <histogram name="MultiDeviceSetup.OOBE.UserChoice"
-    enum="MultiDeviceSetupOOBEUserChoice" expires_after="2023-04-09">
+    enum="MultiDeviceSetupOOBEUserChoice" expires_after="2023-06-11">
   <owner>danlee@google.com</owner>
   <owner>better-together-dev@google.com</owner>
   <owner>hsuregan@chromium.org</owner>
@@ -2034,7 +2034,7 @@
 </histogram>
 
 <histogram name="ProximityAuth.BleWeaveConnectionResult"
-    enum="ProximityAuth_BleWeaveConnectionResult" expires_after="2023-04-09">
+    enum="ProximityAuth_BleWeaveConnectionResult" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2045,7 +2045,7 @@
 
 <histogram name="ProximityAuth.BluetoothGattConnectionResult"
     enum="ProximityAuth_BluetoothGattConnectionResult"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2077,7 +2077,7 @@
 
 <histogram name="ProximityAuth.BluetoothGattWriteCharacteristicResult"
     enum="ProximityAuth_BluetoothGattServiceOperationResult"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2111,14 +2111,14 @@
 </histogram>
 
 <histogram name="SmartLock.AuthMethodChoice.Unlock"
-    enum="SmartLockAuthMethodChoice" expires_after="2023-04-09">
+    enum="SmartLockAuthMethodChoice" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>Records the user's unlock method choice.</summary>
 </histogram>
 
 <histogram name="SmartLock.AuthMethodChoice.Unlock.PasswordState"
-    enum="SmartLockAuthEventPasswordState" expires_after="2023-04-09">
+    enum="SmartLockAuthEventPasswordState" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2128,7 +2128,7 @@
 </histogram>
 
 <histogram name="SmartLock.AuthResult" enum="BooleanSuccess"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2187,7 +2187,7 @@
 </histogram>
 
 <histogram name="SmartLock.AuthResult.Unlock.Failure"
-    enum="SmartLockAuthResultFailureReason" expires_after="2023-04-09">
+    enum="SmartLockAuthResultFailureReason" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2197,7 +2197,7 @@
 </histogram>
 
 <histogram name="SmartLock.EnabledDevicesCount" units="devices"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2208,7 +2208,7 @@
 </histogram>
 
 <histogram name="SmartLock.EnabledState" enum="SmartLockEnabledState"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2244,7 +2244,7 @@
 </histogram>
 
 <histogram name="SmartLock.FirstStatusToUser" enum="FirstSmartLockStatus"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>cclem@google.com</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -2293,7 +2293,7 @@
 
 <histogram name="SmartLock.GetRemoteStatus.Unlock.Failure"
     enum="SmartLockGetRemoteStatusResultFailureReason"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2303,7 +2303,7 @@
 </histogram>
 
 <histogram name="SmartLock.MultiDeviceFeatureState"
-    enum="MultiDevice_FeatureState" expires_after="2023-04-09">
+    enum="MultiDevice_FeatureState" expires_after="2023-06-11">
   <owner>hansberry@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
@@ -2389,7 +2389,7 @@
 </histogram>
 
 <histogram name="SmartLock.Toggle" enum="SmartLockToggle"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>cclem@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -2400,7 +2400,7 @@
 </histogram>
 
 <histogram name="WifiSync.MultiDeviceFeatureState"
-    enum="MultiDevice_FeatureState" expires_after="2023-04-09">
+    enum="MultiDevice_FeatureState" expires_after="2023-06-11">
   <owner>jonmann@chromium.org</owner>
   <owner>better-together-dev@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/crostini/histograms.xml b/tools/metrics/histograms/metadata/crostini/histograms.xml
index 48a0ab9..21b5025c 100644
--- a/tools/metrics/histograms/metadata/crostini/histograms.xml
+++ b/tools/metrics/histograms/metadata/crostini/histograms.xml
@@ -79,7 +79,7 @@
 </histogram>
 
 <histogram name="Crostini.AvailableDiskError" units="MiB"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>davidmunro@google.com</owner>
   <owner>clumptini@google.com</owner>
   <summary>
@@ -170,7 +170,7 @@
 </histogram>
 
 <histogram name="Crostini.ContainerOsVersion" enum="CrostiniContainerOsVersion"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>davidmunro@google.com</owner>
   <owner>clumptini@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cryptohome/histograms.xml b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
index 3b65aff..38c713a2 100644
--- a/tools/metrics/histograms/metadata/cryptohome/histograms.xml
+++ b/tools/metrics/histograms/metadata/cryptohome/histograms.xml
@@ -78,7 +78,7 @@
 </histogram>
 
 <histogram name="Cryptohome.DeletedUserProfiles" units="profiles"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -220,7 +220,7 @@
 </histogram>
 
 <histogram name="Cryptohome.DiskCleanupProgress"
-    enum="CryptohomeDiskCleanupProgress" expires_after="2023-04-09">
+    enum="CryptohomeDiskCleanupProgress" expires_after="2023-06-11">
   <owner>vsavu@google.com</owner>
   <owner>dlunev@chromium.org</owner>
   <owner>sarthakkukreti@chromium.org</owner>
@@ -233,7 +233,7 @@
 </histogram>
 
 <histogram name="Cryptohome.DiskCleanupResult"
-    enum="CryptohomeDiskCleanupResult" expires_after="2023-04-09">
+    enum="CryptohomeDiskCleanupResult" expires_after="2023-06-11">
   <owner>vsavu@google.com</owner>
   <owner>dlunev@chromium.org</owner>
   <summary>Records the result of triggering disk cleanup.</summary>
@@ -415,7 +415,7 @@
 </histogram>
 
 <histogram name="Cryptohome.FreeDiskSpaceTotalFreedInMb" units="MiB"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>vsavu@google.com</owner>
   <owner>gwendal@chromium.org</owner>
   <owner>sarthakkukreti@chromium.org</owner>
@@ -443,7 +443,7 @@
 </histogram>
 
 <histogram name="Cryptohome.GCache.FreedDiskSpaceInMb" units="MB"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>dlunev@chromium.org</owner>
   <owner>sarthakkukreti@chromium.org</owner>
   <summary>
@@ -573,7 +573,7 @@
 </histogram>
 
 <histogram name="Cryptohome.LoginDiskCleanupResult"
-    enum="CryptohomeDiskCleanupResult" expires_after="2023-04-09">
+    enum="CryptohomeDiskCleanupResult" expires_after="2023-06-11">
   <owner>vsavu@google.com</owner>
   <owner>sarthakkukreti@chromium.org</owner>
   <summary>
@@ -679,7 +679,7 @@
 </histogram>
 
 <histogram name="Cryptohome.OOPMountOperationResult"
-    enum="CryptohomeOOPMountOperationResult" expires_after="2023-04-01">
+    enum="CryptohomeOOPMountOperationResult" expires_after="2023-06-11">
   <owner>betuls@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
@@ -804,7 +804,7 @@
 </histogram>
 
 <histogram name="Cryptohome.TimeToGenerateEccAuthValue" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>yich@google.com</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
@@ -848,7 +848,7 @@
 </histogram>
 
 <histogram name="Cryptohome.TimeToMountGuestEx" units="ms"
-    expires_after="2023-04-01">
+    expires_after="2023-06-11">
   <owner>betuls@chromium.org</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
@@ -893,7 +893,7 @@
 </histogram>
 
 <histogram name="Cryptohome.TimeToUSSPersist" units="ms"
-    expires_after="2023-04-01">
+    expires_after="2023-06-11">
   <owner>thomascedeno@google.com</owner>
   <owner>cros-hwsec+uma@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index 56d8755..11bef5f 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -110,7 +110,7 @@
 </histogram>
 
 <histogram name="CustomTabs.Branding.NumberOfClients" units="count"
-    expires_after="2023-02-09">
+    expires_after="2023-06-11">
   <owner>wenyufu@chromium.org</owner>
   <owner>chrome-connective-tissue@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/dev/histograms.xml b/tools/metrics/histograms/metadata/dev/histograms.xml
index 2f6c6d40..3aa080d8 100644
--- a/tools/metrics/histograms/metadata/dev/histograms.xml
+++ b/tools/metrics/histograms/metadata/dev/histograms.xml
@@ -260,7 +260,7 @@
 </histogram>
 
 <histogram name="DevTools.LinearMemoryInspector.RevealedFrom"
-    enum="DevToolsLinearMemoryInspectorRevealedFrom" expires_after="2023-03-19">
+    enum="DevToolsLinearMemoryInspectorRevealedFrom" expires_after="2023-06-11">
   <owner>yangguo@chromium.org</owner>
   <owner>kimanh@chromium.org</owner>
   <summary>
@@ -269,7 +269,7 @@
 </histogram>
 
 <histogram name="DevTools.LinearMemoryInspector.Target"
-    enum="DevToolsLinearMemoryInspectorTarget" expires_after="2023-03-19">
+    enum="DevToolsLinearMemoryInspectorTarget" expires_after="2023-06-11">
   <owner>yangguo@chromium.org</owner>
   <owner>kimanh@chromium.org</owner>
   <summary>
@@ -328,7 +328,7 @@
 </histogram>
 
 <histogram name="DevTools.PanelShown" enum="DevToolsPanel"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>yangguo@chromium.org</owner>
   <owner>bmeurer@chromium.org</owner>
   <summary>Specified DevTools panel was shown.</summary>
diff --git a/tools/metrics/histograms/metadata/disk/histograms.xml b/tools/metrics/histograms/metadata/disk/histograms.xml
index b17708b5..8608f1af 100644
--- a/tools/metrics/histograms/metadata/disk/histograms.xml
+++ b/tools/metrics/histograms/metadata/disk/histograms.xml
@@ -439,7 +439,7 @@
 </histogram>
 
 <histogram name="DiskCache.0.TotalIOTimeRead" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jam@chromium.org</owner>
   <owner>swarm-team@google.com</owner>
   <summary>
@@ -450,7 +450,7 @@
 </histogram>
 
 <histogram name="DiskCache.0.TotalIOTimeWrite" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jam@chromium.org</owner>
   <owner>swarm-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/download/histograms.xml b/tools/metrics/histograms/metadata/download/histograms.xml
index 1f8c883..54d1cf78 100644
--- a/tools/metrics/histograms/metadata/download/histograms.xml
+++ b/tools/metrics/histograms/metadata/download/histograms.xml
@@ -62,7 +62,7 @@
 </variants>
 
 <histogram name="Download.ApiFunctions" enum="DownloadFunctions"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>dtrainor@chromium.org</owner>
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index cc58b74..e132979 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -1132,6 +1132,16 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.Dlp.Errors.DaemonInit"
+    enum="EnterpriseDlpPolicyCrOSDaemonInitError" expires_after="2023-06-01">
+  <owner>accorsi@google.com</owner>
+  <owner>chromeos-dlp@google.com</owner>
+  <summary>
+    Recorded when an error occurs while performing initialization operations in
+    the Data Leak Prevention daemon.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.Dlp.Errors.Fanotify"
     enum="EnterpriseDlpPolicyCrOSFanotifyError" expires_after="2023-06-01">
   <owner>accorsi@google.com</owner>
@@ -1176,6 +1186,15 @@
     expires_after="2023-06-01">
   <owner>accorsi@google.com</owner>
   <owner>chromeos-dlp@google.com</owner>
+  <summary>
+    Record whether the kernel supports fanotify mark filesystem.
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.Dlp.FanotifyMarkFilesystemSupport" enum="Boolean"
+    expires_after="2023-06-01">
+  <owner>accorsi@google.com</owner>
+  <owner>chromeos-dlp@google.com</owner>
   <summary>Record whether the kernel supports fanotify delete events.</summary>
 </histogram>
 
@@ -2625,7 +2644,7 @@
 
 <histogram name="Enterprise.UserPolicyChromeOS.ReregistrationResult"
     enum="EnterpriseUserPolicyChromeOSReregistrationResult"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hendrich@chromium.org</owner>
   <owner>rbock@google.com</owner>
   <owner>chromeos-commercial-remote-management@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/extensions/histograms.xml b/tools/metrics/histograms/metadata/extensions/histograms.xml
index ef0af21..18e695ff 100644
--- a/tools/metrics/histograms/metadata/extensions/histograms.xml
+++ b/tools/metrics/histograms/metadata/extensions/histograms.xml
@@ -1952,7 +1952,7 @@
 </histogram>
 
 <histogram name="Extensions.GuestView.ChangeOwnerWebContentsOnAttach"
-    enum="Boolean" expires_after="2023-04-09">
+    enum="Boolean" expires_after="2023-06-11">
   <owner>mcnee@chromium.org</owner>
   <owner>src/components/guest_view/OWNERS</owner>
   <summary>
@@ -2899,7 +2899,7 @@
 </histogram>
 
 <histogram name="Extensions.ServiceWorkerBackground.RegistrationWhenExpected"
-    enum="Boolean" expires_after="2023-04-01">
+    enum="Boolean" expires_after="2023-06-11">
   <owner>lazyboy@chromium.org</owner>
   <owner>dbertoni@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index 513ab75b..fdf2216 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -448,7 +448,7 @@
 </histogram>
 
 <histogram name="InProductHelp.TextBubble.ShownTime" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>haileywang@chromium.org</owner>
   <owner>shaktisahu@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/file/histograms.xml b/tools/metrics/histograms/metadata/file/histograms.xml
index fb3193a1..07268d53 100644
--- a/tools/metrics/histograms/metadata/file/histograms.xml
+++ b/tools/metrics/histograms/metadata/file/histograms.xml
@@ -301,7 +301,7 @@
 </histogram>
 
 <histogram name="FileBrowser.FileSystemProviderMounted"
-    enum="FileSystemProviderMountType" expires_after="2023-04-10">
+    enum="FileSystemProviderMountType" expires_after="2023-06-11">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -351,7 +351,7 @@
 </histogram>
 
 <histogram name="FileBrowser.FormatFileSystemType"
-    enum="FileManagerFormatFileSystemType" expires_after="2023-04-10">
+    enum="FileManagerFormatFileSystemType" expires_after="2023-06-11">
   <owner>austinct@chromium.org</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -876,7 +876,7 @@
 </histogram>
 
 <histogram name="FileBrowser.ViewingRootType" enum="FileManagerRootType"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -886,7 +886,7 @@
 </histogram>
 
 <histogram name="FileBrowser.ViewingRootType.Offline"
-    enum="FileManagerRootType" expires_after="2023-04-10">
+    enum="FileManagerRootType" expires_after="2023-06-11">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -897,7 +897,7 @@
 </histogram>
 
 <histogram name="FileBrowser.ViewingRootType.Online" enum="FileManagerRootType"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -918,7 +918,7 @@
 </histogram>
 
 <histogram name="FileBrowser.ViewingTaskType.Offline"
-    enum="FileManagerTaskType" expires_after="2023-04-10">
+    enum="FileManagerTaskType" expires_after="2023-06-11">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
@@ -929,7 +929,7 @@
 </histogram>
 
 <histogram name="FileBrowser.ViewingTaskType.Online" enum="FileManagerTaskType"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>simmonsjosh@google.com</owner>
   <owner>src/ui/file_manager/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/fingerprint/histograms.xml b/tools/metrics/histograms/metadata/fingerprint/histograms.xml
index 46d7071..b33970b 100644
--- a/tools/metrics/histograms/metadata/fingerprint/histograms.xml
+++ b/tools/metrics/histograms/metadata/fingerprint/histograms.xml
@@ -156,7 +156,7 @@
 </histogram>
 
 <histogram name="Fingerprint.Unlock.Match.PositiveMatchSecretCorrect"
-    enum="BooleanCorrect" expires_after="2023-04-09">
+    enum="BooleanCorrect" expires_after="2023-06-11">
   <owner>hesling@chromium.org</owner>
   <owner>chromeos-fingerprint@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml
index 4901397..df5966a 100644
--- a/tools/metrics/histograms/metadata/gpu/histograms.xml
+++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -822,7 +822,7 @@
 </histogram>
 
 <histogram name="GPU.GPUInitializationTime.V4" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>zmo@chromium.org</owner>
   <owner>spvw@chromium.org</owner>
   <summary>
@@ -892,7 +892,7 @@
 </histogram>
 
 <histogram name="GPU.HardwareAccelerationModeEnabled" enum="BooleanEnabled"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>zmo@chromium.org</owner>
   <owner>graphics-dev@chromium.org</owner>
   <summary>
@@ -1141,7 +1141,7 @@
 </histogram>
 
 <histogram name="GPU.PassthroughDoLinkProgramTime" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jonahr@google.com</owner>
   <owner>angle-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/history/histograms.xml b/tools/metrics/histograms/metadata/history/histograms.xml
index 66c055a1..ed3fac18c 100644
--- a/tools/metrics/histograms/metadata/history/histograms.xml
+++ b/tools/metrics/histograms/metadata/history/histograms.xml
@@ -1795,7 +1795,7 @@
 </histogram>
 
 <histogram name="History.InMemoryURLHistoryItems" units="items"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mpearson@chromium.org</owner>
   <owner>chrome-omnibox-team@google.com</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -1807,7 +1807,7 @@
 </histogram>
 
 <histogram name="History.InMemoryURLIndexingTime" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mpearson@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -1835,7 +1835,7 @@
 </histogram>
 
 <histogram name="History.InMemoryURLIndexRestoreCacheTime" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>tommycli@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -1846,7 +1846,7 @@
 </histogram>
 
 <histogram name="History.InMemoryURLIndexSaveCacheTime" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>tommycli@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
@@ -1886,7 +1886,7 @@
 </histogram>
 
 <histogram name="History.MonthlyURLCount" units="urls"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mpearson@chromium.org</owner>
   <owner>sky@chromium.org</owner>
   <component>UI&gt;Browser&gt;History</component>
diff --git a/tools/metrics/histograms/metadata/input/histograms.xml b/tools/metrics/histograms/metadata/input/histograms.xml
index 29c9849..abec0ab 100644
--- a/tools/metrics/histograms/metadata/input/histograms.xml
+++ b/tools/metrics/histograms/metadata/input/histograms.xml
@@ -360,7 +360,7 @@
 </histogram>
 
 <histogram name="InputMethod.Assistive.Coverage" enum="IMEAssistiveAction"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jiwan@google.com</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
@@ -487,7 +487,7 @@
 </histogram>
 
 <histogram name="InputMethod.Assistive.Match" enum="IMEAssistiveAction"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jiwan@google.com</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
@@ -1539,7 +1539,7 @@
 </histogram>
 
 <histogram name="InputMethod.VirtualKeyboard.SessionDuration" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>keithlee@chromium.org</owner>
   <owner>essential-inputs-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/installer/histograms.xml b/tools/metrics/histograms/metadata/installer/histograms.xml
index 032aaa9..e354eed 100644
--- a/tools/metrics/histograms/metadata/installer/histograms.xml
+++ b/tools/metrics/histograms/metadata/installer/histograms.xml
@@ -147,7 +147,7 @@
 </histogram>
 
 <histogram name="Installer.PowerwashCount" units="powerwashes"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>kimjae@chromium.org</owner>
   <owner>chromeos-core-services@chromium.org</owner>
   <summary>
@@ -175,7 +175,7 @@
 </histogram>
 
 <histogram name="Installer.Recovery.Reason" enum="ChromeOSRecoveryReason"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>kimjae@chromium.org</owner>
   <owner>chromeos-core-services@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/kerberos/histograms.xml b/tools/metrics/histograms/metadata/kerberos/histograms.xml
index 75b91a48..62a32e3 100644
--- a/tools/metrics/histograms/metadata/kerberos/histograms.xml
+++ b/tools/metrics/histograms/metadata/kerberos/histograms.xml
@@ -132,7 +132,7 @@
 </histogram>
 
 <histogram name="Kerberos.Result.ClearAccounts" enum="KerberosErrorType"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>fsandrade@chromium.org</owner>
   <owner>src/chrome/browser/ash/kerberos/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 2ef6c3f..cf02c59 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -1116,7 +1116,7 @@
 </histogram>
 
 <histogram name="Media.AudioCapturerAudioGlitches" enum="AudioGlitchResult"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>olka@chromium.org</owner>
   <owner>gustaf@chromium.org</owner>
   <owner>webrtc-audio-uma@google.com</owner>
@@ -2309,7 +2309,7 @@
 </histogram>
 
 <histogram name="Media.EME.Widevine.HardwareSecure.CdmInfoStatus"
-    enum="CdmInfoStatus" expires_after="2023-04-11">
+    enum="CdmInfoStatus" expires_after="2023-06-11">
   <owner>xhwang@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -3834,6 +3834,19 @@
   </summary>
 </histogram>
 
+<histogram name="Media.RegionCapture.CropTo.Latency" units="ms"
+    expires_after="2023-05-14">
+  <owner>eladalon@chromium.org</owner>
+  <owner>jophba@chromium.org</owner>
+  <owner>mfoltz@chromium.org</owner>
+  <summary>
+    Duration in milliseconds of the time it takes for a
+    BrowserCaptureMediaStreamTrack.cropTo() call to resolve(either successfully
+    or with an error). Note a call which never resolves won't be counted in this
+    metric.
+  </summary>
+</histogram>
+
 <histogram name="Media.RegionCapture.CropTo.Result" enum="CropToResult"
     expires_after="2023-05-14">
   <owner>eladalon@chromium.org</owner>
@@ -3858,7 +3871,7 @@
 </histogram>
 
 <histogram name="Media.RegionCapture.ProduceCropTarget.Promise.Result"
-    enum="ProduceCropTargetPromiseResult" expires_after="2023-04-09">
+    enum="ProduceCropTargetPromiseResult" expires_after="2023-06-11">
   <owner>eladalon@chromium.org</owner>
   <owner>jophba@chromium.org</owner>
   <owner>mfoltz@chromium.org</owner>
@@ -5372,7 +5385,7 @@
 </histogram>
 
 <histogram name="Media.VTVDA.SessionFailureReason"
-    enum="VTVDASessionFailureType" expires_after="2023-04-09">
+    enum="VTVDASessionFailureType" expires_after="2023-06-11">
   <owner>sandersd@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/memory/histograms.xml b/tools/metrics/histograms/metadata/memory/histograms.xml
index 161b21e..941c0c90 100644
--- a/tools/metrics/histograms/metadata/memory/histograms.xml
+++ b/tools/metrics/histograms/metadata/memory/histograms.xml
@@ -579,11 +579,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Browser2" units="MB"
-    expires_after="2023-01-10">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocator2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The browser process's memory usage reported by the memory instrumentation
     service in MB.
@@ -594,13 +595,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Browser2.Custom" units="bytes"
-    expires_after="2023-01-10">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorCustom2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The browser process's memory usage reported by the memory instrumentation
     service in bytes.
@@ -611,11 +613,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Browser2.Small" units="KB"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorSmall2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The browser process's memory usage reported by the memory instrumentation
     service in KB.
@@ -626,13 +629,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Browser2.Tiny" units="bytes"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorTiny2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The browser process's memory usage reported by the memory instrumentation
     service in bytes.
@@ -683,11 +687,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Extension2" units="MB"
-    expires_after="2023-01-10">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocator2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The extension process's memory usage reported by the memory instrumentation
     service in MB.
@@ -698,13 +703,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Extension2.Custom"
-    units="bytes" expires_after="2023-01-10">
+    units="bytes" expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorCustom2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The extension process's memory usage reported by the memory instrumentation
     service in bytes.
@@ -715,11 +721,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Extension2.Small" units="KB"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorSmall2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The extension process's memory usage reported by the memory instrumentation
     service in KB.
@@ -730,13 +737,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Extension2.Tiny" units="bytes"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorTiny2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The extension process's memory usage reported by the memory instrumentation
     service in bytes.
@@ -761,11 +769,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Gpu2" units="MB"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocator2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The gpu process's memory usage reported by the memory instrumentation
     service in MB.
@@ -776,13 +785,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Gpu2.Custom" units="bytes"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorCustom2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The gpu process's memory usage reported by the memory instrumentation
     service in bytes.
@@ -793,11 +803,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Gpu2.Small" units="KB"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorSmall2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The gpu process's memory usage reported by the memory instrumentation
     service in KB.
@@ -808,13 +819,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Gpu2.Tiny" units="bytes"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorTiny2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The gpu process's memory usage reported by the memory instrumentation
     service in bytes.
@@ -825,13 +837,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.NetworkService2" units="MiB"
-    expires_after="2023-01-10">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocator2" -->
 
   <owner>mmenke@chromium.org</owner>
   <owner>morlovich@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The network service process's memory usage reported by the memory
     instrumentation service in MiB.
@@ -842,13 +855,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.NetworkService2.Custom"
-    units="bytes" expires_after="2023-01-10">
+    units="bytes" expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorCustom2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The network service process's memory usage reported by the memory
     instrumentation service in bytes.
@@ -859,13 +873,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.NetworkService2.Small"
-    units="KiB" expires_after="2023-05-14">
+    units="KiB" expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorSmall2" -->
 
   <owner>mmenke@chromium.org</owner>
   <owner>morlovich@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The network service process's memory usage reported by the memory
     instrumentation service in KiB.
@@ -876,13 +891,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.NetworkService2.Tiny"
-    units="bytes" expires_after="2023-05-14">
+    units="bytes" expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorTiny2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The network service process's memory usage reported by the memory
     instrumentation service in bytes.
@@ -1031,11 +1047,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Renderer2" units="MB"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocator2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The renderer process's memory usage reported by the memory instrumentation
     service in MB.
@@ -1046,13 +1063,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Renderer2.Custom"
-    units="bytes" expires_after="2023-01-10">
+    units="bytes" expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorCustom2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The renderer process's memory usage reported by the memory instrumentation
     service in bytes.
@@ -1063,11 +1081,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Renderer2.Small" units="KB"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorSmall2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The renderer process's memory usage reported by the memory instrumentation
     service in KB.
@@ -1078,13 +1097,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Renderer2.Tiny" units="bytes"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorTiny2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The renderer process's memory usage reported by the memory instrumentation
     service in bytes.
@@ -1124,11 +1144,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Utility2" units="MB"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocator2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The utility process's memory usage reported by the memory instrumentation
     service in MB.
@@ -1139,13 +1160,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Utility2.Custom" units="bytes"
-    expires_after="2023-01-10">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorCustom2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The utility process's memory usage reported by the memory instrumentation
     service in bytes.
@@ -1156,11 +1178,12 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Utility2.Small" units="KB"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorSmall2" -->
 
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The utility process's memory usage reported by the memory instrumentation
     service in KB.
@@ -1171,13 +1194,14 @@
 </histogram>
 
 <histogram base="true" name="Memory.Experimental.Utility2.Tiny" units="bytes"
-    expires_after="2023-05-14">
+    expires_after="2024-01-10">
 <!-- Name completed by histogram_suffixes name="ProcessMemoryAllocatorTiny2" -->
 
   <owner>sashamcintosh@chromium.org</owner>
   <owner>chromeos-gfx@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <owner>ssid@chromium.org</owner>
+  <owner>lizeb@chromium.org</owner>
   <summary>
     The utility process's memory usage reported by the memory instrumentation
     service in bytes.
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml
index c0162b3..c1c3e6a6 100644
--- a/tools/metrics/histograms/metadata/navigation/histograms.xml
+++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -506,7 +506,7 @@
   <summary>Whether the back-forward navigation was served from cache.</summary>
 </histogram>
 
-<histogram name="Navigation.BlobUrl" enum="Boolean" expires_after="M111">
+<histogram name="Navigation.BlobUrl" enum="Boolean" expires_after="2023-06-11">
   <owner>jkokatsu@google.com</owner>
   <owner>chrome-security-owp-team@google.com</owner>
   <summary>
@@ -691,7 +691,7 @@
 </histogram>
 
 <histogram name="Navigation.EngagementTime.Ratio" units="%"
-    expires_after="2023-03-19">
+    expires_after="2023-06-11">
   <owner>estark@chromium.org</owner>
   <owner>security-enamel@chromium.org</owner>
   <summary>
@@ -701,7 +701,7 @@
 </histogram>
 
 <histogram name="Navigation.GetFrameHostForNavigation.Duration" units="ms"
-    expires_after="2023-01-28">
+    expires_after="2023-06-11">
   <owner>peilinwang@google.com</owner>
   <owner>woa-performance-bugs+jank@google.com</owner>
   <summary>
@@ -806,7 +806,7 @@
 </histogram>
 
 <histogram name="Navigation.IsNavigationSameSite.Duration" units="ms"
-    expires_after="2023-01-28">
+    expires_after="2023-06-11">
   <owner>peilinwang@google.com</owner>
   <owner>woa-performance-bugs+jank@google.com</owner>
   <summary>
@@ -1753,7 +1753,7 @@
 
 <histogram
     name="Prerender.Experimental.DefaultSearchEngine.SearchTermExtractorCorrectness"
-    enum="Boolean" expires_after="2023-02-01">
+    enum="Boolean" expires_after="2023-06-11">
   <owner>lingqi@chromium.org</owner>
   <owner>chrome-prerendering@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/nearby/histograms.xml b/tools/metrics/histograms/metadata/nearby/histograms.xml
index 0e5e7a4..d0bd824 100644
--- a/tools/metrics/histograms/metadata/nearby/histograms.xml
+++ b/tools/metrics/histograms/metadata/nearby/histograms.xml
@@ -94,7 +94,7 @@
 </histogram>
 
 <histogram name="Nearby.Connections.Bluetooth.LEMedium.StopScanning.Result"
-    enum="BooleanSuccess" expires_after="2023-04-09">
+    enum="BooleanSuccess" expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>Records whether BLE scanning has been stopped successfully.</summary>
@@ -115,7 +115,7 @@
 
 <histogram
     name="Nearby.Connections.InstantMessaging.TachyonIceConfigFetcher.CacheHit"
-    enum="BooleanCacheHit" expires_after="2023-04-09">
+    enum="BooleanCacheHit" expires_after="2023-06-11">
   <owner>cclem@chromium.org</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -219,7 +219,7 @@
 <histogram
     name="Nearby.Connections.UtilityProcessShutdownReason.DisconnectedMojoDependency"
     enum="NearbyConnectionsUtilityProcessMojoDependencyName"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -232,7 +232,7 @@
 </histogram>
 
 <histogram name="Nearby.Connections.WifiLan.ConnectResult"
-    enum="NearbyConnectionsWifiLanConnectResult" expires_after="2023-04-09">
+    enum="NearbyConnectionsWifiLanConnectResult" expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -245,7 +245,7 @@
 </histogram>
 
 <histogram name="Nearby.Connections.WifiLan.ListenResult"
-    enum="NearbyConnectionsWifiLanListenResult" expires_after="2023-04-09">
+    enum="NearbyConnectionsWifiLanListenResult" expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -303,7 +303,7 @@
 <histogram
     name="Nearby.Share.BackgroundScanning.DeviceNearbySharing.Notification.Flow"
     enum="NearbyShareBackgroundScanningDeviceNearbySharingNotificationFlowEvent"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -329,7 +329,7 @@
 
 <histogram name="Nearby.Share.BackgroundScanning.DevicesDetected"
     enum="NearbyShareBackgroundScanningDevicesDetectedEvent"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -354,7 +354,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.BackgroundScanning.SessionStarted"
-    enum="BooleanSuccess" expires_after="2023-04-09">
+    enum="BooleanSuccess" expires_after="2023-06-11">
   <owner>hansenmichael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -408,7 +408,7 @@
 
 <histogram
     name="Nearby.Share.Certificates.Manager.DownloadPublicCertificatesCount"
-    units="certificates" expires_after="2023-04-09">
+    units="certificates" expires_after="2023-06-11">
   <owner>cclem@chromium.org</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -432,7 +432,7 @@
 
 <histogram
     name="Nearby.Share.Certificates.Manager.DownloadPublicCertificatesHttpResult"
-    enum="NearbyShareHttpResult" expires_after="2023-04-09">
+    enum="NearbyShareHttpResult" expires_after="2023-06-11">
   <owner>cclem@chromium.org</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -526,7 +526,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Connection.EstablishOutgoingConnection.Success"
-    enum="BooleanSuccess" expires_after="2023-04-09">
+    enum="BooleanSuccess" expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -540,7 +540,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Connection.EstablishOutgoingConnectionStatus"
-    enum="NearbyShareFinalStatus" expires_after="2023-04-09">
+    enum="NearbyShareFinalStatus" expires_after="2023-06-11">
   <owner>crisrael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -553,7 +553,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Connection.TimeToEstablishOutgoingConnection"
-    units="ms" expires_after="2023-04-09">
+    units="ms" expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -567,7 +567,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Contacts.CanGetProfileUserName"
-    enum="BooleanSuccess" expires_after="2023-04-09">
+    enum="BooleanSuccess" expires_after="2023-06-11">
   <owner>crisrael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -725,7 +725,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Discovery.FurthestDiscoveryProgress"
-    enum="NearbyShareDiscoveryProgress" expires_after="2023-04-09">
+    enum="NearbyShareDiscoveryProgress" expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -781,7 +781,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Enabled" enum="NearbyShareEnabledState"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -794,7 +794,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.EnabledStateChanged" enum="BooleanEnabled"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>crisrael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -806,7 +806,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.IsKnownContact" enum="BooleanKnown"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -828,7 +828,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Medium.ChangedToMedium"
-    enum="NearbyConnectionsMedium" expires_after="2023-04-09">
+    enum="NearbyConnectionsMedium" expires_after="2023-06-11">
   <owner>crisrael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -839,7 +839,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Medium.InitiateBandwidthUpgradeResult"
-    enum="BooleanSuccess" expires_after="2023-04-09">
+    enum="BooleanSuccess" expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -853,7 +853,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Medium.RequestedBandwidthUpgradeResult"
-    enum="BooleanUpgraded" expires_after="2023-04-09">
+    enum="BooleanUpgraded" expires_after="2023-06-11">
   <owner>crisrael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -886,7 +886,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Onboarding.FlowEvent"
-    enum="NearbyShareOnboardingFlowEvent" expires_after="2023-04-09">
+    enum="NearbyShareOnboardingFlowEvent" expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -959,7 +959,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Payload.Medium" enum="NearbyShareUpgradedMedium"
-    expires_after="2023-06-04">
+    expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -1137,7 +1137,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.TimeFromLocalAcceptToTransferStart" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -1174,7 +1174,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.Transfer.Success" enum="BooleanSuccess"
-    expires_after="2023-06-04">
+    expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -1212,7 +1212,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.VisibilityChoice" enum="NearbyShareVisibility"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>pushi@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
@@ -1223,7 +1223,7 @@
 </histogram>
 
 <histogram name="Nearby.Share.WifiNetworkConfiguration.Result"
-    enum="BooleanSuccess" expires_after="2023-04-09">
+    enum="BooleanSuccess" expires_after="2023-06-11">
   <owner>crisrael@google.com</owner>
   <owner>nearby-share-chromeos-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 9485edb4..6c4e6413 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -79,7 +79,7 @@
 </histogram>
 
 <histogram name="HttpCache.BlockfileWriteInUserBuffer" enum="Boolean"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jam@chromium.org</owner>
   <owner>swarm-team@google.com</owner>
   <summary>
@@ -177,7 +177,7 @@
 </histogram>
 
 <histogram name="HttpCache.RestrictedPrefetchReuse"
-    enum="RestrictedPrefetchReused" expires_after="2023-04-09">
+    enum="RestrictedPrefetchReused" expires_after="2023-06-11">
   <owner>nrosenthal@chromium.org</owner>
   <owner>src/net/OWNERS</owner>
   <summary>
@@ -397,7 +397,7 @@
 </histogram>
 
 <histogram name="Net.CertVerifier.MacKeychainCerts.IntermediateCount"
-    units="certificates" expires_after="2023-04-09">
+    units="certificates" expires_after="2023-06-11">
   <owner>mattm@chromium.org</owner>
   <owner>hchao@chromium.org</owner>
   <summary>
@@ -409,7 +409,7 @@
 </histogram>
 
 <histogram name="Net.CertVerifier.MacKeychainCerts.TotalCount"
-    units="certificates" expires_after="2023-04-09">
+    units="certificates" expires_after="2023-06-11">
   <owner>mattm@chromium.org</owner>
   <owner>hchao@chromium.org</owner>
   <summary>
@@ -421,7 +421,7 @@
 </histogram>
 
 <histogram name="Net.CertVerifier.MacKeychainCerts.TrustCount"
-    units="certificates" expires_after="2023-04-09">
+    units="certificates" expires_after="2023-06-11">
   <owner>mattm@chromium.org</owner>
   <owner>hchao@chromium.org</owner>
   <summary>
@@ -433,7 +433,7 @@
 </histogram>
 
 <histogram name="Net.CertVerifier.MacTrustDomainCacheInitTime" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mattm@chromium.org</owner>
   <owner>hchao@chromium.org</owner>
   <summary>
@@ -466,7 +466,7 @@
 </histogram>
 
 <histogram name="Net.CertVerifier.MacTrustImplCacheInitTime" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mattm@chromium.org</owner>
   <owner>hchao@chromium.org</owner>
   <summary>
@@ -491,7 +491,7 @@
 </histogram>
 
 <histogram name="Net.CertVerifier.PathBuilderIterationCount" units="units"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mattm@chromium.org</owner>
   <owner>rsleevi@chromium.org</owner>
   <summary>
@@ -532,7 +532,7 @@
 </histogram>
 
 <histogram name="Net.CertVerifier_TrialComparisonResult"
-    enum="CertVerifierTrialComparisonResult" expires_after="2023-03-19">
+    enum="CertVerifierTrialComparisonResult" expires_after="2023-06-11">
   <owner>mattm@chromium.org</owner>
   <owner>rsleevi@chromium.org</owner>
   <summary>
@@ -1660,7 +1660,7 @@
 </histogram>
 
 <histogram name="Net.ErrorPageCounts.WebAppAlternativeErrorPage"
-    enum="NetErrorCodes" expires_after="2023-04-09">
+    enum="NetErrorCodes" expires_after="2023-06-11">
   <owner>ericwilligers@chromium.org</owner>
   <owner>alexbn@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index b8859a3..73444793 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -1459,7 +1459,7 @@
 </histogram>
 
 <histogram name="Network.Patchpanel.Dbus" enum="NetworkPatchpanelDbusEvent"
-    expires_after="2022-12-31">
+    expires_after="2023-05-07">
   <owner>hugobenichi@google.com</owner>
   <owner>cros-connectivity@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
@@ -1896,7 +1896,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Ethernet.ExpiredLeaseLengthSeconds2"
-    units="seconds" expires_after="2023-04-09">
+    units="seconds" expires_after="2023-06-11">
   <owner>hugobenichi@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -1909,7 +1909,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Ethernet.PortalAttemptsToOnline" units="units"
-    expires_after="2022-12-31">
+    expires_after="2023-05-07">
   <owner>hugobenichi@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -1957,7 +1957,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Ethernet.TimeToConfig" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hugobenichi@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -2189,7 +2189,7 @@
 </histogram>
 
 <histogram name="Network.Shill.PPPMTUValue" units="bytes"
-    expires_after="2022-12-31">
+    expires_after="2023-05-07">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -2258,7 +2258,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.Ikev2.EndReason" enum="NetworkServiceError"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
@@ -2301,7 +2301,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.L2tpIpsecTunnelGroupUsage"
-    enum="VPNL2TPIPsecTunnelGroupUsage" expires_after="2022-12-31">
+    enum="VPNL2TPIPsecTunnelGroupUsage" expires_after="2023-05-07">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
@@ -2324,7 +2324,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.RemoteAuthenticationType"
-    enum="VPNRemoteAuthenticationType" expires_after="2023-04-09">
+    enum="VPNRemoteAuthenticationType" expires_after="2023-06-11">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -2334,7 +2334,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.TimeOnline" units="seconds"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -2348,7 +2348,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.TimeToConfig" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -2368,7 +2368,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.UserAuthenticationType"
-    enum="VPNUserAuthenticationType" expires_after="2023-04-09">
+    enum="VPNUserAuthenticationType" expires_after="2023-06-11">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -2378,7 +2378,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.WireGuardAllowedIPsType"
-    enum="VPNWireGuardAllowedIPsType" expires_after="2023-04-09">
+    enum="VPNWireGuardAllowedIPsType" expires_after="2023-06-11">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -2388,7 +2388,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.WireGuardKeyPairSource"
-    enum="VPNWireGuardKeyPairSource" expires_after="2022-12-31">
+    enum="VPNWireGuardKeyPairSource" expires_after="2023-05-07">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -2398,7 +2398,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.WireGuardPeersNum" units="WireGuard peers"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-network-metrics@google.com</owner>
   <summary>
@@ -2408,7 +2408,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.{VPNType}.EspEncryptionAlgorithm"
-    enum="VPNIPsecEncryptionAlgorithm" expires_after="2022-12-31">
+    enum="VPNIPsecEncryptionAlgorithm" expires_after="2023-05-07">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
@@ -2421,7 +2421,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.{VPNType}.EspIntegrityAlgorithm"
-    enum="VPNIPsecIntegrirtyAlgorithm" expires_after="2022-12-31">
+    enum="VPNIPsecIntegrirtyAlgorithm" expires_after="2023-05-07">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
@@ -2434,7 +2434,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.{VPNType}.IkeDHGroup" enum="VPNIPsecDHGroup"
-    expires_after="2022-12-31">
+    expires_after="2023-05-07">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
@@ -2447,7 +2447,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.{VPNType}.IkeEncryptionAlgorithm"
-    enum="VPNIPsecEncryptionAlgorithm" expires_after="2022-12-31">
+    enum="VPNIPsecEncryptionAlgorithm" expires_after="2023-05-07">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
@@ -2460,7 +2460,7 @@
 </histogram>
 
 <histogram name="Network.Shill.Vpn.{VPNType}.IkeIntegrityAlgorithm"
-    enum="VPNIPsecIntegrirtyAlgorithm" expires_after="2022-12-31">
+    enum="VPNIPsecIntegrirtyAlgorithm" expires_after="2023-05-07">
   <owner>jiejiang@chromium.org</owner>
   <owner>cros-connectivity@google.com</owner>
   <owner>cros-network-metrics@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index a8d779ff..a57b6e40 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -185,7 +185,7 @@
 </histogram>
 
 <histogram name="NewTabPage.Carts.DiscountConsentStatusAtLoad"
-    enum="CartDiscountConsentStatus" expires_after="2023-06-04">
+    enum="CartDiscountConsentStatus" expires_after="2023-06-11">
   <owner>yuezhanggg@chromium.org</owner>
   <owner>wychen@chromium.org</owner>
   <owner>chrome-shopping@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/offline/histograms.xml b/tools/metrics/histograms/metadata/offline/histograms.xml
index 32ca6865..7062c8c 100644
--- a/tools/metrics/histograms/metadata/offline/histograms.xml
+++ b/tools/metrics/histograms/metadata/offline/histograms.xml
@@ -713,7 +713,7 @@
 </histogram>
 
 <histogram name="OfflinePages.OfflineUsage" enum="OfflinePagesOfflineUsage"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>dimich@chromium.org</owner>
   <owner>offline-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/oobe/histograms.xml b/tools/metrics/histograms/metadata/oobe/histograms.xml
index f8a0acb..7f93f74 100644
--- a/tools/metrics/histograms/metadata/oobe/histograms.xml
+++ b/tools/metrics/histograms/metadata/oobe/histograms.xml
@@ -642,7 +642,7 @@
 </histogram>
 
 <histogram name="OOBE.SyncConsentScreen.IsCapabilityKnown" enum="BooleanKnown"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>osamafathy@google.com</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -663,7 +663,7 @@
 </histogram>
 
 <histogram name="OOBE.SyncConsentScreen.LoadingTime" units="ms"
-    expires_after="2023-04-10">
+    expires_after="2023-06-11">
   <owner>osamafathy@google.com</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
@@ -694,7 +694,7 @@
 </histogram>
 
 <histogram name="OOBE.SyncConsentScreen.UserChoice"
-    enum="SyncConsentUserChoice" expires_after="2023-04-10">
+    enum="SyncConsentUserChoice" expires_after="2023-06-11">
   <owner>osamafathy@google.com</owner>
   <owner>cros-oac@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/optimization/histograms.xml b/tools/metrics/histograms/metadata/optimization/histograms.xml
index 3d5576e..8101c5f 100644
--- a/tools/metrics/histograms/metadata/optimization/histograms.xml
+++ b/tools/metrics/histograms/metadata/optimization/histograms.xml
@@ -745,7 +745,7 @@
 <histogram
     name="OptimizationGuide.PageContentAnnotationsService.ContentAnnotationsStorageStatus"
     enum="OptimizationGuidePageContentAnnotationsStorageStatus"
-    expires_after="2023-06-04">
+    expires_after="2023-06-11">
   <owner>sophiechang@chromium.org</owner>
   <owner>mcrouse@chromium.org</owner>
   <summary>
@@ -981,7 +981,7 @@
 </histogram>
 
 <histogram name="OptimizationGuide.PageTopicsOverrideList.UsedOverride"
-    enum="Boolean" expires_after="M112">
+    enum="Boolean" expires_after="2023-06-11">
   <owner>robertogden@chromium.org</owner>
   <owner>chrome-intelligence-core@google.com</owner>
   <summary>
@@ -1189,7 +1189,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelFetcher.GetModelsResponse.NetErrorCode"
-    enum="NetErrorCodes" expires_after="2023-06-04">
+    enum="NetErrorCodes" expires_after="2023-06-11">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
@@ -1219,7 +1219,7 @@
 
 <histogram
     name="OptimizationGuide.PredictionModelFetcher.GetModelsResponse.Status"
-    enum="HttpResponseCode" expires_after="2023-06-04">
+    enum="HttpResponseCode" expires_after="2023-06-11">
   <owner>mcrouse@chromium.org</owner>
   <owner>sophiechang@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 27004060..52f7a97 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -1624,7 +1624,7 @@
 </histogram>
 
 <histogram name="BlueZ.NumberOfExistingAdvertisements" units="advertisements"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mcchou@chromium.org</owner>
   <owner>chromeos-bt-platform-sw-core@google.com</owner>
   <summary>
@@ -1718,7 +1718,7 @@
 </histogram>
 
 <histogram name="BlueZ.TimeLengthOfDiscoverable" units="seconds"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mcchou@chromium.org</owner>
   <owner>chromeos-bt-platform-sw-core@google.com</owner>
   <summary>
@@ -2939,7 +2939,7 @@
 </histogram>
 
 <histogram name="ClientHints.Viewport.IsDeviceScaleFactorOne" enum="Boolean"
-    expires_after="2023-03-29">
+    expires_after="2023-06-11">
   <owner>curranmax@chromium.org</owner>
   <summary>
     This records whether or not the device scale factor was exactly 1.0 or not
@@ -3234,7 +3234,7 @@
 </histogram>
 
 <histogram name="ContextMenu.LensSupportStatus" enum="LensSupportStatus"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>benwgold@google.com</owner>
   <owner>lens-chrome@google.com</owner>
   <summary>
@@ -3369,7 +3369,7 @@
 </histogram>
 
 <histogram name="ContextMenu.TimeToTakeAction.Abandoned" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>twellington@chromium.org</owner>
   <owner>clank-app-team@google.com</owner>
   <summary>
@@ -4893,7 +4893,7 @@
 </histogram>
 
 <histogram name="DisplayManager.UnifiedDesktopDisplayCountRange"
-    enum="MultiDisplayModeDisplayCountRanges" expires_after="2023-04-09">
+    enum="MultiDisplayModeDisplayCountRanges" expires_after="2023-06-11">
   <owner>zentaro@chromium.org</owner>
   <owner>cros-peripherals@google.com</owner>
   <summary>
@@ -5548,7 +5548,7 @@
 </histogram>
 
 <histogram name="Eche.StreamEvent.ConnectionFail" enum="ConnectionFailReason"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>paulzchen@google.com</owner>
   <owner>andychou@google.com</owner>
   <owner>exo-core-eng@google.com</owner>
@@ -7137,7 +7137,7 @@
 
 <histogram name="Incognito.ClearBrowsingDataDialog.ActionType"
     enum="IncognitoClearBrowsingDataDialogActionType"
-    expires_after="2023-03-19">
+    expires_after="2023-06-11">
   <owner>roagarwal@chromium.org</owner>
   <owner>chrome-incognito@google.com</owner>
   <summary>
@@ -7338,7 +7338,7 @@
 </histogram>
 
 <histogram name="InstanceID.GetToken.RequestStatus.FcmInvalidations"
-    enum="GCMRegistrationRequestStatus" expires_after="2023-04-09">
+    enum="GCMRegistrationRequestStatus" expires_after="2023-06-11">
   <owner>peter@chromium.org</owner>
   <owner>rushans@chromium.org</owner>
   <owner>treib@chromium.org</owner>
@@ -11757,7 +11757,7 @@
 </histogram>
 
 <histogram name="SB2.RemoteCall.Result" enum="SB2RemoteCallResult"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -13534,7 +13534,7 @@
 </histogram>
 
 <histogram name="Tablet.WindowDrag.DragEndEventType"
-    enum="WindowDragEndEventType" expires_after="2023-04-09">
+    enum="WindowDragEndEventType" expires_after="2023-06-11">
   <owner>minch@chromium.org</owner>
   <owner>omrilio@chromium.org</owner>
   <summary>
@@ -14986,7 +14986,7 @@
 </histogram>
 
 <histogram name="VoiceInteraction.VoiceResultConfidenceValue" units="%"
-    expires_after="2023-06-04">
+    expires_after="2023-06-11">
   <owner>basiaz@google.com</owner>
   <owner>chrome-language@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/page/histograms.xml b/tools/metrics/histograms/metadata/page/histograms.xml
index da5b69b..5a3eca93 100644
--- a/tools/metrics/histograms/metadata/page/histograms.xml
+++ b/tools/metrics/histograms/metadata/page/histograms.xml
@@ -61,7 +61,7 @@
 </variants>
 
 <histogram name="PageActionController.ActionTypeShown"
-    enum="PageActionIconType" expires_after="2023-04-09">
+    enum="PageActionIconType" expires_after="2023-06-11">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -81,7 +81,7 @@
 </histogram>
 
 <histogram name="PageActionController.Icon.CTR" enum="PageActionCTREvent"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -97,7 +97,7 @@
 </histogram>
 
 <histogram name="PageActionController.Icon.NumberActionsShownWhenClicked"
-    units="actions" expires_after="2023-04-09">
+    units="actions" expires_after="2023-06-11">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -107,7 +107,7 @@
 </histogram>
 
 <histogram name="PageActionController.NumberActionsShown" units="actions"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -118,7 +118,7 @@
 </histogram>
 
 <histogram name="PageActionController.PagesWithActionsShown"
-    enum="PageActionPageEvent" expires_after="2023-04-09">
+    enum="PageActionPageEvent" expires_after="2023-06-11">
   <owner>emshack@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
   <summary>
@@ -2890,7 +2890,7 @@
 </histogram>
 
 <histogram name="PageLoad.ParseTiming.ParseBlockedOnScriptExecution" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>cduvall@chromium.org</owner>
   <owner>swarm-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index 1a61d422..54000a4 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -435,7 +435,7 @@
 </histogram>
 
 <histogram name="PasswordManager.AccountChooserDialogOneAccount"
-    enum="AccountChooserDismissalReason" expires_after="2023-04-09">
+    enum="AccountChooserDismissalReason" expires_after="2023-06-11">
   <owner>vasilii@chromium.org</owner>
   <owner>kazinova@google.com</owner>
   <summary>
@@ -1648,7 +1648,7 @@
 </histogram>
 
 <histogram name="PasswordManager.Import.DesktopInteractions"
-    enum="PasswordsImportDesktopInteractions" expires_after="2023-04-09">
+    enum="PasswordsImportDesktopInteractions" expires_after="2023-06-11">
   <owner>eliaskh@chromium.org</owner>
   <owner>natiahlyi@google.com</owner>
   <summary>
@@ -1658,7 +1658,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ImportDuration" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>eliaskh@chromium.org</owner>
   <owner>natiahlyi@google.com</owner>
   <summary>
@@ -1693,7 +1693,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ImportFileSize" units="bytes"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>eliaskh@chromium.org</owner>
   <owner>natiahlyi@google.com</owner>
   <summary>
@@ -1704,7 +1704,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ImportResultsStatus"
-    enum="PasswordManagerImportResultsStatus" expires_after="2023-04-09">
+    enum="PasswordManagerImportResultsStatus" expires_after="2023-06-11">
   <owner>eliaskh@chromium.org</owner>
   <owner>natiahlyi@google.com</owner>
   <summary>
@@ -2954,7 +2954,7 @@
 </histogram>
 
 <histogram name="PasswordManager.ReusedPasswordType" enum="ReusedPasswordType"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/phonehub/histograms.xml b/tools/metrics/histograms/metadata/phonehub/histograms.xml
index 32413ad..5b939890 100644
--- a/tools/metrics/histograms/metadata/phonehub/histograms.xml
+++ b/tools/metrics/histograms/metadata/phonehub/histograms.xml
@@ -51,7 +51,7 @@
 </histogram>
 
 <histogram name="PhoneHub.BubbleOpened.Connectable.Failed.HostLastSeen"
-    units="ms" expires_after="2023-04-09">
+    units="ms" expires_after="2023-06-11">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -61,7 +61,7 @@
 </histogram>
 
 <histogram name="PhoneHub.BubbleOpened.Connectable.Page" enum="PhoneHubScreen"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -95,7 +95,7 @@
 </histogram>
 
 <histogram name="PhoneHub.CameraRoll.Content.Present" enum="BooleanPresent"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jasonsun@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -149,7 +149,7 @@
 </histogram>
 
 <histogram name="PhoneHub.CameraRoll.Latency.RefreshItems" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jasonsun@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -160,7 +160,7 @@
 </histogram>
 
 <histogram name="PhoneHub.CameraRoll.OptInEntryPoint"
-    enum="PhoneHubCameraRollOptInEntryPoint" expires_after="2023-04-09">
+    enum="PhoneHubCameraRollOptInEntryPoint" expires_after="2023-06-11">
   <owner>jianbing@google.com</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -187,7 +187,7 @@
 </histogram>
 
 <histogram name="PhoneHub.CompletedUserAction" enum="PhoneHubUserAction"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -233,7 +233,7 @@
 
 <histogram name="PhoneHub.Connection.Result.FailureReason"
     enum="SecureChannelConnectionAttemptFailureReason"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -343,7 +343,7 @@
 </histogram>
 
 <histogram name="PhoneHub.NotificationInteraction"
-    enum="PhoneHubNotificationInteraction" expires_after="2023-04-09">
+    enum="PhoneHubNotificationInteraction" expires_after="2023-06-11">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
@@ -374,7 +374,7 @@
 </histogram>
 
 <histogram name="PhoneHub.OptInEntryPoint" enum="PhoneHubOptInEntryPoint"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>Tracks the UI surface with which users enable Phone Hub.</summary>
@@ -405,7 +405,7 @@
 </histogram>
 
 <histogram name="PhoneHub.PermissionsOnboarding.SetUpMode.IntroScreenShown"
-    enum="PhoneHubPermissionsOnboardingSetUpMode" expires_after="2023-04-09">
+    enum="PhoneHubPermissionsOnboardingSetUpMode" expires_after="2023-06-11">
   <owner>mavishsu@google.com</owner>
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
@@ -417,7 +417,7 @@
 
 <histogram
     name="PhoneHub.PermissionsOnboarding.SetUpMode.SetUpFinishedScreenShown"
-    enum="PhoneHubPermissionsOnboardingSetUpResult" expires_after="2023-04-09">
+    enum="PhoneHubPermissionsOnboardingSetUpResult" expires_after="2023-06-11">
   <owner>mavishsu@google.com</owner>
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
@@ -466,7 +466,7 @@
 </histogram>
 
 <histogram name="PhoneHub.QuickActionClicked" enum="PhoneHubQuickAction"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>Event logged after the user clicks on a quick action.</summary>
@@ -540,7 +540,7 @@
 </histogram>
 
 <histogram name="PhoneHub.Usage.SentMessageTypeCount"
-    enum="PhoneHubMessageType" expires_after="2023-04-09">
+    enum="PhoneHubMessageType" expires_after="2023-06-11">
   <owner>jonmann@chromium.org</owner>
   <owner>chromeos-cross-device-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/plugin_vm/histograms.xml b/tools/metrics/histograms/metadata/plugin_vm/histograms.xml
index 6d152b12..ba6c04b 100644
--- a/tools/metrics/histograms/metadata/plugin_vm/histograms.xml
+++ b/tools/metrics/histograms/metadata/plugin_vm/histograms.xml
@@ -30,7 +30,7 @@
 </histogram>
 
 <histogram name="PluginVm.EngagementTime.Background" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>timloh@google.com</owner>
   <owner>joelhockey@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index 074cfaf..c3a69e4 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -921,7 +921,7 @@
 </histogram>
 
 <histogram name="Power.BatteryDischargeRate" units="mW"
-    expires_after="2023-06-04">
+    expires_after="2023-06-11">
   <owner>puthik@chromium.org</owner>
   <owner>chromeos-platform-power@google.com</owner>
   <summary>
@@ -1688,7 +1688,7 @@
 </histogram>
 
 <histogram name="Power.ForegroundThermalState.ChangeEvent.Android"
-    enum="DeviceThermalState" expires_after="2023-04-09">
+    enum="DeviceThermalState" expires_after="2023-06-11">
   <owner>carlscab@google.com</owner>
   <owner>eseckler@chromium.org</owner>
   <owner>khokhlov@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/printing/histograms.xml b/tools/metrics/histograms/metadata/printing/histograms.xml
index b4bef15..79879b8 100644
--- a/tools/metrics/histograms/metadata/printing/histograms.xml
+++ b/tools/metrics/histograms/metadata/printing/histograms.xml
@@ -83,7 +83,7 @@
 </histogram>
 
 <histogram name="Printing.CUPS.AddressResolutionResult" enum="BooleanSuccess"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>bmgordon@chromium.org</owner>
   <owner>cros-printing-dev@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/privacy/histograms.xml b/tools/metrics/histograms/metadata/privacy/histograms.xml
index ee1b12d3..f3a09ec 100644
--- a/tools/metrics/histograms/metadata/privacy/histograms.xml
+++ b/tools/metrics/histograms/metadata/privacy/histograms.xml
@@ -20,7 +20,7 @@
 <histograms>
 
 <histogram name="Privacy.AboutThisSite.PageLoadValidation"
-    enum="AboutThisSiteStatus" expires_after="2023-04-09">
+    enum="AboutThisSiteStatus" expires_after="2023-06-11">
   <owner>dullweber@chromium.org</owner>
   <owner>olesiamarukhno@chromium.org</owner>
   <summary>
@@ -196,7 +196,7 @@
 </histogram>
 
 <histogram name="Privacy.CookiesInUseDialog.Action"
-    enum="CookiesInUseDialogActions" expires_after="2023-04-09">
+    enum="CookiesInUseDialogActions" expires_after="2023-06-11">
   <owner>alimariam@google.com</owner>
   <owner>olesiamarukhno@google.com</owner>
   <owner>sauski@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/profile/histograms.xml b/tools/metrics/histograms/metadata/profile/histograms.xml
index 529ff4d..a7f941a 100644
--- a/tools/metrics/histograms/metadata/profile/histograms.xml
+++ b/tools/metrics/histograms/metadata/profile/histograms.xml
@@ -148,7 +148,7 @@
 </histogram>
 
 <histogram name="Profile.Destroyer.OffTheRecord" enum="ProfileDestructionType"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>rhalavati@chromium.org</owner>
   <owner>chrome-privacy-core@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/quota/histograms.xml b/tools/metrics/histograms/metadata/quota/histograms.xml
index 639483b..d5e0f97 100644
--- a/tools/metrics/histograms/metadata/quota/histograms.xml
+++ b/tools/metrics/histograms/metadata/quota/histograms.xml
@@ -120,7 +120,7 @@
 </histogram>
 
 <histogram name="Quota.EvictedBytesPerRound" units="MB"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>ayui@chromium.org</owner>
   <owner>chrome-owp-storage@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 90466398..da0bf00a 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -1252,7 +1252,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.CannotCheckInvalidUrl" enum="BooleanEnabled"
-    expires_after="2023-02-28">
+    expires_after="2023-06-11">
   <owner>thefrog@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1312,7 +1312,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.GetToken.Time" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1426,7 +1426,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.Network.Result"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2023-04-23">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2023-06-11">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1517,7 +1517,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.RT.Response.VerdictType"
-    enum="SafeBrowsingRTLookupResponseVerdictType" expires_after="2023-04-09">
+    enum="SafeBrowsingRTLookupResponseVerdictType" expires_after="2023-06-11">
   <owner>xinghuilu@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml
index b8ec3d2..b50dd4d 100644
--- a/tools/metrics/histograms/metadata/sb_client/histograms.xml
+++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -183,7 +183,7 @@
 </histogram>
 
 <histogram name="SBClientDownload.DownloadRequestDuration" units="ms"
-    expires_after="2023-06-04">
+    expires_after="2023-06-11">
   <owner>vakh@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <owner>mattm@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/scanning/histograms.xml b/tools/metrics/histograms/metadata/scanning/histograms.xml
index 5da3fdf..24ad8bde 100644
--- a/tools/metrics/histograms/metadata/scanning/histograms.xml
+++ b/tools/metrics/histograms/metadata/scanning/histograms.xml
@@ -53,7 +53,7 @@
 </histogram>
 
 <histogram name="Scanning.MultiPageScan.PageScanResult"
-    enum="ScanJobFailureReason" expires_after="2023-03-19">
+    enum="ScanJobFailureReason" expires_after="2023-06-11">
   <owner>gavinwill@chromium.org</owner>
   <owner>cros-peripherals@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/search/histograms.xml b/tools/metrics/histograms/metadata/search/histograms.xml
index 76059e0..cadc9627 100644
--- a/tools/metrics/histograms/metadata/search/histograms.xml
+++ b/tools/metrics/histograms/metadata/search/histograms.xml
@@ -1626,7 +1626,7 @@
 </histogram>
 
 <histogram name="Search.RelatedSearches.CarouselScrolled" enum="Boolean"
-    expires_after="2023-03-19">
+    expires_after="2023-06-11">
   <owner>donnd@chromium.org</owner>
   <owner>gangwu@chromium.org</owner>
   <owner>related-searches-vteam@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index dc8f334..4bf11471 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -554,7 +554,7 @@
 </histogram>
 
 <histogram name="Security.SCTAuditing.OptOut.ReportCount" units="reports"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>cthomp@chromium.org</owner>
   <owner>jdeblasio@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
index 366b5ac..3191678 100644
--- a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
@@ -120,7 +120,7 @@
 
 <histogram
     name="SegmentationPlatform.AdaptiveToolbar.SegmentSelection.Computed"
-    enum="AdaptiveToolbarButtonVariant" expires_after="2023-06-04">
+    enum="AdaptiveToolbarButtonVariant" expires_after="2023-06-11">
   <owner>shaktisahu@chromium.org</owner>
   <owner>chrome-segmentation-platform@google.com</owner>
   <summary>
@@ -540,7 +540,7 @@
 </histogram>
 
 <histogram name="SegmentationPlatform.SelectionFailedReason"
-    enum="SegmentationSelectionFailureReason" expires_after="2023-06-04">
+    enum="SegmentationSelectionFailureReason" expires_after="2023-06-11">
   <owner>ssid@chromium.org</owner>
   <owner>chrome-segmentation-platform@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/service/histograms.xml b/tools/metrics/histograms/metadata/service/histograms.xml
index be74428..4809913 100644
--- a/tools/metrics/histograms/metadata/service/histograms.xml
+++ b/tools/metrics/histograms/metadata/service/histograms.xml
@@ -454,7 +454,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.InstallEvent.All.FetchCount" units="fetches"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>wanderview@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -806,7 +806,7 @@
 
 <histogram
     name="ServiceWorker.OnBrowserStartup.SkipServiceWorkerOnFirstNavigation"
-    enum="BooleanSkipped" expires_after="2023-04-09">
+    enum="BooleanSkipped" expires_after="2023-06-11">
   <owner>chikamune@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -1193,7 +1193,7 @@
 </histogram>
 
 <histogram name="ServiceWorker.Storage.ReadInitialDataFromDB.Time" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>chikamune@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
@@ -1204,7 +1204,7 @@
 
 <histogram
     name="ServiceWorker.Storage.RegisteredStorageKeyCacheInitialization.Time"
-    units="ms" expires_after="2023-04-09">
+    units="ms" expires_after="2023-06-11">
   <owner>chikamune@chromium.org</owner>
   <owner>chrome-worker@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/sharing/histograms.xml b/tools/metrics/histograms/metadata/sharing/histograms.xml
index ba8ea164..684c7de 100644
--- a/tools/metrics/histograms/metadata/sharing/histograms.xml
+++ b/tools/metrics/histograms/metadata/sharing/histograms.xml
@@ -643,7 +643,7 @@
 </histogram>
 
 <histogram name="Sharing.SmsFetcherScreenOnAndUnlocked" enum="Boolean"
-    expires_after="2023-02-26">
+    expires_after="2023-06-11">
   <owner>yigu@chromium.org</owner>
   <owner>src/content/browser/sms/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/side_search/histograms.xml b/tools/metrics/histograms/metadata/side_search/histograms.xml
index d55867c..f063dac 100644
--- a/tools/metrics/histograms/metadata/side_search/histograms.xml
+++ b/tools/metrics/histograms/metadata/side_search/histograms.xml
@@ -123,7 +123,7 @@
 
 <histogram
     name="SideSearch.NavigationCommittedWithinSideSearchCountPerJourney2"
-    units="navigations" expires_after="2023-04-09">
+    units="navigations" expires_after="2023-06-11">
   <owner>yuhengh@chromium.org</owner>
   <owner>tluk@chromium.org</owner>
   <owner>romanarora@chromium.org</owner>
@@ -149,7 +149,7 @@
 </histogram>
 
 <histogram name="SideSearch.OpenAction" enum="SideSearchOpenActionType"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>yuhengh@chromium.org</owner>
   <owner>tluk@chromium.org</owner>
   <owner>romanarora@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml
index 80fd922..d0ceb24 100644
--- a/tools/metrics/histograms/metadata/signin/histograms.xml
+++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -22,7 +22,7 @@
 
 <histograms>
 
-<histogram name="Signin" enum="SigninHelperFlow" expires_after="2022-04-10">
+<histogram name="Signin" enum="SigninHelperFlow" expires_after="2023-06-11">
   <owner>mlerman@chromium.org</owner>
   <summary>
     Tracks user interactions as they sign in through a flow. The suffix of the
@@ -48,7 +48,7 @@
 </histogram>
 
 <histogram name="Signin.AccountCapabilities.FetchResult"
-    enum="AccountCapabilitiesFetchResult" expires_after="2023-04-10">
+    enum="AccountCapabilitiesFetchResult" expires_after="2023-06-11">
   <owner>alexilin@chromium.org</owner>
   <owner>msarda@chromium.org</owner>
   <owner>chrome-signin-team@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/storage/histograms.xml b/tools/metrics/histograms/metadata/storage/histograms.xml
index 2eee3fe..6fb07e0 100644
--- a/tools/metrics/histograms/metadata/storage/histograms.xml
+++ b/tools/metrics/histograms/metadata/storage/histograms.xml
@@ -838,7 +838,7 @@
 </histogram>
 
 <histogram name="Storage.SharedStorage.OnShutdown.RecoveryOnDiskAttempted"
-    units="BooleanRecoveryOnDiskAttempted" expires_after="2023-04-09">
+    units="BooleanRecoveryOnDiskAttempted" expires_after="2023-06-11">
   <owner>cammie@chromium.org</owner>
   <owner>yaoxia@chromium.org</owner>
   <owner>chrome-ads-histograms@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index a7a459a..05f07db 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -613,7 +613,7 @@
 </histogram>
 
 <histogram name="Sync.Local.Enabled2" enum="BooleanEnabled"
-    expires_after="2023-02-19">
+    expires_after="2023-06-11">
   <owner>igorruvinov@chromium.org</owner>
   <owner>pastarmovj@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1458,7 +1458,7 @@
 </histogram>
 
 <histogram name="Sync.TrustedVaultFileReadStatus"
-    enum="TrustedVaultFileReadStatus" expires_after="2023-04-09">
+    enum="TrustedVaultFileReadStatus" expires_after="2023-06-11">
   <owner>mmoskvitin@google.com</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1466,7 +1466,7 @@
 </histogram>
 
 <histogram name="Sync.TrustedVaultFileWriteSuccess" enum="Boolean"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mmoskvitin@google.com</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1489,7 +1489,7 @@
 </histogram>
 
 <histogram name="Sync.TrustedVaultKeyRetrievalTrigger"
-    enum="TrustedVaultUserActionTrigger" expires_after="2023-04-09">
+    enum="TrustedVaultUserActionTrigger" expires_after="2023-06-11">
   <owner>mmoskvitin@google.com</owner>
   <owner>mastiz@chromium.org</owner>
   <component>Services&gt;Sync</component>
@@ -1623,7 +1623,7 @@
 </histogram>
 
 <histogram name="Sync.URLFetchResponse"
-    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2023-04-09">
+    enum="CombinedHttpResponseAndNetErrorCode" expires_after="2023-06-11">
   <owner>mastiz@chromium.org</owner>
   <owner>treib@chromium.org</owner>
   <component>Services&gt;Sync</component>
diff --git a/tools/metrics/histograms/metadata/tab/histograms.xml b/tools/metrics/histograms/metadata/tab/histograms.xml
index 46b5270..5a01550 100644
--- a/tools/metrics/histograms/metadata/tab/histograms.xml
+++ b/tools/metrics/histograms/metadata/tab/histograms.xml
@@ -1666,7 +1666,7 @@
 </histogram>
 
 <histogram name="Tabs.RecentlyClosed.HistoricalSaverCloseType"
-    enum="HistoricalSaverCloseType" expires_after="2023-04-09">
+    enum="HistoricalSaverCloseType" expires_after="2023-06-11">
   <owner>ckitagawa@chromium.org</owner>
   <owner>fredmello@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/uma/histograms.xml b/tools/metrics/histograms/metadata/uma/histograms.xml
index c190868e..e01c711 100644
--- a/tools/metrics/histograms/metadata/uma/histograms.xml
+++ b/tools/metrics/histograms/metadata/uma/histograms.xml
@@ -271,7 +271,7 @@
 </histogram>
 
 <histogram name="UMA.InitSequence" enum="UmaInitSequence"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>asvitkine@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
@@ -635,7 +635,7 @@
 </histogram>
 
 <histogram name="UMA.PersistentHistograms.InitResult"
-    enum="PersistentHistogramsInitResult" expires_after="2023-04-09">
+    enum="PersistentHistogramsInitResult" expires_after="2023-06-11">
   <owner>asvitkine@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
@@ -645,7 +645,7 @@
 </histogram>
 
 <histogram name="UMA.PrimaryUserType" enum="UserType"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>michaelpg@chromium.org</owner>
   <owner>yilkal@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
@@ -722,7 +722,7 @@
 </histogram>
 
 <histogram name="UMA.ProtoCompressionRatio" units="%"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>asvitkine@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
@@ -904,7 +904,7 @@
 </histogram>
 
 <histogram name="UMA.TruncatedEvents.UserAction" units="events"
-    expires_after="2023-06-04">
+    expires_after="2023-06-11">
   <owner>rkaplow@chromium.org</owner>
   <owner>src/base/metrics/OWNERS</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/update_engine/histograms.xml b/tools/metrics/histograms/metadata/update_engine/histograms.xml
index 28378647..6eca1ee 100644
--- a/tools/metrics/histograms/metadata/update_engine/histograms.xml
+++ b/tools/metrics/histograms/metadata/update_engine/histograms.xml
@@ -299,7 +299,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.RollbackTargetVersion"
-    enum="UpdateEngineChromeOsVersionPrefix" expires_after="2023-03-26">
+    enum="UpdateEngineChromeOsVersionPrefix" expires_after="2023-06-11">
   <owner>mpolzer@google.com</owner>
   <owner>managed-platforms@google.com</owner>
   <summary>
@@ -365,7 +365,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.ConsecutiveUpdate.Count" units="updates"
-    expires_after="2023-03-05">
+    expires_after="2023-06-11">
   <owner>kimjae@chromium.org</owner>
   <owner>chromeos-core-services@google.com</owner>
   <summary>
@@ -751,7 +751,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.UrlSwitchCount" units="count"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>kimjae@chromium.org</owner>
   <owner>chromeos-core-services@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/v8/histograms.xml b/tools/metrics/histograms/metadata/v8/histograms.xml
index 962ef63..92546c8 100644
--- a/tools/metrics/histograms/metadata/v8/histograms.xml
+++ b/tools/metrics/histograms/metadata/v8/histograms.xml
@@ -1003,7 +1003,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCBackgroundMarking" units="ms" expires_after="2023-04-09">
+<histogram name="V8.GCBackgroundMarking" units="ms" expires_after="2023-06-11">
   <owner>mlippautz@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -1028,7 +1028,7 @@
 </histogram>
 
 <histogram name="V8.GCCompactorBackground" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -1056,7 +1056,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCFinalizeMC.Clear" units="ms" expires_after="2023-04-09">
+<histogram name="V8.GCFinalizeMC.Clear" units="ms" expires_after="2023-06-11">
   <owner>mlippautz@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
@@ -1126,7 +1126,7 @@
 </histogram>
 
 <histogram name="V8.GCFinalizeMCReduceMemoryBackground" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mlippautz@chromium.org</owner>
   <owner>hpayer@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
@@ -1149,7 +1149,7 @@
   </summary>
 </histogram>
 
-<histogram name="V8.GCIncrementalMarking" units="ms" expires_after="2023-04-09">
+<histogram name="V8.GCIncrementalMarking" units="ms" expires_after="2023-06-11">
   <owner>hpayer@chromium.org</owner>
   <owner>mlippautz@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
@@ -1190,7 +1190,7 @@
   <summary>Reason a mark-compact garbage collection was started in V8.</summary>
 </histogram>
 
-<histogram name="V8.GCMarkingSum" units="ms" expires_after="2023-04-09">
+<histogram name="V8.GCMarkingSum" units="ms" expires_after="2023-06-11">
   <owner>mlippautz@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -1201,7 +1201,7 @@
 </histogram>
 
 <histogram name="V8.GCScavenger.ScavengeMain" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mlippautz@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>
@@ -1211,7 +1211,7 @@
 </histogram>
 
 <histogram name="V8.GCScavenger.ScavengeRoots" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>mlippautz@chromium.org</owner>
   <owner>v8-memory-sheriffs@google.com</owner>
   <summary>Time spent in scavenging the roots during a V8 scavenge.</summary>
diff --git a/tools/metrics/histograms/metadata/web_apk/histograms.xml b/tools/metrics/histograms/metadata/web_apk/histograms.xml
index 4c876d5d..808000e 100644
--- a/tools/metrics/histograms/metadata/web_apk/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_apk/histograms.xml
@@ -210,7 +210,7 @@
 </histogram>
 
 <histogram name="WebApk.Notification.PermissionRequestResult"
-    enum="ContentSetting" expires_after="2023-03-26">
+    enum="ContentSetting" expires_after="2023-06-11">
   <owner>mvanouwerkerk@chromium.org</owner>
   <owner>peconn@chromium.org</owner>
   <owner>
@@ -286,7 +286,7 @@
 </histogram>
 
 <histogram name="WebApk.Startup.Cold.ShellLaunchToSplashscreenVisible"
-    units="ms" expires_after="2023-04-09">
+    units="ms" expires_after="2023-06-11">
   <owner>mheikal@chromium.org</owner>
   <owner>yfriedman@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/web_audio/histograms.xml b/tools/metrics/histograms/metadata/web_audio/histograms.xml
index 205a6a7..d08d7c8 100644
--- a/tools/metrics/histograms/metadata/web_audio/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_audio/histograms.xml
@@ -44,7 +44,7 @@
 </histogram>
 
 <histogram name="WebAudio.AudioBuffer.SampleRate384kHz" units="Hz"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hongchan@chromium.org</owner>
   <owner>mjwilson@chromium.org</owner>
   <owner>src/third_party/blink/renderer/modules/webaudio/OWNERS</owner>
@@ -89,7 +89,7 @@
 </histogram>
 
 <histogram name="WebAudio.AudioContext.latencyHintCategory" units="units"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hongchan@chromium.org</owner>
   <owner>mjwilson@chromium.org</owner>
   <summary>
@@ -102,7 +102,7 @@
 </histogram>
 
 <histogram name="WebAudio.AudioContext.latencyHintMilliSeconds" units="ms"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hongchan@chromium.org</owner>
   <owner>mjwilson@chromium.org</owner>
   <summary>
@@ -125,7 +125,7 @@
 </histogram>
 
 <histogram name="WebAudio.AudioContextOptions.sampleRate" units="Hz"
-    expires_after="2023-04-09">
+    expires_after="2023-06-11">
   <owner>hongchan@chromium.org</owner>
   <owner>mjwilson@chromium.org</owner>
   <owner>src/third_party/blink/renderer/modules/webaudio/OWNERS</owner>
diff --git a/tools/metrics/histograms/metadata/web_rtc/histograms.xml b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
index 05faed3b..2ff4bef 100644
--- a/tools/metrics/histograms/metadata/web_rtc/histograms.xml
+++ b/tools/metrics/histograms/metadata/web_rtc/histograms.xml
@@ -529,7 +529,7 @@
 </histogram>
 
 <histogram name="WebRTC.Audio.EchoCanceller.ReliableDelayEstimates"
-    enum="WebRTCAecDelayEstimateReliability" expires_after="2023-04-09">
+    enum="WebRTCAecDelayEstimateReliability" expires_after="2023-06-11">
   <owner>peah@chromium.org</owner>
   <owner>saza@chromium.org</owner>
   <owner>webrtc-audio-uma@google.com</owner>
@@ -683,7 +683,7 @@
 </histogram>
 
 <histogram name="WebRTC.AudioInputSampleRate" enum="AudioSampleRate"
-    expires_after="2023-04-11">
+    expires_after="2023-06-11">
   <owner>saza@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <owner>webrtc-audio-uma@google.com</owner>
@@ -754,34 +754,6 @@
   </summary>
 </histogram>
 
-<histogram name="WebRTC.BWE.MidCallProbing.Initiated" units="kbps"
-    expires_after="2020-02-16">
-  <owner>philipel@chromium.org</owner>
-  <summary>
-    The bitrate that will be probed, triggered by an update to the max
-    configured bitrate.
-  </summary>
-</histogram>
-
-<histogram name="WebRTC.BWE.MidCallProbing.ProbedKbps" units="kbps"
-    expires_after="2020-03-01">
-  <owner>philipel@chromium.org</owner>
-  <summary>
-    The resulting bitrate probed, triggered by an update to the max configured
-    bitrate.
-  </summary>
-</histogram>
-
-<histogram name="WebRTC.BWE.MidCallProbing.Success" units="kbps"
-    expires_after="2020-02-16">
-  <owner>philipel@chromium.org</owner>
-  <summary>
-    A successful probing attempt for a given bitrate, triggered by an update to
-    the max configured bitrate. NOTE! This is not the resulting bitrate from a
-    probing attempt, see WebRTC.BWE.MidCallProbing.ProbedKbps.
-  </summary>
-</histogram>
-
 <histogram name="WebRTC.BWE.Probing.ProbeClusterSizeInBytes" units="bytes"
     expires_after="2022-01-23">
   <owner>jonasolsson@chromium.org</owner>
@@ -1412,7 +1384,7 @@
 </histogram>
 
 <histogram name="WebRTC.PeerConnection.RtcpMux" enum="PeerConnectionRtcpMux"
-    expires_after="2023-04-07">
+    expires_after="2023-06-11">
   <owner>hta@chromium.org</owner>
   <owner>webrtc-dev@chromium.org</owner>
   <summary>
@@ -1995,7 +1967,7 @@
 </histogram>
 
 <histogram name="WebRTC.UserMediaRequest.Result2"
-    enum="MediaStreamRequestResult2" expires_after="2023-04-09">
+    enum="MediaStreamRequestResult2" expires_after="2023-06-11">
   <owner>toprice@chromium.org</owner>
   <owner>agpalak@chromium.org</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/webapps/histograms.xml b/tools/metrics/histograms/metadata/webapps/histograms.xml
index efd1c04..4e4baf8 100644
--- a/tools/metrics/histograms/metadata/webapps/histograms.xml
+++ b/tools/metrics/histograms/metadata/webapps/histograms.xml
@@ -23,7 +23,7 @@
 <histograms>
 
 <histogram name="AppBanners.BeforeInstallEvent"
-    enum="AppBannersBeforeInstallEvent" expires_after="2023-04-09">
+    enum="AppBannersBeforeInstallEvent" expires_after="2023-06-11">
   <owner>dominickn@chromium.org</owner>
   <owner>pjmclachlan@google.com</owner>
   <owner>desktop-pwas-team@google.com</owner>
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 78a5397..ca60eed2 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -404,4 +404,5 @@
  <item id="password_sync_token_fetcher" added_in_milestone="110" content_hash_code="0451c1ff" os_list="chromeos" file_path="chrome/browser/ash/login/saml/password_sync_token_fetcher.cc" />
  <item id="projector_xhr_loader" added_in_milestone="110" content_hash_code="071c4ac5" os_list="chromeos" file_path="ash/webui/projector_app/projector_xhr_sender.cc" />
  <item id="bruschetta_installer_download" added_in_milestone="110" content_hash_code="01b953f4" os_list="chromeos" file_path="chrome/browser/ash/bruschetta/bruschetta_installer.cc" />
+ <item id="quick_start_session_auth_requester" added_in_milestone="110" content_hash_code="08353e70" os_list="chromeos" file_path="chrome/browser/ash/login/oobe_quick_start/second_device_auth_broker.cc" />
 </annotations>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 0b17640c..62326cd 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -271,6 +271,7 @@
       <annotation id="nearby_webrtc_connection"/>
       <annotation id="projector_xhr_loader"/>
       <annotation id="bruschetta_installer_download"/>
+      <annotation id="quick_start_session_auth_requester"/>
     </sender>
   </group>
   <group name="Admin Features" hidden="true">
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 6dd12a54..3f51e891 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -255,6 +255,7 @@
   // Background filters.
   clone->SetBackgroundBlur(background_blur_sigma_);
   clone->SetBackgroundZoom(zoom_, zoom_inset_);
+  clone->SetBackgroundOffset(background_offset_);
 
   // Filters.
   clone->SetLayerSaturation(layer_saturation_);
@@ -676,6 +677,11 @@
   SetLayerBackgroundFilters();
 }
 
+void Layer::SetBackgroundOffset(const gfx::Point& background_offset) {
+  background_offset_ = background_offset;
+  SetLayerBackgroundFilters();
+}
+
 void Layer::SetAlphaShape(std::unique_ptr<ShapeRects> shape) {
   alpha_shape_ = std::move(shape);
 
@@ -735,6 +741,10 @@
     filters.Append(cc::FilterOperation::CreateBlurFilter(background_blur_sigma_,
                                                          SkTileMode::kClamp));
   }
+
+  if (!background_offset_.IsOrigin()) {
+    filters.Append(cc::FilterOperation::CreateOffsetFilter(background_offset_));
+  }
   cc_layer_->SetBackdropFilters(filters);
 }
 
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 1e530722..72d396030 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -305,6 +305,9 @@
   // edge across |inset| pixels.
   void SetBackgroundZoom(float zoom, int inset);
 
+  // Applies an offset when drawing pixels for the layer background filter.
+  void SetBackgroundOffset(const gfx::Point& background_offset);
+
   // Set the shape of this layer.
   const ShapeRects* alpha_shape() const { return alpha_shape_.get(); }
   void SetAlphaShape(std::unique_ptr<ShapeRects> shape);
@@ -764,6 +767,9 @@
   // Width of the border in pixels, where the scaling is blended.
   int zoom_inset_;
 
+  // Offset to apply when drawing pixels for the layer background filter.
+  gfx::Point background_offset_;
+
   // Shape of the window.
   std::unique_ptr<ShapeRects> alpha_shape_;
 
diff --git a/ui/file_manager/integration_tests/file_manager/gear_menu.js b/ui/file_manager/integration_tests/file_manager/gear_menu.js
index 16e5904..8f70b278 100644
--- a/ui/file_manager/integration_tests/file_manager/gear_menu.js
+++ b/ui/file_manager/integration_tests/file_manager/gear_menu.js
@@ -414,7 +414,10 @@
  * opens the feedback window.
  */
 testcase.showSendFeedbackAction = async () => {
-  const feedbackWindowUrl = 'chrome://feedback/';
+  const isOsFeedbackEnabled =
+      await sendTestMessage({name: 'isOsFeedbackEnabled'}) === 'true';
+  const feedbackWindowOrigin =
+      isOsFeedbackEnabled ? 'chrome://os-feedback' : 'chrome://feedback';
 
   // Open Files.App on Downloads.
   const appId = await openNewWindow(RootPath.DOWNLOADS);
@@ -431,7 +434,8 @@
   await remoteCall.waitForElement(appId, '#gear-menu:not([hidden])');
 
   // Check that there is no feedback window opened.
-  chrome.test.assertFalse(await remoteCall.windowUrlExists(feedbackWindowUrl));
+  chrome.test.assertFalse(
+      await remoteCall.windowOriginExists(feedbackWindowOrigin));
 
   // Click #send-feedback, which should be shown and enabled.
   await remoteCall.waitAndClickElement(
@@ -443,8 +447,8 @@
   // Check that the feedback window is open.
   const caller = getCaller();
   return repeatUntil(async () => {
-    if (!await remoteCall.windowUrlExists(feedbackWindowUrl)) {
-      return pending(caller, `Waiting for ${feedbackWindowUrl} to open`);
+    if (!await remoteCall.windowOriginExists(feedbackWindowOrigin)) {
+      return pending(caller, `Waiting for ${feedbackWindowOrigin} to open`);
     }
   });
 };
diff --git a/ui/file_manager/integration_tests/remote_call.js b/ui/file_manager/integration_tests/remote_call.js
index 13e9729..a924691 100644
--- a/ui/file_manager/integration_tests/remote_call.js
+++ b/ui/file_manager/integration_tests/remote_call.js
@@ -556,13 +556,13 @@
   }
 
   /**
-   * Returns whether an window exists with the expected URL.
-   * @param {string} expectedUrl
+   * Returns whether a window exists with the expected origin.
+   * @param {string} expectedOrigin
    * @return {!Promise<boolean>} Promise resolved with true or false depending
    *     on whether such window exists.
    */
-  async windowUrlExists(expectedUrl) {
-    const command = {name: 'expectWindowURL', expectedUrl: expectedUrl};
+  async windowOriginExists(expectedOrigin) {
+    const command = {name: 'expectWindowOrigin', expectedOrigin};
     const windowExists = await sendTestMessage(command);
     return windowExists == 'true';
   }
diff --git a/ui/gl/gl_fence_egl.cc b/ui/gl/gl_fence_egl.cc
index 8660e78..b70ed9e5 100644
--- a/ui/gl/gl_fence_egl.cc
+++ b/ui/gl/gl_fence_egl.cc
@@ -7,12 +7,14 @@
 #include "base/memory/ptr_util.h"
 #include "ui/gl/egl_util.h"
 #include "ui/gl/gl_bindings.h"
+#include "ui/gl/gl_bindings_autogen_gl.h"
 #include "ui/gl/gl_surface_egl.h"
 
 namespace gl {
 
 namespace {
 bool g_check_egl_fence_before_wait = false;
+bool g_flush_before_create_fence = false;
 }  // namespace
 
 GLFenceEGL::GLFenceEGL() = default;
@@ -40,10 +42,17 @@
   g_check_egl_fence_before_wait = true;
 }
 
+// static
+void GLFenceEGL::FlushBeforeCreateFence() {
+  g_flush_before_create_fence = true;
+}
+
 bool GLFenceEGL::InitializeInternal(EGLenum type, EGLint* attribs) {
   sync_ = EGL_NO_SYNC_KHR;
   display_ = eglGetCurrentDisplay();
   if (display_ != EGL_NO_DISPLAY) {
+    if (g_flush_before_create_fence)
+      glFlush();
     sync_ = eglCreateSyncKHR(display_, type, attribs);
     glFlush();
   }
diff --git a/ui/gl/gl_fence_egl.h b/ui/gl/gl_fence_egl.h
index 80a8be0..a1935547 100644
--- a/ui/gl/gl_fence_egl.h
+++ b/ui/gl/gl_fence_egl.h
@@ -30,6 +30,8 @@
   // i965 platforms. TODO(crbug.com/1246254): Remove this.
   static void CheckEGLFenceBeforeWait();
 
+  static void FlushBeforeCreateFence();
+
   // GLFence implementation:
   bool HasCompleted() override;
   void ClientWait() override;