diff --git a/DEPS b/DEPS
index 4fb17fb..c4f2950 100644
--- a/DEPS
+++ b/DEPS
@@ -304,15 +304,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': 'bab970602f56a36149edc0b1781bb255ac6ea94f',
+  'skia_revision': 'ef6e86c973d9d74dd578242afc7fa4100cbf3517',
   # 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': '2e6df1125542b4f2284301d807d546a9d00cdd57',
+  'v8_revision': '1ad52a3e65e0eca20e659a6bb3626573ba853b41',
   # 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': '1b8d11a580fbe5f0c7024ee7912f2d4361e11c18',
+  'angle_revision': '39ac3fab8d1000a521da7d94644812435efba85a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -375,7 +375,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'c8904f0248c4d61966bdf62d4e7f3bead51c4b27',
+  'catapult_revision': '70baabbf9304ff509407aba258e4b1e2f20e074c',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -467,7 +467,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.
-  'cros_components_revision': '247f84f08e5ce752f40cd2a74693ec0b8dc7bb7b',
+  'cros_components_revision': '11c179a6b1862edb6228bf474e16e2df45ae738b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -796,7 +796,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    'd358ebe285396c6f2c537634889e972ef5dc8c4d',
+    '4f6619d1b7963bc7e9815a33b6f877490c452cf2',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -825,7 +825,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + '329d9b5d276bc6762249fc2d1240cbc6312eb13f',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'f949cc1df66544fc723a396caaacf9f18733e3d3',
       'condition': 'checkout_ios',
   },
 
@@ -1188,7 +1188,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '68d90394c323220471625b03df4f94949284b279',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'c204c2c40869f5be9a2932efe8550a479cca1004',
       'condition': 'checkout_chromeos',
   },
 
@@ -1226,7 +1226,7 @@
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
 
   'src/third_party/devtools-frontend-internal': {
-      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '988ef18d0b769e9f18972973b3e781feda912690',
+      'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '62d0046d44bde48dd88c4da97d5369e4036f3298',
     'condition': 'checkout_src_internal',
   },
 
@@ -1693,7 +1693,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + '0ce368c86c8aa9cf6338ddcd80c485c3bf1e5e7e',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'b7b22f491aa4aac9db51e34cdb53a88e57543b35',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1878,7 +1878,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'e8dbfc3f48b4605bd0eb5bfb7c361434480b55fd',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'c0b470c327b8803000c61d93feafeb3fa5f0d2c0',
+    Var('webrtc_git') + '/src.git' + '@' + '52518633fbcbaf02deb5e5703ffd9a23534ade53',
 
   # 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.
@@ -1968,7 +1968,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': Var('chrome_git') + '/chrome/src-internal.git@3201722c75cc9aac2e0827cde347d2616f508eda',
+    'url': Var('chrome_git') + '/chrome/src-internal.git@53a267e20b1eb25a02a7fd1c896a4fe49bf94d3a',
     'condition': 'checkout_src_internal',
   },
 
@@ -2009,7 +2009,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'dxlLAiVW6rnv0SjxpLPWg9hos9Nb7RMwt4t3i0O2D3UC',
+        'version': 'MIyyB91sSIAFT8SAu6XCJi0eajWmZwNrMkjdnWVkesMC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4167,7 +4167,7 @@
 
   'src/ios_internal':  {
       'url': '{chrome_git}/chrome/ios_internal.git' + '@' +
-        '1858ff8a4321479da1b94cd0cfa40b3b8e5d5840',
+        '9e7489dab09b4754f4b0cf18835d4cc2ee6b5c40',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 81c8260..13415bd 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -105,7 +105,6 @@
     base_module_target = ":system_webview_base_bundle_module"
     bundle_name = "SystemWebView"
     min_sdk_version = default_min_sdk_version
-    compress_shared_libraries = true
   }
 
   if (is_official_build) {
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 840bff8..3b1c64a 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -354,6 +354,10 @@
             Flag.baseFeature(MetricsFeatures.REPORTING_SERVICE_FLUSH_PREFS_ON_UPLOAD_IN_BACKGROUND,
                     "Controls whether we immediately flush Local State after "
                             + "uploading a UMA log while in background."),
+            Flag.baseFeature(MetricsFeatures.SUBPROCESS_METRICS_PROVIDER_LEAKY,
+                    "Whether SubprocessMetricsProvider should be leaky, so that it can listen "
+                            + "to subprocesses exiting even after the MetricsService has been "
+                            + "destroyed."),
             Flag.baseFeature(ContentFeatures.MAIN_THREAD_COMPOSITING_PRIORITY,
                     "When enabled runs the main thread at compositing priority."),
             Flag.baseFeature(AwFeatures.WEBVIEW_UMA_UPLOAD_QUALITY_OF_SERVICE_SET_TO_DEFAULT,
diff --git a/android_webview/system_webview_bundle.gni b/android_webview/system_webview_bundle.gni
index 46ededdc..97fa93f 100644
--- a/android_webview/system_webview_bundle.gni
+++ b/android_webview/system_webview_bundle.gni
@@ -95,8 +95,6 @@
     enable_language_splits = true
     if (_is_trichrome) {
       min_sdk_version = 29
-    } else {
-      compress_shared_libraries = true
     }
     if (webview_includes_weblayer) {
       extra_modules = [ _weblayer_module_desc ]
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index be4721b..bdf6d3e 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -361,6 +361,8 @@
     "capture_mode/key_combo_view.h",
     "capture_mode/key_item_view.cc",
     "capture_mode/key_item_view.h",
+    "capture_mode/normal_capture_bar_view.cc",
+    "capture_mode/normal_capture_bar_view.h",
     "capture_mode/pointer_highlight_layer.cc",
     "capture_mode/pointer_highlight_layer.h",
     "capture_mode/recording_overlay_controller.cc",
diff --git a/ash/capture_mode/capture_audio_mixing_unittests.cc b/ash/capture_mode/capture_audio_mixing_unittests.cc
index 7190c2a..95a063f 100644
--- a/ash/capture_mode/capture_audio_mixing_unittests.cc
+++ b/ash/capture_mode/capture_audio_mixing_unittests.cc
@@ -135,18 +135,16 @@
   struct {
     const char* const scope_name;
     AudioRecordingMode audio_mode;
-    bool should_service_record_audio;
+    int expected_number_of_audio_capturers;
   } kTestCases[] = {
-      {"Off", AudioRecordingMode::kOff, /*should_service_record_audio=*/false},
+      {"Off", AudioRecordingMode::kOff,
+       /*expected_number_of_audio_capturers=*/0},
       {"Microphone", AudioRecordingMode::kMicrophone,
-       /*should_service_record_audio=*/true},
-
-      // TODO(afakhry): Update the following two cases when the service actually
-      // implements recording multiple streams.
+       /*expected_number_of_audio_capturers=*/1},
       {"System audio", AudioRecordingMode::kSystem,
-       /*should_service_record_audio=*/false},
+       /*expected_number_of_audio_capturers=*/1},
       {"System and microphone audio", AudioRecordingMode::kSystemAndMicrophone,
-       /*should_service_record_audio=*/true},
+       /*expected_number_of_audio_capturers=*/2},
   };
 
   for (const auto& test_case : kTestCases) {
@@ -160,8 +158,8 @@
     auto* test_delegate = static_cast<TestCaptureModeDelegate*>(
         controller->delegate_for_testing());
     CaptureModeTestApi().FlushRecordingServiceForTesting();
-    EXPECT_EQ(test_case.should_service_record_audio,
-              test_delegate->IsDoingAudioRecording());
+    EXPECT_EQ(test_case.expected_number_of_audio_capturers,
+              test_delegate->GetNumberOfAudioCapturers());
     controller->EndVideoRecording(EndRecordingReason::kStopRecordingButton);
 
     WaitForCaptureFileToBeSaved();
diff --git a/ash/capture_mode/capture_mode_bar_view.cc b/ash/capture_mode/capture_mode_bar_view.cc
index eaed47b..8ba189f 100644
--- a/ash/capture_mode/capture_mode_bar_view.cc
+++ b/ash/capture_mode/capture_mode_bar_view.cc
@@ -11,32 +11,23 @@
 #include "ash/capture_mode/capture_mode_metrics.h"
 #include "ash/capture_mode/capture_mode_session.h"
 #include "ash/capture_mode/capture_mode_session_focus_cycler.h"
-#include "ash/capture_mode/capture_mode_source_view.h"
-#include "ash/capture_mode/capture_mode_type_view.h"
 #include "ash/capture_mode/capture_mode_util.h"
-#include "ash/constants/ash_features.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 "ash/style/ash_color_provider.h"
 #include "ash/style/icon_button.h"
 #include "ash/style/system_shadow.h"
 #include "base/functional/bind.h"
 #include "chromeos/constants/chromeos_features.h"
-#include "ui/aura/window.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
-#include "ui/color/color_id.h"
 #include "ui/compositor/layer.h"
-#include "ui/gfx/geometry/size.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/strings/grit/ui_strings.h"
 #include "ui/views/background.h"
-#include "ui/views/controls/separator.h"
 #include "ui/views/highlight_border.h"
 #include "ui/views/layout/box_layout.h"
-#include "ui/views/view.h"
 
 namespace ash {
 
@@ -46,34 +37,30 @@
 
 constexpr int kBorderRadius = 20;
 
-constexpr int kSeparatorHeight = 20;
-
 }  // namespace
 
-CaptureModeBarView::CaptureModeBarView(CaptureModeBehavior* active_behavior)
-    : capture_type_view_(
-          AddChildView(std::make_unique<CaptureModeTypeView>(active_behavior))),
-      separator_1_(AddChildView(std::make_unique<views::Separator>())),
-      capture_source_view_(
-          AddChildView(std::make_unique<CaptureModeSourceView>())),
-      separator_2_(AddChildView(std::make_unique<views::Separator>())),
-      settings_button_(AddChildView(std::make_unique<IconButton>(
-          base::BindRepeating(&CaptureModeBarView::OnSettingsButtonPressed,
-                              base::Unretained(this)),
-          IconButton::Type::kMediumFloating,
-          &kCaptureModeSettingsIcon,
-          l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_TOOLTIP_SETTINGS),
-          /*is_togglable=*/true,
-          /*has_border=*/true))),
-      close_button_(AddChildView(std::make_unique<IconButton>(
-          base::BindRepeating(&CaptureModeBarView::OnCloseButtonPressed,
-                              base::Unretained(this)),
-          IconButton::Type::kMediumFloating,
-          &kCaptureModeCloseIcon,
-          l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE),
-          /*is_togglable=*/false,
-          /*has_border=*/true))),
-      shadow_(SystemShadow::CreateShadowOnNinePatchLayerForView(
+CaptureModeTypeView* CaptureModeBarView::capture_type_view() const {
+  return nullptr;
+}
+
+CaptureModeSourceView* CaptureModeBarView::capture_source_view() const {
+  return nullptr;
+}
+
+void CaptureModeBarView::OnCaptureSourceChanged(CaptureModeSource new_source) {
+  return;
+}
+
+void CaptureModeBarView::OnCaptureTypeChanged(CaptureModeType new_type) {
+  return;
+}
+
+void CaptureModeBarView::SetSettingsMenuShown(bool shown) {
+  settings_button_->SetToggled(shown);
+}
+
+CaptureModeBarView::CaptureModeBarView()
+    : shadow_(SystemShadow::CreateShadowOnNinePatchLayerForView(
           this,
           SystemShadow::Type::kElevation12)) {
   SetPaintToLayer();
@@ -89,20 +76,6 @@
   box_layout->set_cross_axis_alignment(
       views::BoxLayout::CrossAxisAlignment::kCenter);
 
-  // Customize the settings button toggled color.
-  settings_button_->SetIconToggledColorId(kColorAshButtonIconColor);
-  settings_button_->SetBackgroundToggledColorId(
-      kColorAshControlBackgroundColorInactive);
-
-  // Add highlight helper to settings button and close button.
-  CaptureModeSessionFocusCycler::HighlightHelper::Install(settings_button_);
-  CaptureModeSessionFocusCycler::HighlightHelper::Install(close_button_);
-
-  separator_1_->SetColorId(ui::kColorAshSystemUIMenuSeparator);
-  separator_1_->SetPreferredLength(kSeparatorHeight);
-  separator_2_->SetColorId(ui::kColorAshSystemUIMenuSeparator);
-  separator_2_->SetPreferredLength(kSeparatorHeight);
-
   capture_mode_util::SetHighlightBorder(
       this, kBorderRadius,
       chromeos::features::IsJellyrollEnabled()
@@ -114,17 +87,30 @@
 
 CaptureModeBarView::~CaptureModeBarView() = default;
 
-void CaptureModeBarView::OnCaptureSourceChanged(CaptureModeSource new_source) {
-  capture_source_view_->OnCaptureSourceChanged(new_source);
-}
+void CaptureModeBarView::AppendCommonElements() {
+  settings_button_ = AddChildView(std::make_unique<IconButton>(
+      base::BindRepeating(&CaptureModeBarView::OnSettingsButtonPressed,
+                          base::Unretained(this)),
+      IconButton::Type::kMediumFloating, &kCaptureModeSettingsIcon,
+      l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_TOOLTIP_SETTINGS),
+      /*is_togglable=*/true,
+      /*has_border=*/true));
+  close_button_ = AddChildView(std::make_unique<IconButton>(
+      base::BindRepeating(&CaptureModeBarView::OnCloseButtonPressed,
+                          base::Unretained(this)),
+      IconButton::Type::kMediumFloating, &kCaptureModeCloseIcon,
+      l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE),
+      /*is_togglable=*/false,
+      /*has_border=*/true));
 
-void CaptureModeBarView::OnCaptureTypeChanged(CaptureModeType new_type) {
-  capture_type_view_->OnCaptureTypeChanged(new_type);
-  capture_source_view_->OnCaptureTypeChanged(new_type);
-}
+  // Customize the settings button toggled color.
+  settings_button_->SetIconToggledColorId(kColorAshButtonIconColor);
+  settings_button_->SetBackgroundToggledColorId(
+      kColorAshControlBackgroundColorInactive);
 
-void CaptureModeBarView::SetSettingsMenuShown(bool shown) {
-  settings_button_->SetToggled(shown);
+  // Add highlight helper to settings button and close button.
+  CaptureModeSessionFocusCycler::HighlightHelper::Install(settings_button_);
+  CaptureModeSessionFocusCycler::HighlightHelper::Install(close_button_);
 }
 
 void CaptureModeBarView::OnSettingsButtonPressed(const ui::Event& event) {
diff --git a/ash/capture_mode/capture_mode_bar_view.h b/ash/capture_mode/capture_mode_bar_view.h
index 9be7334..a22d2d3 100644
--- a/ash/capture_mode/capture_mode_bar_view.h
+++ b/ash/capture_mode/capture_mode_bar_view.h
@@ -11,75 +11,48 @@
 #include "ui/base/metadata/metadata_header_macros.h"
 #include "ui/views/view.h"
 
-namespace views {
-class Separator;
-}  // namespace views
-
 namespace ash {
 
-class CaptureModeBehavior;
 class CaptureModeSourceView;
 class CaptureModeTypeView;
 class IconButton;
 class SystemShadow;
 
-// A view that acts as the content view of the capture mode bar widget.
-// It has a set of buttons to toggle between image and video capture, and
-// another set of buttons to toggle between fullscreen, region, and window
-// capture sources. It also contains a settings button. The structure looks like
-// this:
-//
-//   +---------------------------------------------------------------+
-//   |  +----------------+  |                       |                |
-//   |  |  +---+  +---+  |  |  +---+  +---+  +---+  |  +---+  +---+  |
-//   |  |  |   |  |   |  |  |  |   |  |   |  |   |  |  |   |  |   |  |
-//   |  |  +---+  +---+  |  |  +---+  +---+  +---+  |  +---+  +---+  |
-//   |  +----------------+  |  ^                 ^  |  ^      ^      |
-//   +--^----------------------|-----------------|-----|------|------+
-//   ^  |                      +-----------------+     |      |
-//   |  |                      |                       |      IconButton
-//   |  |                      |                       |
-//   |  |                      |                       IconButton
-//   |  |                      CaptureModeSourceView
-//   |  CaptureModeTypeView
-//   |
-//   CaptureModeBarView
-//
+// The contents of the capture bar can change based on the session initiation
+// type. Different clients of capture mode require different capture mode bar.
+// See `CaptureModeBehavior`.
 class ASH_EXPORT CaptureModeBarView : public views::View {
  public:
   METADATA_HEADER(CaptureModeBarView);
 
-  // The `active_behavior` decides the capture bar configurations.
-  explicit CaptureModeBarView(CaptureModeBehavior* active_behavior);
-  CaptureModeBarView(const CaptureModeBarView&) = delete;
-  CaptureModeBarView& operator=(const CaptureModeBarView&) = delete;
-  ~CaptureModeBarView() override;
-
-  CaptureModeTypeView* capture_type_view() const { return capture_type_view_; }
-  CaptureModeSourceView* capture_source_view() const {
-    return capture_source_view_;
-  }
   IconButton* settings_button() const { return settings_button_; }
   IconButton* close_button() const { return close_button_; }
 
+  // TODO(minch): Renames these two functions to GetCaptureTypeView and
+  // GetCaptureSourceView and updates all the clients.
+  virtual CaptureModeTypeView* capture_type_view() const;
+  virtual CaptureModeSourceView* capture_source_view() const;
+
   // Called when either the capture mode source or type changes.
-  void OnCaptureSourceChanged(CaptureModeSource new_source);
-  void OnCaptureTypeChanged(CaptureModeType new_type);
+  virtual void OnCaptureSourceChanged(CaptureModeSource new_source);
+  virtual void OnCaptureTypeChanged(CaptureModeType new_type);
 
   // Called when settings is toggled on or off.
   void SetSettingsMenuShown(bool shown);
 
+ protected:
+  CaptureModeBarView();
+  ~CaptureModeBarView() override;
+
+  // Adds the common elements of different capture bars to the bar view.
+  void AppendCommonElements();
+
  private:
   void OnSettingsButtonPressed(const ui::Event& event);
   void OnCloseButtonPressed();
 
-  // Owned by the views hierarchy.
-  raw_ptr<CaptureModeTypeView, ExperimentalAsh> capture_type_view_;
-  raw_ptr<views::Separator, ExperimentalAsh> separator_1_;
-  raw_ptr<CaptureModeSourceView, ExperimentalAsh> capture_source_view_;
-  raw_ptr<views::Separator, ExperimentalAsh> separator_2_;
-  raw_ptr<IconButton, ExperimentalAsh> settings_button_;
-  raw_ptr<IconButton, ExperimentalAsh> close_button_;
+  raw_ptr<IconButton, ExperimentalAsh> settings_button_ = nullptr;
+  raw_ptr<IconButton, ExperimentalAsh> close_button_ = nullptr;
   std::unique_ptr<SystemShadow> shadow_;
 };
 
diff --git a/ash/capture_mode/capture_mode_session.cc b/ash/capture_mode/capture_mode_session.cc
index b152d01..3e12e48 100644
--- a/ash/capture_mode/capture_mode_session.cc
+++ b/ash/capture_mode/capture_mode_session.cc
@@ -10,7 +10,6 @@
 #include "ash/accessibility/accessibility_controller_impl.h"
 #include "ash/accessibility/magnifier/magnifier_glass.h"
 #include "ash/capture_mode/capture_label_view.h"
-#include "ash/capture_mode/capture_mode_bar_view.h"
 #include "ash/capture_mode/capture_mode_behavior.h"
 #include "ash/capture_mode/capture_mode_camera_controller.h"
 #include "ash/capture_mode/capture_mode_camera_preview_view.h"
@@ -23,6 +22,7 @@
 #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/normal_capture_bar_view.h"
 #include "ash/capture_mode/recording_type_menu_view.h"
 #include "ash/capture_mode/user_nudge_controller.h"
 #include "ash/display/mouse_cursor_event_filter.h"
@@ -500,7 +500,7 @@
       capture_mode_util::GetCaptureBarBounds(current_root_, active_behavior_),
       "CaptureModeBarWidget"));
   capture_mode_bar_view_ = capture_mode_bar_widget_->SetContentsView(
-      std::make_unique<CaptureModeBarView>(active_behavior_));
+      std::make_unique<NormalCaptureBarView>(active_behavior_));
   capture_mode_bar_widget_->GetNativeWindow()->SetTitle(
       l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_A11Y_TITLE));
   capture_mode_bar_widget_->Show();
diff --git a/ash/capture_mode/normal_capture_bar_view.cc b/ash/capture_mode/normal_capture_bar_view.cc
new file mode 100644
index 0000000..8c0ce46f
--- /dev/null
+++ b/ash/capture_mode/normal_capture_bar_view.cc
@@ -0,0 +1,65 @@
+// Copyright 2023 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/normal_capture_bar_view.h"
+
+#include "ash/capture_mode/capture_mode_source_view.h"
+#include "ash/capture_mode/capture_mode_type_view.h"
+#include "ui/aura/window.h"
+#include "ui/base/metadata/metadata_impl_macros.h"
+#include "ui/color/color_id.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/views/controls/separator.h"
+
+namespace ash {
+
+namespace {
+
+constexpr int kSeparatorHeight = 20;
+
+}  // namespace
+
+NormalCaptureBarView::NormalCaptureBarView(
+    CaptureModeBehavior* active_behavior) {
+  capture_type_view_ =
+      AddChildView(std::make_unique<CaptureModeTypeView>(active_behavior));
+  views::Separator* separator_1 =
+      AddChildView(std::make_unique<views::Separator>());
+  capture_source_view_ =
+      AddChildView(std::make_unique<CaptureModeSourceView>());
+  views::Separator* separator_2 =
+      AddChildView(std::make_unique<views::Separator>());
+
+  separator_1->SetColorId(ui::kColorAshSystemUIMenuSeparator);
+  separator_1->SetPreferredLength(kSeparatorHeight);
+  separator_2->SetColorId(ui::kColorAshSystemUIMenuSeparator);
+  separator_2->SetPreferredLength(kSeparatorHeight);
+
+  AppendCommonElements();
+}
+
+NormalCaptureBarView::~NormalCaptureBarView() = default;
+
+CaptureModeTypeView* NormalCaptureBarView::capture_type_view() const {
+  return capture_type_view_;
+}
+
+CaptureModeSourceView* NormalCaptureBarView::capture_source_view() const {
+  return capture_source_view_;
+}
+
+void NormalCaptureBarView::OnCaptureSourceChanged(
+    CaptureModeSource new_source) {
+  capture_source_view_->OnCaptureSourceChanged(new_source);
+}
+
+void NormalCaptureBarView::OnCaptureTypeChanged(CaptureModeType new_type) {
+  capture_type_view_->OnCaptureTypeChanged(new_type);
+  capture_source_view_->OnCaptureTypeChanged(new_type);
+}
+
+BEGIN_METADATA(NormalCaptureBarView, CaptureModeBarView)
+END_METADATA
+
+}  // namespace ash
diff --git a/ash/capture_mode/normal_capture_bar_view.h b/ash/capture_mode/normal_capture_bar_view.h
new file mode 100644
index 0000000..37d5a67
--- /dev/null
+++ b/ash/capture_mode/normal_capture_bar_view.h
@@ -0,0 +1,65 @@
+// Copyright 2023 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_NORMAL_CAPTURE_BAR_VIEW_H_
+#define ASH_CAPTURE_MODE_NORMAL_CAPTURE_BAR_VIEW_H_
+
+#include "ash/ash_export.h"
+#include "ash/capture_mode/capture_mode_bar_view.h"
+#include "ash/capture_mode/capture_mode_types.h"
+#include "base/memory/raw_ptr.h"
+#include "ui/base/metadata/metadata_header_macros.h"
+
+namespace ash {
+
+class CaptureModeBehavior;
+class CaptureModeSourceView;
+class CaptureModeTypeView;
+
+// A view that acts as the content view of the capture mode bar widget. It has
+// a set of buttons to toggle between image and video capture, and another set
+// of buttons to toggle between fullscreen, region, and window capture sources.
+// It also contains a settings button. The structure looks like this:
+//
+//   +---------------------------------------------------------------+
+//   |  +----------------+  |                       |                |
+//   |  |  +---+  +---+  |  |  +---+  +---+  +---+  |  +---+  +---+  |
+//   |  |  |   |  |   |  |  |  |   |  |   |  |   |  |  |   |  |   |  |
+//   |  |  +---+  +---+  |  |  +---+  +---+  +---+  |  +---+  +---+  |
+//   |  +----------------+  |  ^                 ^  |  ^      ^      |
+//   +--^----------------------|-----------------|-----|------|------+
+//   ^  |                      +-----------------+     |      |
+//   |  |                      |                       |      IconButton
+//   |  |                      |                       |
+//   |  |                      |                       IconButton
+//   |  |                      CaptureModeSourceView
+//   |  CaptureModeTypeView
+//   |
+//   NormalCaptureBarView
+//
+class ASH_EXPORT NormalCaptureBarView : public CaptureModeBarView {
+ public:
+  METADATA_HEADER(NormalCaptureBarView);
+
+  // The `active_behavior` decides the capture bar configurations.
+  explicit NormalCaptureBarView(CaptureModeBehavior* active_behavior);
+  NormalCaptureBarView(const NormalCaptureBarView&) = delete;
+  NormalCaptureBarView& operator=(const NormalCaptureBarView&) = delete;
+  ~NormalCaptureBarView() override;
+
+  // CaptureModeBarView:
+  CaptureModeTypeView* capture_type_view() const override;
+  CaptureModeSourceView* capture_source_view() const override;
+  void OnCaptureSourceChanged(CaptureModeSource new_source) override;
+  void OnCaptureTypeChanged(CaptureModeType new_type) override;
+
+ private:
+  raw_ptr<CaptureModeTypeView, ExperimentalAsh> capture_type_view_ = nullptr;
+  raw_ptr<CaptureModeSourceView, ExperimentalAsh> capture_source_view_ =
+      nullptr;
+};
+
+}  // namespace ash
+
+#endif  // ASH_CAPTURE_MODE_NORMAL_CAPTURE_BAR_VIEW_H_
diff --git a/ash/capture_mode/test_capture_mode_delegate.cc b/ash/capture_mode/test_capture_mode_delegate.cc
index 51d48c6..05ed5ac 100644
--- a/ash/capture_mode/test_capture_mode_delegate.cc
+++ b/ash/capture_mode/test_capture_mode_delegate.cc
@@ -79,6 +79,11 @@
   return recording_service_ && recording_service_->IsDoingAudioRecording();
 }
 
+int TestCaptureModeDelegate::GetNumberOfAudioCapturers() const {
+  return recording_service_ ? recording_service_->GetNumberOfAudioCapturers()
+                            : 0;
+}
+
 base::FilePath TestCaptureModeDelegate::GetUserDefaultDownloadsFolder() const {
   DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
 
diff --git a/ash/capture_mode/test_capture_mode_delegate.h b/ash/capture_mode/test_capture_mode_delegate.h
index b192f01..3f81bf7 100644
--- a/ash/capture_mode/test_capture_mode_delegate.h
+++ b/ash/capture_mode/test_capture_mode_delegate.h
@@ -83,6 +83,9 @@
   // currently recording audio.
   bool IsDoingAudioRecording() const;
 
+  // Returns the number of audio capturers owned by the recording service.
+  int GetNumberOfAudioCapturers() const;
+
   // CaptureModeDelegate:
   base::FilePath GetUserDefaultDownloadsFolder() const override;
   void ShowScreenCaptureItemInFolder(const base::FilePath& file_path) override;
diff --git a/ash/system/media/media_notification_provider.h b/ash/system/media/media_notification_provider.h
index 489c288..71dc7865 100644
--- a/ash/system/media/media_notification_provider.h
+++ b/ash/system/media/media_notification_provider.h
@@ -57,10 +57,6 @@
       bool should_clip_height,
       const std::string& item_id = "") = 0;
 
-  // Returns a MediaNotificationContainerimplView for the active MediaSession.
-  // Displayed in the quick settings of the Ash shelf.
-  virtual std::unique_ptr<views::View> GetActiveMediaNotificationView() = 0;
-
   // Used for ash to notify the bubble is closing.
   virtual void OnBubbleClosing() = 0;
 
diff --git a/ash/system/media/media_tray.cc b/ash/system/media/media_tray.cc
index 99e7664e3..c8a93c0 100644
--- a/ash/system/media/media_tray.cc
+++ b/ash/system/media/media_tray.cc
@@ -78,21 +78,6 @@
   return diagonal_len > kMinimumScreenSizeDiagonal;
 }
 
-// Used for getting default pin state for experiment.
-bool GetIsPinnedToShelfByFeatureParams() {
-  switch (media::kCrosGlobalMediaControlsPinParam.Get()) {
-    case media::kCrosGlobalMediaControlsPinOptions::kPin:
-      return true;
-    case media::kCrosGlobalMediaControlsPinOptions::kNotPin:
-      return false;
-    case media::kCrosGlobalMediaControlsPinOptions::kHeuristic:
-      return GetIsPinnedToShelfByDefault();
-  }
-
-  NOTREACHED();
-  return false;
-}
-
 // Enum that specifies the pin state of global media controls.
 enum PinState {
   kDefault = 0,
@@ -178,7 +163,7 @@
     case PinState::kUnpinned:
       return false;
     case PinState::kDefault:
-      return GetIsPinnedToShelfByFeatureParams();
+      return GetIsPinnedToShelfByDefault();
   }
 
   NOTREACHED();
diff --git a/ash/system/media/media_tray_unittest.cc b/ash/system/media/media_tray_unittest.cc
index 3e6c10b..44d0eda 100644
--- a/ash/system/media/media_tray_unittest.cc
+++ b/ash/system/media/media_tray_unittest.cc
@@ -13,9 +13,7 @@
 #include "ash/system/tray/tray_bubble_wrapper.h"
 #include "ash/test/ash_test_base.h"
 #include "base/memory/raw_ptr.h"
-#include "base/test/scoped_feature_list.h"
 #include "components/media_message_center/media_notification_view_impl.h"
-#include "media/base/media_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/events/event.h"
 
@@ -42,13 +40,10 @@
     MediaNotificationProvider::Set(old_provider_);
   }
 
-  // Medianotificationprovider implementations.
+  // MediaNotificationProvider implementations.
   MOCK_METHOD((std::unique_ptr<views::View>),
               GetMediaNotificationListView,
               (int, bool, const std::string&));
-  MOCK_METHOD((std::unique_ptr<views::View>),
-              GetActiveMediaNotificationView,
-              ());
   MOCK_METHOD(void, OnBubbleClosing, ());
   MOCK_METHOD(global_media_controls::MediaItemManager*,
               GetMediaItemManager,
@@ -100,7 +95,6 @@
   ~MediaTrayTest() override = default;
 
   void SetUp() override {
-    feature_list_.InitAndEnableFeature(media::kGlobalMediaControlsForChromeOS);
     AshTestBase::SetUp();
 
     provider_ = std::make_unique<MockMediaNotificationProvider>();
@@ -170,8 +164,6 @@
   std::unique_ptr<MockMediaNotificationProvider> provider_;
   raw_ptr<MediaTray, ExperimentalAsh> media_tray_;
   std::unique_ptr<MockTrayBackgroundView> mock_tray_;
-
-  base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_F(MediaTrayTest, MediaTrayVisibilityTest) {
@@ -431,51 +423,4 @@
   EXPECT_NE(nullptr, media_tray()->GetBubbleView());
 }
 
-class MediaTrayPinnedParamTest : public AshTestBase {
- public:
-  MediaTrayPinnedParamTest() = default;
-  ~MediaTrayPinnedParamTest() override = default;
-
-  void SetUp() override {
-    auto& pin_param = media::kCrosGlobalMediaControlsPinParam;
-    feature_list_.InitAndEnableFeatureWithParameters(
-        media::kGlobalMediaControlsForChromeOS,
-        {{pin_param.name,
-          pin_param.GetName(media::kCrosGlobalMediaControlsPinOptions::kPin)}});
-    AshTestBase::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-TEST_F(MediaTrayPinnedParamTest, PinParamTest) {
-  UpdateDisplay("200x100");
-  EXPECT_TRUE(MediaTray::IsPinnedToShelf());
-}
-
-class MediaTrayNotPinnedParamTest : public AshTestBase {
- public:
-  MediaTrayNotPinnedParamTest() = default;
-  ~MediaTrayNotPinnedParamTest() override = default;
-
-  void SetUp() override {
-    auto& pin_param = media::kCrosGlobalMediaControlsPinParam;
-    feature_list_.InitAndEnableFeatureWithParameters(
-        media::kGlobalMediaControlsForChromeOS,
-        {{pin_param.name,
-          pin_param.GetName(
-              media::kCrosGlobalMediaControlsPinOptions::kNotPin)}});
-    AshTestBase::SetUp();
-  }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-TEST_F(MediaTrayNotPinnedParamTest, PinParamTest) {
-  UpdateDisplay("2560x1440");
-  EXPECT_FALSE(MediaTray::IsPinnedToShelf());
-}
-
 }  // namespace ash
diff --git a/ash/system/media/unified_media_controls_container_unittest.cc b/ash/system/media/unified_media_controls_container_unittest.cc
index 6dac4af..d734901 100644
--- a/ash/system/media/unified_media_controls_container_unittest.cc
+++ b/ash/system/media/unified_media_controls_container_unittest.cc
@@ -13,8 +13,6 @@
 #include "ash/system/unified/unified_system_tray_bubble.h"
 #include "ash/system/unified/unified_system_tray_view.h"
 #include "ash/test/ash_test_base.h"
-#include "base/test/scoped_feature_list.h"
-#include "media/base/media_switches.h"
 
 namespace ash {
 
@@ -24,7 +22,6 @@
   ~UnifiedMediaControlsContainerTest() override = default;
 
   void SetUp() override {
-    feature_list_.InitAndEnableFeature(media::kGlobalMediaControlsForChromeOS);
     AshTestBase::SetUp();
 
     // Ensure media tray is not pinned to shelf so that media controls
@@ -46,9 +43,6 @@
   UnifiedMediaControlsContainer* media_controls_container() {
     return system_tray_view()->media_controls_container_for_testing();
   }
-
- private:
-  base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_F(UnifiedMediaControlsContainerTest, DoNotShowControlsWhenInDetailedView) {
diff --git a/ash/system/media/unified_media_controls_controller_unittest.cc b/ash/system/media/unified_media_controls_controller_unittest.cc
index c677fee..236f3b2 100644
--- a/ash/system/media/unified_media_controls_controller_unittest.cc
+++ b/ash/system/media/unified_media_controls_controller_unittest.cc
@@ -7,9 +7,7 @@
 #include "ash/system/media/unified_media_controls_view.h"
 #include "ash/test/ash_test_base.h"
 #include "base/ranges/algorithm.h"
-#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
-#include "media/base/media_switches.h"
 #include "services/media_session/public/cpp/test/test_media_controller.h"
 #include "services/media_session/public/mojom/media_session.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -54,7 +52,6 @@
   ~UnifiedMediaControlsControllerTest() override = default;
 
   void SetUp() override {
-    feature_list_.InitAndEnableFeature(media::kGlobalMediaControlsForChromeOS);
     AshTestBase::SetUp();
 
     mock_delegate_ = std::make_unique<MockMediaControlsDelegate>();
@@ -173,8 +170,6 @@
         std::vector<MediaSessionAction>(actions_.begin(), actions_.end()));
   }
 
-  base::test::ScopedFeatureList feature_list_;
-
   std::unique_ptr<views::Widget> widget_;
   std::unique_ptr<UnifiedMediaControlsController> controller_;
   std::unique_ptr<UnifiedMediaControlsView> media_controls_;
diff --git a/ash/system/media/unified_media_controls_detailed_view_controller_unittest.cc b/ash/system/media/unified_media_controls_detailed_view_controller_unittest.cc
index a24b084..d81af5c 100644
--- a/ash/system/media/unified_media_controls_detailed_view_controller_unittest.cc
+++ b/ash/system/media/unified_media_controls_detailed_view_controller_unittest.cc
@@ -44,9 +44,6 @@
               GetMediaNotificationListView,
               (int, bool, const std::string&));
   MOCK_METHOD(void, OnBubbleClosing, ());
-  std::unique_ptr<views::View> GetActiveMediaNotificationView() override {
-    return std::make_unique<views::View>();
-  }
   MOCK_METHOD(global_media_controls::MediaItemManager*,
               GetMediaItemManager,
               ());
diff --git a/ash/system/status_area_widget.cc b/ash/system/status_area_widget.cc
index 824b6f0a..b093cee 100644
--- a/ash/system/status_area_widget.cc
+++ b/ash/system/status_area_widget.cc
@@ -50,7 +50,6 @@
 #include "base/i18n/time_formatting.h"
 #include "base/metrics/histogram_macros.h"
 #include "chromeos/ash/services/assistant/public/cpp/features.h"
-#include "media/base/media_switches.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/display/display.h"
@@ -139,9 +138,7 @@
 
   palette_tray_ = AddTrayButton(std::make_unique<PaletteTray>(shelf_));
 
-  if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsForChromeOS)) {
-    media_tray_ = AddTrayButton(std::make_unique<MediaTray>(shelf_));
-  }
+  media_tray_ = AddTrayButton(std::make_unique<MediaTray>(shelf_));
 
   if (features::IsEcheSWAEnabled()) {
     eche_tray_ = AddTrayButton(std::make_unique<EcheTray>(shelf_));
diff --git a/ash/system/unified/quick_settings_view.cc b/ash/system/unified/quick_settings_view.cc
index 07dc62c..1d48a70 100644
--- a/ash/system/unified/quick_settings_view.cc
+++ b/ash/system/unified/quick_settings_view.cc
@@ -155,8 +155,7 @@
   if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsCrOSUpdatedUI)) {
     media_view_container_ = system_tray_container_->AddChildView(
         std::make_unique<QuickSettingsMediaViewContainer>(controller_));
-  } else if (base::FeatureList::IsEnabled(
-                 media::kGlobalMediaControlsForChromeOS)) {
+  } else {
     media_controls_container_ = system_tray_container_->AddChildView(
         std::make_unique<UnifiedMediaControlsContainer>());
     media_controls_container_->SetExpandedAmount(1.0f);
@@ -208,6 +207,7 @@
 }
 
 void QuickSettingsView::ShowMediaControls() {
+  DCHECK(media_controls_container_);
   media_controls_container_->SetShouldShowMediaControls(true);
 
   if (detailed_view_container_->GetVisible()) {
diff --git a/ash/system/unified/quick_settings_view.h b/ash/system/unified/quick_settings_view.h
index 7647c5c..7f832c59 100644
--- a/ash/system/unified/quick_settings_view.h
+++ b/ash/system/unified/quick_settings_view.h
@@ -54,12 +54,10 @@
   views::View* AddSliderView(std::unique_ptr<views::View> slider_view);
 
   // Adds media controls view to `media_controls_container_`. Only called if
-  // media::kGlobalMediaControlsForChromeOS is enabled and
   // media::kGlobalMediaControlsCrOSUpdatedUI is disabled.
   void AddMediaControlsView(views::View* media_controls);
 
   // Shows media controls view. Only called if
-  // media::kGlobalMediaControlsForChromeOS is enabled and
   // media::kGlobalMediaControlsCrOSUpdatedUI is disabled.
   void ShowMediaControls();
 
@@ -140,7 +138,7 @@
   raw_ptr<QuickSettingsFooter, ExperimentalAsh> footer_ = nullptr;
   raw_ptr<views::View, ExperimentalAsh> detailed_view_container_ = nullptr;
 
-  // Null if media::kGlobalMediaControlsForChromeOS is disabled.
+  // Null if media::kGlobalMediaControlsCrOSUpdatedUI is enabled.
   raw_ptr<UnifiedMediaControlsContainer, ExperimentalAsh>
       media_controls_container_ = nullptr;
 
diff --git a/ash/system/unified/unified_system_tray_controller.cc b/ash/system/unified/unified_system_tray_controller.cc
index adf026a99..c23c5eb 100644
--- a/ash/system/unified/unified_system_tray_controller.cc
+++ b/ash/system/unified/unified_system_tray_controller.cc
@@ -172,8 +172,7 @@
 
   InitFeaturePods();
 
-  if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsForChromeOS) &&
-      !Shell::Get()->session_controller()->IsScreenLocked() &&
+  if (!Shell::Get()->session_controller()->IsScreenLocked() &&
       !MediaTray::IsPinnedToShelf()) {
     media_controls_controller_ =
         std::make_unique<UnifiedMediaControlsController>(this);
@@ -201,8 +200,7 @@
   auto qs_view = std::make_unique<QuickSettingsView>(this);
   quick_settings_view_ = qs_view.get();
 
-  if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsForChromeOS) &&
-      !Shell::Get()->session_controller()->IsScreenLocked() &&
+  if (!Shell::Get()->session_controller()->IsScreenLocked() &&
       !MediaTray::IsPinnedToShelf()) {
     if (base::FeatureList::IsEnabled(
             media::kGlobalMediaControlsCrOSUpdatedUI)) {
diff --git a/ash/system/unified/unified_system_tray_view.cc b/ash/system/unified/unified_system_tray_view.cc
index 7369b9a..168f3de2 100644
--- a/ash/system/unified/unified_system_tray_view.cc
+++ b/ash/system/unified/unified_system_tray_view.cc
@@ -25,7 +25,6 @@
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "base/memory/raw_ptr.h"
-#include "media/base/media_switches.h"
 #include "ui/accessibility/ax_enums.mojom.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/compositor/layer.h"
@@ -190,6 +189,7 @@
       system_info_view_(new UnifiedSystemInfoView(controller_)),
       system_tray_container_(new SystemTrayContainer()),
       detailed_view_container_(new DetailedViewContainer()),
+      media_controls_container_(new UnifiedMediaControlsContainer()),
       focus_search_(std::make_unique<views::FocusSearch>(this, false, false)),
       interacted_by_tap_recorder_(
           std::make_unique<InteractedByTapRecorder>(this)) {
@@ -214,11 +214,8 @@
   system_tray_container_->AddChildView(feature_pods_container_.get());
   system_tray_container_->AddChildView(page_indicator_view_.get());
 
-  if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsForChromeOS)) {
-    media_controls_container_ = new UnifiedMediaControlsContainer();
-    system_tray_container_->AddChildView(media_controls_container_.get());
-    media_controls_container_->SetExpandedAmount(expanded_amount_);
-  }
+  system_tray_container_->AddChildView(media_controls_container_.get());
+  media_controls_container_->SetExpandedAmount(expanded_amount_);
 
   system_tray_container_->AddChildView(sliders_container_.get());
 
@@ -240,17 +237,13 @@
 void UnifiedSystemTrayView::SetMaxHeight(int max_height) {
   max_height_ = max_height;
 
-  int media_controls_container_height =
-      media_controls_container_ ? media_controls_container_->GetExpandedHeight()
-                                : 0;
-
   // FeaturePodsContainer can adjust it's height by reducing the number of rows
   // it uses. It will calculate how many rows to use based on the max height
   // passed here.
   feature_pods_container_->SetMaxHeight(
       max_height - top_shortcuts_view_->GetPreferredSize().height() -
       page_indicator_view_->GetPreferredSize().height() -
-      media_controls_container_height -
+      media_controls_container_->GetExpandedHeight() -
       sliders_container_->GetExpandedHeight() -
       system_info_view_->GetPreferredSize().height());
 }
@@ -266,7 +259,6 @@
 
 void UnifiedSystemTrayView::AddMediaControlsView(views::View* media_controls) {
   DCHECK(media_controls);
-  DCHECK(media_controls_container_);
 
   media_controls->SetPaintToLayer();
   media_controls->layer()->SetFillsBoundsOpaquely(false);
@@ -302,9 +294,7 @@
 void UnifiedSystemTrayView::ResetDetailedView() {
   detailed_view_container_->RemoveAllChildViews();
   detailed_view_container_->SetVisible(false);
-  if (media_controls_container_) {
-    media_controls_container_->MaybeShowMediaControls();
-  }
+  media_controls_container_->MaybeShowMediaControls();
   system_tray_container_->SetVisible(true);
   sliders_container_->UpdateOpacity();
   PreferredSizeChanged();
@@ -333,9 +323,7 @@
   top_shortcuts_view_->SetExpandedAmount(expanded_amount);
   feature_pods_container_->SetExpandedAmount(expanded_amount);
   page_indicator_view_->SetExpandedAmount(expanded_amount);
-  if (media_controls_container_) {
-    media_controls_container_->SetExpandedAmount(expanded_amount);
-  }
+  media_controls_container_->SetExpandedAmount(expanded_amount);
   sliders_container_->SetExpandedAmount(expanded_amount);
 
   PreferredSizeChanged();
@@ -345,9 +333,6 @@
 }
 
 int UnifiedSystemTrayView::GetExpandedSystemTrayHeight() const {
-  int media_controls_container_height =
-      media_controls_container_ ? media_controls_container_->GetExpandedHeight()
-                                : 0;
   return (notification_hidden_view_->GetVisible()
               ? notification_hidden_view_->GetPreferredSize().height()
               : 0) +
@@ -355,7 +340,7 @@
          feature_pods_container_->GetExpandedHeight() +
          page_indicator_view_->GetExpandedHeight() +
          sliders_container_->GetExpandedHeight() +
-         media_controls_container_height +
+         media_controls_container_->GetExpandedHeight() +
          system_info_view_->GetPreferredSize().height();
 }
 
diff --git a/ash/system/unified/unified_system_tray_view.h b/ash/system/unified/unified_system_tray_view.h
index 291c31f5..ec2bf814 100644
--- a/ash/system/unified/unified_system_tray_view.h
+++ b/ash/system/unified/unified_system_tray_view.h
@@ -199,10 +199,8 @@
   const raw_ptr<UnifiedSystemInfoView, ExperimentalAsh> system_info_view_;
   const raw_ptr<SystemTrayContainer, ExperimentalAsh> system_tray_container_;
   const raw_ptr<views::View, ExperimentalAsh> detailed_view_container_;
-
-  // Null if media::kGlobalMediaControlsForChromeOS is disabled.
-  raw_ptr<UnifiedMediaControlsContainer, ExperimentalAsh>
-      media_controls_container_ = nullptr;
+  const raw_ptr<UnifiedMediaControlsContainer, ExperimentalAsh>
+      media_controls_container_;
 
   // The maximum height available to the view.
   int max_height_ = 0;
diff --git a/ash/webui/personalization_app/resources/js/ambient/ambient_subpage_element.html b/ash/webui/personalization_app/resources/js/ambient/ambient_subpage_element.html
index 4fb9a45..1da7ad529 100644
--- a/ash/webui/personalization_app/resources/js/ambient/ambient_subpage_element.html
+++ b/ash/webui/personalization_app/resources/js/ambient/ambient_subpage_element.html
@@ -165,9 +165,7 @@
           </div>
         </template>
         <template is="dom-if" if="[[!loading_]]">
-          <toggle-row id="ambientToggleRow" checked="[[ambientModeEnabled_]]"
-              on-click="onClickAmbientModeButton_" on-change="onToggleStateChanged_">
-          </toggle-row>
+          <toggle-row id="ambientToggleRow"></toggle-row>
           <template is="dom-if" if="[[ambientModeEnabled_]]">
             <animation-theme-list
                 selected-animation-theme="[[animationTheme_]]">
@@ -247,9 +245,7 @@
           </div>
         </template>
         <template is="dom-if" if="[[!loading_]]">
-          <toggle-row id="ambientToggleRow" class="clickable" checked="[[ambientModeEnabled_]]"
-              on-click="onClickAmbientModeButton_" on-change="onToggleStateChanged_">
-          </toggle-row>
+          <toggle-row id="ambientToggleRow"></toggle-row>
           <template is="dom-if" if="[[!ambientModeEnabled_]]">
             <ambient-zero-state id="zeroState"></ambient-zero-state>
           </template>
diff --git a/ash/webui/personalization_app/resources/js/ambient/ambient_subpage_element.ts b/ash/webui/personalization_app/resources/js/ambient/ambient_subpage_element.ts
index 980e75c8..8d1e375 100644
--- a/ash/webui/personalization_app/resources/js/ambient/ambient_subpage_element.ts
+++ b/ash/webui/personalization_app/resources/js/ambient/ambient_subpage_element.ts
@@ -27,7 +27,6 @@
 import {getAmbientProvider} from './ambient_interface_provider.js';
 import {AmbientObserver} from './ambient_observer.js';
 import {getTemplate} from './ambient_subpage_element.html.js';
-import {ToggleRow} from './toggle_row_element.js';
 import {getZerosArray} from './utils.js';
 
 export class AmbientSubpage extends WithPersonalizationStore {
@@ -174,17 +173,6 @@
     }
   }
 
-  private onClickAmbientModeButton_(event: Event) {
-    event.stopPropagation();
-    this.setAmbientModeEnabled_(!this.ambientModeEnabled_);
-  }
-
-  private onToggleStateChanged_(event: Event) {
-    const toggleRow = event.currentTarget as ToggleRow;
-    const ambientModeEnabled = toggleRow!.checked;
-    this.setAmbientModeEnabled_(ambientModeEnabled);
-  }
-
   private setAmbientModeEnabled_(ambientModeEnabled: boolean) {
     setAmbientModeEnabled(
         ambientModeEnabled, getAmbientProvider(), this.getStore());
diff --git a/ash/webui/personalization_app/resources/js/ambient/toggle_row_element.html b/ash/webui/personalization_app/resources/js/ambient/toggle_row_element.html
index 76b89c7..e5b230b 100644
--- a/ash/webui/personalization_app/resources/js/ambient/toggle_row_element.html
+++ b/ash/webui/personalization_app/resources/js/ambient/toggle_row_element.html
@@ -5,14 +5,15 @@
 </style>
 <template is="dom-if" if="[[isPersonalizationJellyEnabled_]]">
   <h3 id="toggleRowTitle" class="ambient-subpage-element-title" aria-hidden="true">
-    [[getToggleRowTitle_(checked)]]
+    [[getToggleRowTitle_(ambientModeEnabled_)]]
   </h3>
 </template>
 <div class="ambient-toggle-row-container">
   <div class="ambient-toggle-row">
     <p id="toggleDescription">$i18n{ambientModePageDescription}</p>
-    <cr-toggle id="toggle" checked="{{checked}}"
-        aria-labelledby="toggleDescription">
+    <cr-toggle id="toggle" class="clickable" checked="[[ambientModeEnabled_]]"
+        aria-labelledby="toggleDescription"
+        on-change="onAmbientModeToggled_">
     </cr-toggle>
   </div>
 </div>
diff --git a/ash/webui/personalization_app/resources/js/ambient/toggle_row_element.ts b/ash/webui/personalization_app/resources/js/ambient/toggle_row_element.ts
index 6eeeb0e..16b1675 100644
--- a/ash/webui/personalization_app/resources/js/ambient/toggle_row_element.ts
+++ b/ash/webui/personalization_app/resources/js/ambient/toggle_row_element.ts
@@ -14,6 +14,8 @@
 import {isPersonalizationJellyEnabled} from '../load_time_booleans.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
 
+import {setAmbientModeEnabled} from './ambient_controller.js';
+import {getAmbientProvider} from './ambient_interface_provider.js';
 import {getTemplate} from './toggle_row_element.html.js';
 
 export interface ToggleRow {
@@ -31,33 +33,46 @@
 
   static get properties() {
     return {
-      checked:
-          {type: Boolean, value: false, notify: true, reflectToAttribute: true},
-
       isPersonalizationJellyEnabled_: {
         type: Boolean,
         value() {
           return isPersonalizationJellyEnabled();
         },
       },
+      ambientModeEnabled_: Boolean,
     };
   }
 
-  checked: boolean;
   private isPersonalizationJellyEnabled_: boolean;
+  private ambientModeEnabled_: boolean|null;
   override ariaLabel: string;
 
   override focus() {
     this.$.toggle.focus();
   }
 
+  override connectedCallback() {
+    super.connectedCallback();
+    this.watch<ToggleRow['ambientModeEnabled_']>(
+        'ambientModeEnabled_', state => state.ambient.ambientModeEnabled);
+    this.updateFromStore();
+  }
+
   private getAriaLabel_(): string {
-    return this.i18n(this.checked ? 'ambientModeOn' : 'ambientModeOff');
+    return this.i18n(
+        this.ambientModeEnabled_ ? 'ambientModeOn' : 'ambientModeOff');
   }
 
   private getToggleRowTitle_(): string {
     return this.getAriaLabel_().toUpperCase();
   }
+
+  private onAmbientModeToggled_(event: Event) {
+    const toggleButton = event.currentTarget as CrToggleElement;
+    const ambientModeEnabled = toggleButton!.checked;
+    setAmbientModeEnabled(
+        ambientModeEnabled, getAmbientProvider(), this.getStore());
+  }
 }
 
 declare global {
diff --git a/ash/webui/projector_app/BUILD.gn b/ash/webui/projector_app/BUILD.gn
index b61f30583..b167adf 100644
--- a/ash/webui/projector_app/BUILD.gn
+++ b/ash/webui/projector_app/BUILD.gn
@@ -73,6 +73,7 @@
     ":projector_app",
     "//ash/public/cpp:cpp",
     "//ash/public/cpp:test_support",
+    "//ash/webui/projector_app/public/mojom:projector_mojo_bindings",
     "//base/test:test_support",
     "//components/signin/public/identity_manager",
     "//components/signin/public/identity_manager:test_support",
diff --git a/ash/webui/projector_app/mojom/BUILD.gn b/ash/webui/projector_app/mojom/BUILD.gn
index 63fee38..c150dc59 100644
--- a/ash/webui/projector_app/mojom/BUILD.gn
+++ b/ash/webui/projector_app/mojom/BUILD.gn
@@ -17,6 +17,10 @@
 
 mojom("projector_mojo_bindings") {
   sources = [ "untrusted_projector.mojom" ]
-  deps = [ "//ash/webui/projector_app/public/mojom:projector_mojo_bindings" ]
+  deps = [
+    "//ash/webui/projector_app/public/mojom:projector_mojo_bindings",
+    "//mojo/public/mojom/base",
+    "//url/mojom:url_mojom_gurl",
+  ]
   webui_module_path = "/$cur_dir"
 }
diff --git a/ash/webui/projector_app/mojom/untrusted_projector.mojom b/ash/webui/projector_app/mojom/untrusted_projector.mojom
index 397b4a9c..f19aa92 100644
--- a/ash/webui/projector_app/mojom/untrusted_projector.mojom
+++ b/ash/webui/projector_app/mojom/untrusted_projector.mojom
@@ -6,6 +6,7 @@
 import "ash/webui/projector_app/public/mojom/projector_types.mojom";
 import "mojo/public/mojom/base/values.mojom";
 import "mojo/public/mojom/base/safe_base_name.mojom";
+import "url/mojom/url.mojom";
 
 // UntrustedProjectorPageHandler handles requests that come from the
 // javascript to the browser process.
@@ -43,6 +44,28 @@
   // fail if the new screencast preconditions are not met.
   StartProjectorSession(
     mojo_base.mojom.SafeBaseName storage_dir_name) => (bool success);
+
+  // Used to send xhr requests through the browser process. This is
+  // needed due to cross-origin-resource-sharing restriction set by
+  // Google Drive.
+  // The parameters for the request are the following:
+  // 1. The request URL
+  // 2. The request method, for example: GET
+  // 3. The request body data.
+  // 4. A bool to indicate whether or not to use end user credential to
+  // authorize the request.
+  // 5. A bool to indicate whether or not to use api key to authorize the
+  // request.
+  // 6. Additional headers objects.
+  // 7. The email address associated with the account
+  SendXhr(
+      url.mojom.Url url,
+      RequestType method,
+      string? request_body,
+      bool use_credentials,
+      bool use_api_key,
+      map<string, string>? headers,
+      string? account_email)  => (XhrResponse response);
 };
 
 // Implemented in Javascript to handle requests from the browser process.
diff --git a/ash/webui/projector_app/projector_message_handler.cc b/ash/webui/projector_app/projector_message_handler.cc
index 9142402..16a484e 100644
--- a/ash/webui/projector_app/projector_message_handler.cc
+++ b/ash/webui/projector_app/projector_message_handler.cc
@@ -12,7 +12,6 @@
 #include "ash/public/cpp/projector/projector_new_screencast_precondition.h"
 #include "ash/webui/projector_app/projector_app_client.h"
 #include "ash/webui/projector_app/projector_screencast.h"
-#include "ash/webui/projector_app/projector_xhr_sender.h"
 #include "base/check.h"
 #include "base/functional/bind.h"
 #include "base/json/values_util.h"
@@ -34,9 +33,6 @@
 constexpr char kExpirationTime[] = "expirationTime";
 constexpr char kError[] = "error";
 constexpr char kOAuthTokenInfo[] = "oauthTokenInfo";
-constexpr char kXhrSuccess[] = "success";
-constexpr char kXhrResponseBody[] = "response";
-constexpr char kXhrError[] = "error";
 
 // Projector Error Strings.
 constexpr char kNoneStr[] = "NONE";
@@ -69,10 +65,7 @@
 
 }  // namespace
 
-ProjectorMessageHandler::ProjectorMessageHandler()
-    : xhr_sender_(std::make_unique<ProjectorXhrSender>(
-          ProjectorAppClient::Get()->GetUrlLoaderFactory())) {}
-
+ProjectorMessageHandler::ProjectorMessageHandler() = default;
 ProjectorMessageHandler::~ProjectorMessageHandler() = default;
 
 base::WeakPtr<ProjectorMessageHandler> ProjectorMessageHandler::GetWeakPtr() {
@@ -94,10 +87,6 @@
                                      base::Unretained(this)));
 
   web_ui()->RegisterMessageCallback(
-      "sendXhr", base::BindRepeating(&ProjectorMessageHandler::SendXhr,
-                                     base::Unretained(this)));
-
-  web_ui()->RegisterMessageCallback(
       "getVideo", base::BindRepeating(&ProjectorMessageHandler::GetVideo,
                                       base::Unretained(this)));
 }
@@ -145,47 +134,6 @@
                      GetWeakPtr(), oauth_token_fetch_callback));
 }
 
-void ProjectorMessageHandler::SendXhr(const base::Value::List& args) {
-  // Two arguments. The first is callback id, and the second is the list
-  // containing function arguments for making the request.
-  DCHECK_EQ(args.size(), 2u);
-  const auto& callback_id = args[0].GetString();
-
-  const auto& func_args = args[1].GetList();
-  // Four function arguments:
-  // 1. The request URL.
-  // 2. The request method, for example: GET
-  // 3. The request body data.
-  // 4. A bool to indicate whether or not to use end user credential to
-  // authorize the request.
-  // 5. A bool to indicate whether or not to use api key to authorize the
-  // request.
-  // 6. Additional headers objects.
-  // 7. The email address associated with the account
-  DCHECK_EQ(func_args.size(), 7u);
-
-  const auto& url = func_args[0].GetString();
-  const auto& method = func_args[1].GetString();
-
-  std::string request_body =
-      func_args[2].is_string() ? func_args[2].GetString() : std::string();
-  bool use_credentials =
-      func_args[3].is_bool() ? func_args[3].GetBool() : false;
-  bool use_api_key = func_args[4].is_bool() ? func_args[4].GetBool() : false;
-  std::string account_email =
-      func_args[6].is_string() ? func_args[6].GetString() : std::string();
-
-  DCHECK(!url.empty());
-  DCHECK(!method.empty());
-  xhr_sender_->Send(
-      GURL(url), method, request_body, use_credentials, use_api_key,
-      base::BindOnce(&ProjectorMessageHandler::OnXhrRequestCompleted,
-                     GetWeakPtr(), callback_id),
-      func_args[5].is_dict() ? func_args[5].GetDict().Clone()
-                             : base::Value::Dict(),
-      account_email);
-}
-
 void ProjectorMessageHandler::OnError(const base::Value::List& args) {
   // TODO(b/195113693): Get the SWA dialog associated with this WebUI and close
   // it.
@@ -213,21 +161,6 @@
   ResolveJavascriptCallback(base::Value(js_callback_id), response);
 }
 
-void ProjectorMessageHandler::OnXhrRequestCompleted(
-    const std::string& js_callback_id,
-    bool success,
-    const std::string& response_body,
-    const std::string& error) {
-  AllowJavascript();
-
-  base::Value::Dict response;
-  response.Set(kXhrSuccess, success);
-  response.Set(kXhrResponseBody, response_body);
-  response.Set(kXhrError, error);
-
-  ResolveJavascriptCallback(base::Value(js_callback_id), response);
-}
-
 void ProjectorMessageHandler::GetVideo(const base::Value::List& args) {
   // Two arguments. The first is callback id, and the second is the list
   // containing the item id and resource key.
diff --git a/ash/webui/projector_app/projector_message_handler.h b/ash/webui/projector_app/projector_message_handler.h
index 162ec7c..5edc8b07 100644
--- a/ash/webui/projector_app/projector_message_handler.h
+++ b/ash/webui/projector_app/projector_message_handler.h
@@ -20,7 +20,6 @@
 
 namespace ash {
 
-class ProjectorXhrSender;
 struct ProjectorScreencastVideo;
 
 // Enum to record the different errors that may occur in the Projector app.
@@ -46,14 +45,6 @@
 
   void set_web_ui_for_test(content::WebUI* web_ui) { set_web_ui(web_ui); }
 
- protected:
-  // Called when the XHR request is completed. Resolves the javascript promise
-  // created by ProjectorBrowserProxy.sendXhr by calling the `js_callback_id`.
-  virtual void OnXhrRequestCompleted(const std::string& js_callback_id,
-                                     bool success,
-                                     const std::string& response_body,
-                                     const std::string& error);
-
  private:
   // Requested by the Projector SWA to list the available accounts (primary and
   // secondary accounts) in the current session. The list of accounts will be
@@ -64,9 +55,6 @@
   // account email provided in the `args`.
   void GetOAuthTokenForAccount(const base::Value::List& args);
 
-  // Requested by the Projector SWA to send XHR request.
-  void SendXhr(const base::Value::List& args);
-
   // Called by the Projector SWA when an error occurred.
   void OnError(const base::Value::List& args);
 
@@ -91,7 +79,6 @@
                       const std::string& error_message);
 
   ProjectorOAuthTokenFetcher oauth_token_fetcher_;
-  std::unique_ptr<ProjectorXhrSender> xhr_sender_;
 
   base::WeakPtrFactory<ProjectorMessageHandler> weak_ptr_factory_{this};
 };
diff --git a/ash/webui/projector_app/projector_xhr_sender.cc b/ash/webui/projector_app/projector_xhr_sender.cc
index ee4497a5..8f061d0f 100644
--- a/ash/webui/projector_app/projector_xhr_sender.cc
+++ b/ash/webui/projector_app/projector_xhr_sender.cc
@@ -8,6 +8,8 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/webui/projector_app/projector_app_client.h"
+#include "ash/webui/projector_app/public/mojom/projector_types.mojom.h"
+#include "base/containers/flat_map.h"
 #include "base/functional/bind.h"
 #include "base/strings/string_util.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
@@ -108,6 +110,19 @@
   return false;
 }
 
+inline std::string RequestTypeToString(projector::mojom::RequestType method) {
+  switch (method) {
+    case projector::mojom::RequestType::kPost:
+      return "POST";
+    case projector::mojom::RequestType::kGet:
+      return "GET";
+    case projector::mojom::RequestType::kPatch:
+      return "PATCH";
+  }
+
+  NOTREACHED_NORETURN();
+}
+
 // The maximum number of retries for the SimpleURLLoader requests. Three times
 // is an arbitrary number to start with.
 const int kMaxRetries = 3;
@@ -119,19 +134,19 @@
     : url_loader_factory_(url_loader_factory) {}
 ProjectorXhrSender::~ProjectorXhrSender() = default;
 
-void ProjectorXhrSender::Send(const GURL& url,
-                              const std::string& method,
-                              const std::string& request_body,
-                              bool use_credentials,
-                              bool use_api_key,
-                              SendRequestCallback callback,
-                              const base::Value::Dict& headers,
-                              const std::string& account_email) {
+void ProjectorXhrSender::Send(
+    const GURL& url,
+    projector::mojom::RequestType method,
+    const absl::optional<std::string>& request_body,
+    bool use_credentials,
+    bool use_api_key,
+    SendRequestCallback callback,
+    const absl::optional<base::flat_map<std::string, std::string>>& headers,
+    const absl::optional<std::string>& account_email) {
   if (!IsUrlAllowlisted(url.spec())) {
     std::move(callback).Run(
-        /*success=*/false,
         /*response_body=*/std::string(),
-        /*error=*/"UNSUPPORTED_URL");
+        /*response_code=*/projector::mojom::XhrResponseCode::kUnsupportedURL);
     LOG(ERROR) << "URL is not supported.";
     return;
   }
@@ -149,34 +164,37 @@
   if (ash::features::IsProjectorViewerUseSecondaryAccountEnabled() &&
       !IsValidEmail(account_email)) {
     std::move(callback).Run(
-        /*success=*/false,
         /*response_body=*/std::string(),
-        /*error=*/"INVALID_ACCOUNT_EMAIL");
+        /*response_code=*/projector::mojom::XhrResponseCode::
+            kInvalidAccountEmail);
     LOG(ERROR) << "User email is invalid";
     return;
   }
 
-  const std::string& email =
-      (ash::features::IsProjectorViewerUseSecondaryAccountEnabled() &&
-       !account_email.empty())
-          ? account_email
-          : ProjectorAppClient::Get()
+  std::string email;
+  if (account_email.has_value() && !account_email->empty() &&
+      ash::features::IsProjectorViewerUseSecondaryAccountEnabled()) {
+    email = *account_email;
+  } else {
+    email = ProjectorAppClient::Get()
                 ->GetIdentityManager()
                 ->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin)
                 .email;
+  }
+
   // Fetch OAuth token for authorizing the request.
   oauth_token_fetcher_.GetAccessTokenFor(
       email, base::BindOnce(&ProjectorXhrSender::OnAccessTokenRequestCompleted,
                             weak_factory_.GetWeakPtr(), request_url, method,
-                            request_body, headers.Clone(), use_credentials,
+                            request_body, headers, use_credentials,
                             std::move(callback)));
 }
 
 void ProjectorXhrSender::OnAccessTokenRequestCompleted(
     const GURL& url,
-    const std::string& method,
-    const std::string& request_body,
-    const base::Value::Dict& headers,
+    projector::mojom::RequestType method,
+    const absl::optional<std::string>& request_body,
+    const absl::optional<base::flat_map<std::string, std::string>>& headers,
     bool use_credentials,
     SendRequestCallback callback,
     const std::string& email,
@@ -184,9 +202,9 @@
     const signin::AccessTokenInfo& info) {
   if (error.state() != GoogleServiceAuthError::State::NONE) {
     std::move(callback).Run(
-        /*success=*/false,
         /*response_body=*/std::string(),
-        /*error=*/"TOKEN_FETCH_FAILURE");
+        /*response_code=*/projector::mojom::XhrResponseCode::
+            kTokenFetchFailure);
     LOG(ERROR) << "Failed to reqeust access token, error:" << error.ToString();
     return;
   }
@@ -195,17 +213,18 @@
               /*allow_cookie=*/use_credentials, std::move(callback));
 }
 
-void ProjectorXhrSender::SendRequest(const GURL& url,
-                                     const std::string& method,
-                                     const std::string& request_body,
-                                     const std::string& token,
-                                     const base::Value::Dict& headers,
-                                     bool allow_cookie,
-                                     SendRequestCallback callback) {
+void ProjectorXhrSender::SendRequest(
+    const GURL& url,
+    projector::mojom::RequestType method,
+    const absl::optional<std::string>& request_body,
+    const std::string& token,
+    const absl::optional<base::flat_map<std::string, std::string>>& headers,
+    bool allow_cookie,
+    SendRequestCallback callback) {
   // Build resource request.
   auto resource_request = std::make_unique<network::ResourceRequest>();
   resource_request->url = url;
-  resource_request->method = method;
+  resource_request->method = RequestTypeToString(method);
   // The OAuth token will be empty if the request is using end user credentials
   // for authorization.
   if (!token.empty()) {
@@ -215,8 +234,10 @@
   resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept,
                                       "application/json");
 
-  for (auto [key, value] : headers) {
-    resource_request->headers.SetHeader(key, value.GetString());
+  if (headers.has_value()) {
+    for (auto [key, value] : *headers) {
+      resource_request->headers.SetHeader(key, value);
+    }
   }
 
   // Send resource request.
@@ -228,8 +249,10 @@
   // for non-2xx response.
   loader->SetAllowHttpErrorResults(true);
 
-  if (!request_body.empty())
-    loader->AttachStringForUpload(request_body, "application/json");
+  if (request_body.has_value() && !request_body->empty()) {
+    loader->AttachStringForUpload(*request_body, "application/json");
+  }
+
   loader->SetRetryOptions(
       kMaxRetries,
       network::SimpleURLLoader::RETRY_ON_5XX |
@@ -260,10 +283,12 @@
   bool is_success =
       response_body && response_code >= 200 && response_code < 300;
   auto response_body_or_empty = response_body ? *response_body : std::string();
+
   std::move(callback).Run(
-      /*success=*/is_success,
       /*response_body=*/response_body_or_empty,
-      /*error=*/is_success ? std::string() : "XHR_FETCH_FAILURE");
+      /*response_code=*/is_success
+          ? projector::mojom::XhrResponseCode::kSuccess
+          : projector::mojom::XhrResponseCode::kXhrFetchFailure);
   if (!is_success) {
     LOG(ERROR) << "Failed to send XHR request, Http error code: "
                << response_code
@@ -273,7 +298,9 @@
   loader_map_.erase(request_id);
 }
 
-bool ProjectorXhrSender::IsValidEmail(const std::string& email) {
+bool ProjectorXhrSender::IsValidEmail(
+    const absl::optional<std::string>& email_check) {
+  const auto email = email_check.value_or(std::string());
   if (email.empty()) {
     return true;
   }
diff --git a/ash/webui/projector_app/projector_xhr_sender.h b/ash/webui/projector_app/projector_xhr_sender.h
index 8247d7e..6d39404 100644
--- a/ash/webui/projector_app/projector_xhr_sender.h
+++ b/ash/webui/projector_app/projector_xhr_sender.h
@@ -9,6 +9,8 @@
 #include <string>
 
 #include "ash/webui/projector_app/projector_oauth_token_fetcher.h"
+#include "ash/webui/projector_app/public/mojom/projector_types.mojom.h"
+#include "base/containers/flat_map.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/values.h"
@@ -29,7 +31,6 @@
 namespace ash {
 
 constexpr char kDriveV3BaseUrl[] = "https://www.googleapis.com/drive/v3/files/";
-constexpr char kRequestMethodPatch[] = "PATCH";
 
 /**
  * Projector XHR sender. Used by Projector App to send XHR requests.
@@ -37,12 +38,11 @@
 class ProjectorXhrSender {
  public:
   // Callback triggered when a XHR request is completed. `response_body`
-  // contains the response text if success, empty otherwise. `error` contains
-  // error message if not success, empty otherwise.
+  // contains the response text if success, empty otherwise. `response_code`
+  // contains the response code.
   using SendRequestCallback =
-      base::OnceCallback<void(bool success,
-                              const std::string& response_body,
-                              const std::string& error)>;
+      base::OnceCallback<void(const std::string& response_body,
+                              projector::mojom::XhrResponseCode error)>;
 
   explicit ProjectorXhrSender(
       network::mojom::URLLoaderFactory* url_loader_factory);
@@ -62,34 +62,38 @@
   // get_video_info response to add streaming auth token in cookie. There is
   // no use case for sending requests with credentials only (without oauth
   // token).
-  virtual void Send(const GURL& url,
-                    const std::string& method,
-                    const std::string& request_body,
-                    bool use_credentials,
-                    bool use_api_key,
-                    SendRequestCallback callback,
-                    const base::Value::Dict& headers = base::Value::Dict(),
-                    const std::string& account_email = std::string());
+  virtual void Send(
+      const GURL& url,
+      projector::mojom::RequestType method,
+      const absl::optional<std::string>& request_body,
+      bool use_credentials,
+      bool use_api_key,
+      SendRequestCallback callback,
+      const absl::optional<base::flat_map<std::string, std::string>>& headers =
+          absl::nullopt,
+      const absl::optional<std::string>& account_email = absl::nullopt);
 
  private:
   // Triggered when an OAuth token fetch completed.
-  void OnAccessTokenRequestCompleted(const GURL& url,
-                                     const std::string& method,
-                                     const std::string& request_body,
-                                     const base::Value::Dict& headers,
-                                     bool use_credentials,
-                                     SendRequestCallback callback,
-                                     const std::string& email,
-                                     GoogleServiceAuthError error,
-                                     const signin::AccessTokenInfo& info);
+  void OnAccessTokenRequestCompleted(
+      const GURL& url,
+      projector::mojom::RequestType method,
+      const absl::optional<std::string>& request_body,
+      const absl::optional<base::flat_map<std::string, std::string>>& headers,
+      bool use_credentials,
+      SendRequestCallback callback,
+      const std::string& email,
+      GoogleServiceAuthError error,
+      const signin::AccessTokenInfo& info);
 
-  void SendRequest(const GURL& url,
-                   const std::string& method,
-                   const std::string& request_body,
-                   const std::string& token,
-                   const base::Value::Dict& headers,
-                   bool allow_cookie,
-                   SendRequestCallback callback);
+  void SendRequest(
+      const GURL& url,
+      projector::mojom::RequestType method,
+      const absl::optional<std::string>& request_body,
+      const std::string& token,
+      const absl::optional<base::flat_map<std::string, std::string>>& headers,
+      bool allow_cookie,
+      SendRequestCallback callback);
 
   // Triggered when an XHR request completed.
   void OnSimpleURLLoaderComplete(int request_id,
@@ -97,7 +101,7 @@
                                  std::unique_ptr<std::string> response_body);
 
   // Validate the email address provided with xhr request
-  bool IsValidEmail(const std::string& email);
+  bool IsValidEmail(const absl::optional<std::string>& email_check);
 
   ProjectorOAuthTokenFetcher oauth_token_fetcher_;
   raw_ptr<network::mojom::URLLoaderFactory, ExperimentalAsh>
diff --git a/ash/webui/projector_app/public/mojom/projector_types.mojom b/ash/webui/projector_app/public/mojom/projector_types.mojom
index 5379625..42b70ff 100644
--- a/ash/webui/projector_app/public/mojom/projector_types.mojom
+++ b/ash/webui/projector_app/public/mojom/projector_types.mojom
@@ -64,3 +64,25 @@
     kProjectorViewerOnboardingShowCount = 2,
     kProjectorGalleryOnboardingShowCount = 3,
 };
+
+// The error code for the XhrResponse.
+enum XhrResponseCode {
+    kSuccess = 0,
+    kTokenFetchFailure = 1,
+    kXhrFetchFailure = 2,
+    kUnsupportedURL = 3,
+    kInvalidAccountEmail = 4,
+};
+
+// XhrResponse sent from the browser process the JS WebUI.
+struct XhrResponse {
+    string response;
+    XhrResponseCode response_code;
+};
+
+// The HTTP request type that comes from the JS WebUI.
+enum RequestType {
+    kPost = 0,
+    kGet = 1,
+    kPatch = 2,
+};
diff --git a/ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js b/ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js
index b48578a8..acc6dd7 100644
--- a/ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js
+++ b/ash/webui/projector_app/resources/app/trusted/projector_browser_proxy.js
@@ -34,23 +34,6 @@
   onError(msg) {}
 
   /**
-   * Send XHR request.
-   * @param {string} url the request URL.
-   * @param {string} method the request method.
-   * @param {string=} requestBody the request body data.
-   * @param {boolean=} useCredentials authorize the request with end user
-   *     credentials. Used for getting streaming URL.
-   * @param {boolean=} useApiKey authorize the request with API key. Used for
-   *     translaton requests.
-   * @param {Object=} headers additional headers.
-   * @param {string=} accountEmail the email associated with user account.
-   * @return {!Promise<!projectorApp.XhrResponse>}
-   */
-  sendXhr(
-      url, method, requestBody, useCredentials, useApiKey, headers,
-      accountEmail) {}
-
-  /**
    * Gets information about the specified video from DriveFS.
    * @param {string} videoFileId The Drive item id of the video file.
    * @param {string|undefined} resourceKey The Drive item resource key.
@@ -94,21 +77,6 @@
   }
 
   /** @override */
-  sendXhr(
-      url, method, requestBody, useCredentials, useApiKey, headers,
-      accountEmail) {
-    return sendWithPromise('sendXhr', [
-      url,
-      method,
-      requestBody,
-      useCredentials,
-      useApiKey,
-      headers,
-      accountEmail,
-    ]);
-  }
-
-  /** @override */
   getVideo(videoFileId, resourceKey) {
     return sendWithPromise('getVideo', [videoFileId, resourceKey]);
   }
diff --git a/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js b/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js
index 6584785..de83fe2 100644
--- a/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js
+++ b/ash/webui/projector_app/resources/app/trusted/trusted_app_comm_factory.js
@@ -57,17 +57,6 @@
     this.registerMethod('onError', (msg) => {
       this.browserProxy_.onError(msg);
     });
-    this.registerMethod('sendXhr', (values) => {
-      if (!values || values.length != 7) {
-        return {
-          success: false,
-          error: 'INVALID_ARGUMENTS',
-        };
-      }
-      return this.browserProxy_.sendXhr(
-          values[0], values[1], values[2], values[3], values[4], values[5],
-          values[6]);
-    });
     this.registerMethod('getVideo', (args) => {
       if (!args || args.length != 2) {
         return Promise.reject('Incorrect args for getVideo');
diff --git a/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js b/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js
index 0c6ffce..ce57680 100644
--- a/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js
+++ b/ash/webui/projector_app/resources/app/untrusted/untrusted_app_comm_factory.js
@@ -132,16 +132,9 @@
   sendXhr(
       url, method, requestBody, useCredentials, useApiKey, headers,
       accountEmail) {
-    return AppUntrustedCommFactory.getPostMessageAPIClient().callApiFn(
-        'sendXhr', [
-          url,
-          method,
-          requestBody ? requestBody : '',
-          !!useCredentials,
-          !!useApiKey,
-          headers,
-          accountEmail,
-        ]);
+    return browserProxy.sendXhr(
+        url, method, requestBody, !!useCredentials, !!useApiKey, headers,
+        accountEmail);
   },
 
   /**
diff --git a/ash/webui/projector_app/resources/app/untrusted/untrusted_projector_browser_proxy.js b/ash/webui/projector_app/resources/app/untrusted/untrusted_projector_browser_proxy.js
index 9f621a63..6455885 100644
--- a/ash/webui/projector_app/resources/app/untrusted/untrusted_projector_browser_proxy.js
+++ b/ash/webui/projector_app/resources/app/untrusted/untrusted_projector_browser_proxy.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {UntrustedProjectorPageCallbackRouter, UntrustedProjectorPageHandlerFactory, UntrustedProjectorPageHandlerRemote, UntrustedProjectorPageRemote} from './ash/webui/projector_app/mojom/untrusted_projector.mojom-webui.js';
-import {PrefsThatProjectorCanAskFor} from './ash/webui/projector_app/public/mojom/projector_types.mojom-webui.js';
+import {PrefsThatProjectorCanAskFor, RequestType, XhrResponseCode} from './ash/webui/projector_app/public/mojom/projector_types.mojom-webui.js';
 
 const booleanUserPrefs = new Map([
   [
@@ -27,6 +27,44 @@
   ],
 ]);
 
+const requestMaps = new Map([
+  [
+    'POST',
+    RequestType.kPost,
+  ],
+  [
+    'GET',
+    RequestType.kGet,
+  ],
+  [
+    'PATCH',
+    RequestType.kPatch,
+  ],
+]);
+
+const errorCodeMap = new Map([
+  [
+    XhrResponseCode.kSuccess,
+    '',
+  ],
+  [
+    XhrResponseCode.kTokenFetchFailure,
+    'TOKEN_FETCH_FAILURE',
+  ],
+  [
+    XhrResponseCode.kXhrFetchFailure,
+    'XHR_FETCH_FAILURE',
+  ],
+  [
+    XhrResponseCode.kUnsupportedURL,
+    'UNSUPPORTED_URL',
+  ],
+  [
+    XhrResponseCode.kInvalidAccountEmail,
+    'INVALID_ACCOUNT_EMAIL',
+  ],
+]);
+
 export class UntrustedProjectorBrowserProxyImpl {
   constructor() {
     this.pageHandlerFactory = UntrustedProjectorPageHandlerFactory.getRemote();
@@ -128,6 +166,27 @@
     });
     return success;
   }
+
+  async sendXhr(
+      url, method, requestBody, useCredentials, useApiKey, headers,
+      accountEmail) {
+    if (!requestMaps.has(method)) {
+      throw new Error(`Invalid request method. ${method}`);
+    }
+
+    const requestMethod = requestMaps.get(method);
+    const {response} = await this.pageHandlerRemote.sendXhr(
+        {url}, requestMethod, requestBody, useCredentials, useApiKey, headers,
+        accountEmail);
+
+    // TODO(b/237337607): Remove the success field and just pass response
+    // directly.
+    return {
+      success: response.responseCode === XhrResponseCode.kSuccess,
+      response: response.response,
+      error: errorCodeMap.get(response.responseCode),
+    };
+  }
 }
 
 /**
diff --git a/ash/webui/projector_app/test/mock_xhr_sender.cc b/ash/webui/projector_app/test/mock_xhr_sender.cc
index fbe5883..ca32702c 100644
--- a/ash/webui/projector_app/test/mock_xhr_sender.cc
+++ b/ash/webui/projector_app/test/mock_xhr_sender.cc
@@ -9,22 +9,22 @@
 
 namespace ash {
 MockXhrSender::MockXhrSender(
-    base::OnceCallback<
-        void(const GURL&, const std::string&, const std::string&)> quit_closure,
+    OnSendCallback quit_closure,
     network::mojom::URLLoaderFactory* url_loader_factory)
     : ProjectorXhrSender(url_loader_factory),
       quit_closure_(std::move(quit_closure)) {}
 
 MockXhrSender::~MockXhrSender() = default;
 
-void MockXhrSender::Send(const GURL& url,
-                         const std::string& method,
-                         const std::string& request_body,
-                         bool use_credentials,
-                         bool use_api_key,
-                         SendRequestCallback callback,
-                         const base::Value::Dict& headers,
-                         const std::string& account_email) {
+void MockXhrSender::Send(
+    const GURL& url,
+    projector::mojom::RequestType method,
+    const absl::optional<std::string>& request_body,
+    bool use_credentials,
+    bool use_api_key,
+    SendRequestCallback callback,
+    const absl::optional<base::flat_map<std::string, std::string>>& headers,
+    const absl::optional<std::string>& account_email) {
   std::move(quit_closure_).Run(url, method, request_body);
 }
 }  // namespace ash
diff --git a/ash/webui/projector_app/test/mock_xhr_sender.h b/ash/webui/projector_app/test/mock_xhr_sender.h
index ff0dfea1..a3f0138b 100644
--- a/ash/webui/projector_app/test/mock_xhr_sender.h
+++ b/ash/webui/projector_app/test/mock_xhr_sender.h
@@ -22,8 +22,10 @@
 // indexable text request is sent correctly.
 class MockXhrSender : public ProjectorXhrSender {
  public:
-  using OnSendCallback = base::OnceCallback<
-      void(const GURL&, const std::string&, const std::string&)>;
+  using OnSendCallback =
+      base::OnceCallback<void(const GURL&,
+                              projector::mojom::RequestType,
+                              const absl::optional<std::string>&)>;
 
   MockXhrSender(OnSendCallback quit_closure,
                 network::mojom::URLLoaderFactory* url_loader_factory);
@@ -32,14 +34,15 @@
   ~MockXhrSender() override;
 
   // ProjectorXhrSender:
-  void Send(const GURL& url,
-            const std::string& method,
-            const std::string& request_body,
-            bool use_credentials,
-            bool use_api_key,
-            SendRequestCallback callback,
-            const base::Value::Dict& headers,
-            const std::string& account_email) override;
+  void Send(
+      const GURL& url,
+      projector::mojom::RequestType method,
+      const absl::optional<std::string>& request_body,
+      bool use_credentials,
+      bool use_api_key,
+      SendRequestCallback callback,
+      const absl::optional<base::flat_map<std::string, std::string>>& headers,
+      const absl::optional<std::string>& account_email) override;
 
  private:
   // Quits the current run loop. Used to verify the MockXhrSender::Send getting
diff --git a/ash/webui/projector_app/test/projector_message_handler_unittest.cc b/ash/webui/projector_app/test/projector_message_handler_unittest.cc
index bf7bb223..247c16b6d 100644
--- a/ash/webui/projector_app/test/projector_message_handler_unittest.cc
+++ b/ash/webui/projector_app/test/projector_message_handler_unittest.cc
@@ -27,54 +27,15 @@
 const char kVideoFileId[] = "video_file_id";
 const char kResourceKey[] = "resource_key";
 
-const char kTestXhrUrl[] = "https://www.googleapis.com/drive/v3/files/fileID";
-const char kTestXhrUnsupportedUrl[] = "https://www.example.com";
-const char kTestXhrMethod[] = "POST";
-const char kTestXhrRequestBody[] = "{}";
-const char kTestXhrHeaderKey[] = "X-Goog-Drive-Resource-Keys";
-const char kTestXhrHeaderValue[] = "resource-key";
-
-const char kXhrResponseSuccessPath[] = "success";
-const char kXhrResponseErrorPath[] = "error";
-const char kXhrResponseStringPath[] = "response";
-
 const char kWebUIResponse[] = "cr.webUIResponse";
 const char kGetAccountsCallback[] = "getAccountsCallback";
 const char kGetOAuthTokenCallback[] = "getOAuthTokenCallback";
-const char kSendXhrCallback[] = "sendXhrCallback";
 const char kGetVideoCallback[] = "getVideoCallback";
 
 }  // namespace
 
 namespace ash {
 
-class ProjectorMessageHandlerForTest : public ProjectorMessageHandler {
- public:
-  ProjectorMessageHandlerForTest() = default;
-  ProjectorMessageHandlerForTest(const ProjectorMessageHandlerForTest&) =
-      delete;
-  ProjectorMessageHandlerForTest& operator=(
-      const ProjectorMessageHandlerForTest&) = delete;
-  ~ProjectorMessageHandlerForTest() override = default;
-
-  // ProjectorMessageHandler:
-  void OnXhrRequestCompleted(const std::string& js_callback_id,
-                             bool success,
-                             const std::string& response_body,
-                             const std::string& error) override {
-    ProjectorMessageHandler::OnXhrRequestCompleted(js_callback_id, success,
-                                                   response_body, error);
-    std::move(quit_closure_).Run();
-  }
-
-  void SetXhrRequestRunLoopQuitClosure(base::RepeatingClosure closure) {
-    quit_closure_ = base::BindOnce(closure);
-  }
-
- private:
-  base::OnceClosure quit_closure_;
-};
-
 class ProjectorMessageHandlerUnitTest : public testing::Test {
  public:
   ProjectorMessageHandlerUnitTest() = default;
@@ -86,7 +47,7 @@
 
   // testing::Test
   void SetUp() override {
-    message_handler_ = std::make_unique<ProjectorMessageHandlerForTest>();
+    message_handler_ = std::make_unique<ProjectorMessageHandler>();
     message_handler_->set_web_ui_for_test(&web_ui());
     message_handler_->RegisterMessages();
   }
@@ -97,9 +58,7 @@
     return *(web_ui().call_data()[sequence_number]);
   }
 
-  ProjectorMessageHandlerForTest* message_handler() {
-    return message_handler_.get();
-  }
+  ProjectorMessageHandler* message_handler() { return message_handler_.get(); }
   content::TestWebUI& web_ui() { return web_ui_; }
   MockProjectorController& controller() { return mock_controller_; }
   MockAppClient& mock_app_client() { return mock_app_client_; }
@@ -107,7 +66,7 @@
  private:
   base::test::SingleThreadTaskEnvironment task_environment_;
 
-  std::unique_ptr<ProjectorMessageHandlerForTest> message_handler_;
+  std::unique_ptr<ProjectorMessageHandler> message_handler_;
   MockProjectorController mock_controller_;
   MockAppClient mock_app_client_;
   content::TestWebUI web_ui_;
@@ -161,212 +120,6 @@
   EXPECT_EQ(call_data.arg1()->GetString(), kGetOAuthTokenCallback);
 }
 
-TEST_F(ProjectorMessageHandlerUnitTest, SendXhr) {
-  const std::string& test_response_body = "{}";
-
-  base::Value::List list_args;
-  list_args.Append(kSendXhrCallback);
-  base::Value::List args;
-  args.Append(kTestXhrUrl);
-  args.Append(kTestXhrMethod);
-  args.Append(kTestXhrRequestBody);
-  // Add useCredentials.
-  args.Append(true);
-  // Add useApiKey.
-  args.Append(false);
-  // Add additional headers.
-  base::Value::Dict dict;
-  dict.Set(kTestXhrHeaderKey, kTestXhrHeaderValue);
-  args.Append(std::move(dict));
-  args.Append(base::Value());
-  list_args.Append(std::move(args));
-
-  mock_app_client().test_url_loader_factory().AddResponse(kTestXhrUrl,
-                                                          test_response_body);
-
-  base::RunLoop run_loop;
-  message_handler()->SetXhrRequestRunLoopQuitClosure(run_loop.QuitClosure());
-  web_ui().HandleReceivedMessage("sendXhr", list_args);
-  mock_app_client().WaitForAccessRequest(kTestUserEmail);
-
-  run_loop.Run();
-
-  EXPECT_EQ(web_ui().call_data().size(), 1u);
-
-  const content::TestWebUI::CallData& call_data = FetchCallData(0);
-  EXPECT_EQ(call_data.function_name(), kWebUIResponse);
-  EXPECT_EQ(call_data.arg1()->GetString(), kSendXhrCallback);
-
-  // Whether the callback was rejected or not.
-  EXPECT_TRUE(call_data.arg2()->GetBool());
-  ASSERT_TRUE(call_data.arg3()->is_dict());
-
-  // Verify that it is success.
-  const base::Value::Dict& arg3_dict = call_data.arg3()->GetDict();
-  EXPECT_TRUE(*arg3_dict.FindBool(kXhrResponseSuccessPath));
-
-  // Verify the response.
-  const std::string* response = arg3_dict.FindString(kXhrResponseStringPath);
-  EXPECT_EQ(test_response_body, *response);
-
-  // Verify error is empty.
-  const std::string* error = arg3_dict.FindString(kXhrResponseErrorPath);
-  EXPECT_TRUE(error->empty());
-}
-
-TEST_F(ProjectorMessageHandlerUnitTest, SendXhrWithEmail) {
-  const std::string& test_response_body = "{}";
-
-  base::Value::List list_args;
-  list_args.Append(kSendXhrCallback);
-  base::Value::List args;
-  args.Append(kTestXhrUrl);
-  args.Append(kTestXhrMethod);
-  args.Append(kTestXhrRequestBody);
-  // Add useCredentials.
-  args.Append(true);
-  // Add useApiKey.
-  args.Append(false);
-  // Add additional headers.
-  base::Value::Dict dict;
-  dict.Set(kTestXhrHeaderKey, kTestXhrHeaderValue);
-  args.Append(std::move(dict));
-  args.Append(kTestUserEmail);
-  list_args.Append(std::move(args));
-
-  mock_app_client().test_url_loader_factory().AddResponse(kTestXhrUrl,
-                                                          test_response_body);
-
-  base::RunLoop run_loop;
-  message_handler()->SetXhrRequestRunLoopQuitClosure(run_loop.QuitClosure());
-  web_ui().HandleReceivedMessage("sendXhr", list_args);
-  mock_app_client().WaitForAccessRequest(kTestUserEmail);
-  run_loop.Run();
-
-  EXPECT_EQ(web_ui().call_data().size(), 1u);
-
-  const content::TestWebUI::CallData& call_data = FetchCallData(0);
-  EXPECT_EQ(call_data.function_name(), kWebUIResponse);
-  EXPECT_EQ(call_data.arg1()->GetString(), kSendXhrCallback);
-
-  // Whether the callback was rejected or not.
-  EXPECT_TRUE(call_data.arg2()->GetBool());
-  ASSERT_TRUE(call_data.arg3()->is_dict());
-
-  // Verify that it is success.
-  const base::Value::Dict& arg3_dict = call_data.arg3()->GetDict();
-  EXPECT_TRUE(*arg3_dict.FindBool(kXhrResponseSuccessPath));
-
-  // Verify the response.
-  const std::string* response = arg3_dict.FindString(kXhrResponseStringPath);
-  EXPECT_EQ(test_response_body, *response);
-
-  // Verify error is empty.
-  const std::string* error = arg3_dict.FindString(kXhrResponseErrorPath);
-  EXPECT_TRUE(error->empty());
-}
-
-TEST_F(ProjectorMessageHandlerUnitTest, SendXhrFailed) {
-  const std::string& test_error_response_body = "error";
-
-  base::Value::List list_args;
-  list_args.Append(kSendXhrCallback);
-  base::Value::List args;
-  args.Append(kTestXhrUrl);
-  args.Append(kTestXhrMethod);
-  args.Append(kTestXhrRequestBody);
-  // Add useCredentials.
-  args.Append(true);
-  // Add useApiKey.
-  args.Append(false);
-  // Add additional headers.
-  base::Value::Dict dict;
-  dict.Set(kTestXhrHeaderKey, kTestXhrHeaderValue);
-  args.Append(std::move(dict));
-  args.Append(kTestUserEmail);
-  list_args.Append(std::move(args));
-
-  mock_app_client().test_url_loader_factory().AddResponse(
-      /*url=*/kTestXhrUrl,
-      /*content=*/test_error_response_body,
-      /*status=*/net::HttpStatusCode::HTTP_NOT_FOUND);
-
-  base::RunLoop run_loop;
-  message_handler()->SetXhrRequestRunLoopQuitClosure(run_loop.QuitClosure());
-  web_ui().HandleReceivedMessage("sendXhr", list_args);
-  mock_app_client().WaitForAccessRequest(kTestUserEmail);
-
-  run_loop.Run();
-
-  EXPECT_EQ(web_ui().call_data().size(), 1u);
-
-  const content::TestWebUI::CallData& call_data = FetchCallData(0);
-  EXPECT_EQ(call_data.function_name(), kWebUIResponse);
-  EXPECT_EQ(call_data.arg1()->GetString(), kSendXhrCallback);
-
-  // Whether the callback was rejected.
-  EXPECT_TRUE(call_data.arg2()->GetBool());
-  ASSERT_TRUE(call_data.arg3()->is_dict());
-
-  // Verify that request failed.
-  const base::Value::Dict& arg3_dict = call_data.arg3()->GetDict();
-  EXPECT_FALSE(*arg3_dict.FindBool(kXhrResponseSuccessPath));
-
-  // Verify the response.
-  const std::string* response = arg3_dict.FindString(kXhrResponseStringPath);
-  EXPECT_EQ(test_error_response_body, *response);
-
-  // Verify error is empty.
-  const std::string* error = arg3_dict.FindString(kXhrResponseErrorPath);
-  EXPECT_EQ("XHR_FETCH_FAILURE", *error);
-}
-
-TEST_F(ProjectorMessageHandlerUnitTest, SendXhrWithUnSupportedUrl) {
-  base::Value::List list_args;
-  list_args.Append(kSendXhrCallback);
-  base::Value::List args;
-  args.Append(kTestXhrUnsupportedUrl);
-  args.Append(kTestXhrMethod);
-  args.Append(kTestXhrRequestBody);
-  // Add useCredentials.
-  args.Append(true);
-  // Add useApiKey.
-  args.Append(false);
-  // Add additional headers.
-  base::Value::Dict dict;
-  dict.Set(kTestXhrHeaderKey, kTestXhrHeaderValue);
-  args.Append(std::move(dict));
-  args.Append(kTestUserEmail);
-  list_args.Append(std::move(args));
-
-  base::RunLoop run_loop;
-  message_handler()->SetXhrRequestRunLoopQuitClosure(run_loop.QuitClosure());
-  web_ui().HandleReceivedMessage("sendXhr", list_args);
-  run_loop.Run();
-
-  EXPECT_EQ(web_ui().call_data().size(), 1u);
-
-  const content::TestWebUI::CallData& call_data = FetchCallData(0);
-  EXPECT_EQ(call_data.function_name(), kWebUIResponse);
-  EXPECT_EQ(call_data.arg1()->GetString(), kSendXhrCallback);
-
-  // Whether the callback was rejected or not.
-  EXPECT_TRUE(call_data.arg2()->GetBool());
-  ASSERT_TRUE(call_data.arg3()->is_dict());
-
-  // Verify that it is success.
-  const base::Value::Dict& arg3_dict = call_data.arg3()->GetDict();
-  EXPECT_TRUE(arg3_dict.FindBool(kXhrResponseSuccessPath));
-
-  // Verify the response.
-  const std::string* response = arg3_dict.FindString(kXhrResponseStringPath);
-  EXPECT_TRUE(response->empty());
-
-  // Verify error is UNSUPPORTED_URL.
-  const std::string* error = arg3_dict.FindString(kXhrResponseErrorPath);
-  EXPECT_EQ("UNSUPPORTED_URL", *error);
-}
-
 TEST_F(ProjectorMessageHandlerUnitTest, GetVideo) {
   ProjectorScreencastVideo expected_video;
   expected_video.file_id = kVideoFileId;
diff --git a/ash/webui/projector_app/test/projector_xhr_sender_unittest.cc b/ash/webui/projector_app/test/projector_xhr_sender_unittest.cc
index c217bdbf..21dbe604 100644
--- a/ash/webui/projector_app/test/projector_xhr_sender_unittest.cc
+++ b/ash/webui/projector_app/test/projector_xhr_sender_unittest.cc
@@ -64,20 +64,21 @@
   base::RunLoop run_loop;
 
   const std::string& test_response_body = "{}";
-  sender()->Send(
-      GURL(kTestDriveRequestUrl), "GET", /*request_body=*/"",
-      /*use_credentials=*/false,
-      /*use_api_key=*/false,
-      base::BindOnce(
-          [](const std::string& expected_response_body,
-             base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_TRUE(success);
-            EXPECT_EQ(expected_response_body, response_body);
-            EXPECT_EQ("", error);
-            quit_closure.Run();
-          },
-          test_response_body, run_loop.QuitClosure()));
+  sender()->Send(GURL(kTestDriveRequestUrl),
+                 projector::mojom::RequestType::kGet, /*request_body=*/"",
+                 /*use_credentials=*/false,
+                 /*use_api_key=*/false,
+                 base::BindOnce(
+                     [](const std::string& expected_response_body,
+                        base::RepeatingClosure quit_closure,
+                        const std::string& response_body,
+                        projector::mojom::XhrResponseCode response_code) {
+                       EXPECT_EQ(expected_response_body, response_body);
+                       EXPECT_EQ(response_code,
+                                 projector::mojom::XhrResponseCode::kSuccess);
+                       quit_closure.Run();
+                     },
+                     test_response_body, run_loop.QuitClosure()));
 
   mock_app_client().test_url_loader_factory().AddResponse(kTestDriveRequestUrl,
                                                           test_response_body);
@@ -91,38 +92,40 @@
 TEST_F(ProjectorXhrSenderTest, TwoRequests) {
   base::RunLoop run_loop;
   const std::string& test_response_body = "{}";
-  sender()->Send(
-      GURL(kTestDriveRequestUrl), "GET", /*request_body=*/"",
-      /*use_credentials=*/false,
-      /*use_api_key=*/false,
-      base::BindOnce(
-          [](const std::string& expected_response_body,
-             base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_TRUE(success);
-            EXPECT_EQ(expected_response_body, response_body);
-            EXPECT_EQ("", error);
-            quit_closure.Run();
-          },
-          test_response_body, run_loop.QuitClosure()));
+  sender()->Send(GURL(kTestDriveRequestUrl),
+                 projector::mojom::RequestType::kGet, /*request_body=*/"",
+                 /*use_credentials=*/false,
+                 /*use_api_key=*/false,
+                 base::BindOnce(
+                     [](const std::string& expected_response_body,
+                        base::RepeatingClosure quit_closure,
+                        const std::string& response_body,
+                        projector::mojom::XhrResponseCode response_code) {
+                       EXPECT_EQ(expected_response_body, response_body);
+                       EXPECT_EQ(response_code,
+                                 projector::mojom::XhrResponseCode::kSuccess);
+                       quit_closure.Run();
+                     },
+                     test_response_body, run_loop.QuitClosure()));
 
   base::RunLoop run_loop2;
   const std::string& test_response_body2 = "{data: {}}";
   auto translation_url = GURL(kTestTranslationRequestUrl);
-  sender()->Send(
-      translation_url, "GET", /*request_body=*/"",
-      /*use_credentials=*/false,
-      /*use_api_key=*/false,
-      base::BindOnce(
-          [](const std::string& expected_response_body,
-             base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_TRUE(success);
-            EXPECT_EQ(expected_response_body, response_body);
-            EXPECT_EQ("", error);
-            quit_closure.Run();
-          },
-          test_response_body2, run_loop2.QuitClosure()));
+  sender()->Send(translation_url, projector::mojom::RequestType::kGet,
+                 /*request_body=*/"",
+                 /*use_credentials=*/false,
+                 /*use_api_key=*/false,
+                 base::BindOnce(
+                     [](const std::string& expected_response_body,
+                        base::RepeatingClosure quit_closure,
+                        const std::string& response_body,
+                        projector::mojom::XhrResponseCode response_code) {
+                       EXPECT_EQ(expected_response_body, response_body);
+                       EXPECT_EQ(response_code,
+                                 projector::mojom::XhrResponseCode::kSuccess);
+                       quit_closure.Run();
+                     },
+                     test_response_body2, run_loop2.QuitClosure()));
 
   mock_app_client().test_url_loader_factory().AddResponse(kTestDriveRequestUrl,
                                                           test_response_body);
@@ -141,20 +144,21 @@
   base::RunLoop run_loop;
 
   const std::string& test_response_body = "{}";
-  sender()->Send(
-      GURL(kTestDriveRequestUrl), "GET", /*request_body=*/"",
-      /*use_credentials=*/true,
-      /*use_api_key=*/false,
-      base::BindOnce(
-          [](const std::string& expected_response_body,
-             base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_TRUE(success);
-            EXPECT_EQ(expected_response_body, response_body);
-            EXPECT_EQ("", error);
-            quit_closure.Run();
-          },
-          test_response_body, run_loop.QuitClosure()));
+  sender()->Send(GURL(kTestDriveRequestUrl),
+                 projector::mojom::RequestType::kGet, /*request_body=*/"",
+                 /*use_credentials=*/true,
+                 /*use_api_key=*/false,
+                 base::BindOnce(
+                     [](const std::string& expected_response_body,
+                        base::RepeatingClosure quit_closure,
+                        const std::string& response_body,
+                        projector::mojom::XhrResponseCode response_code) {
+                       EXPECT_EQ(expected_response_body, response_body);
+                       EXPECT_EQ(response_code,
+                                 projector::mojom::XhrResponseCode::kSuccess);
+                       quit_closure.Run();
+                     },
+                     test_response_body, run_loop.QuitClosure()));
 
   mock_app_client().test_url_loader_factory().AddResponse(kTestDriveRequestUrl,
                                                           test_response_body);
@@ -171,19 +175,20 @@
 
   auto url = GURL(kTestTranslationRequestUrl);
   const std::string& test_response_body = "{}";
-  sender()->Send(
-      url, "GET", /*request_body=*/"", /*use_credentials=*/false,
-      /*use_api_key=*/true,
-      base::BindOnce(
-          [](const std::string& expected_response_body,
-             base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_TRUE(success);
-            EXPECT_EQ(expected_response_body, response_body);
-            EXPECT_EQ("", error);
-            quit_closure.Run();
-          },
-          test_response_body, run_loop.QuitClosure()));
+  sender()->Send(url, projector::mojom::RequestType::kGet, /*request_body=*/"",
+                 /*use_credentials=*/false,
+                 /*use_api_key=*/true,
+                 base::BindOnce(
+                     [](const std::string& expected_response_body,
+                        base::RepeatingClosure quit_closure,
+                        const std::string& response_body,
+                        projector::mojom::XhrResponseCode response_code) {
+                       EXPECT_EQ(expected_response_body, response_body);
+                       EXPECT_EQ(response_code,
+                                 projector::mojom::XhrResponseCode::kSuccess);
+                       quit_closure.Run();
+                     },
+                     test_response_body, run_loop.QuitClosure()));
 
   // Verify that http request is sent with API key.
   mock_app_client().test_url_loader_factory().AddResponse(
@@ -196,14 +201,16 @@
   base::RunLoop run_loop;
 
   sender()->Send(
-      GURL(kTestDriveRequestUrl), /*method=*/"GET", /*request_body=*/"",
+      GURL(kTestDriveRequestUrl),
+      /*method=*/projector::mojom::RequestType::kGet, /*request_body=*/"",
       /*use_credentials=*/false, /*use_api_key=*/false,
       base::BindOnce(
-          [](base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_FALSE(success);
+          [](base::RepeatingClosure quit_closure,
+             const std::string& response_body,
+             projector::mojom::XhrResponseCode response_code) {
             EXPECT_EQ("", response_body);
-            EXPECT_EQ("XHR_FETCH_FAILURE", error);
+            EXPECT_EQ(response_code,
+                      projector::mojom::XhrResponseCode::kXhrFetchFailure);
             quit_closure.Run();
           },
           run_loop.QuitClosure()));
@@ -222,14 +229,16 @@
   base::RunLoop run_loop;
 
   sender()->Send(
-      GURL("https://example.com"), /*method=*/"GET", /*request_body=*/"",
+      GURL("https://example.com"),
+      /*method=*/projector::mojom::RequestType::kGet, /*request_body=*/"",
       /*use_credentials=*/false, /*use_api_key=*/false,
       base::BindOnce(
-          [](base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_FALSE(success);
+          [](base::RepeatingClosure quit_closure,
+             const std::string& response_body,
+             projector::mojom::XhrResponseCode response_code) {
             EXPECT_EQ("", response_body);
-            EXPECT_EQ("UNSUPPORTED_URL", error);
+            EXPECT_EQ(response_code,
+                      projector::mojom::XhrResponseCode::kUnsupportedURL);
             quit_closure.Run();
           },
           run_loop.QuitClosure()));
@@ -244,21 +253,22 @@
   base::RunLoop run_loop;
 
   const std::string& test_response_body = "{}";
-  sender()->Send(
-      GURL(kTestDriveRequestUrl), "GET", /*request_body=*/"",
-      /*use_credentials=*/false,
-      /*use_api_key=*/false,
-      base::BindOnce(
-          [](const std::string& expected_response_body,
-             base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_TRUE(success);
-            EXPECT_EQ(expected_response_body, response_body);
-            EXPECT_EQ("", error);
-            quit_closure.Run();
-          },
-          test_response_body, run_loop.QuitClosure()),
-      base::Value::Dict(), kTestUserEmail);
+  sender()->Send(GURL(kTestDriveRequestUrl),
+                 projector::mojom::RequestType::kGet, /*request_body=*/"",
+                 /*use_credentials=*/false,
+                 /*use_api_key=*/false,
+                 base::BindOnce(
+                     [](const std::string& expected_response_body,
+                        base::RepeatingClosure quit_closure,
+                        const std::string& response_body,
+                        projector::mojom::XhrResponseCode response_code) {
+                       EXPECT_EQ(expected_response_body, response_body);
+                       EXPECT_EQ(response_code,
+                                 projector::mojom::XhrResponseCode::kSuccess);
+                       quit_closure.Run();
+                     },
+                     test_response_body, run_loop.QuitClosure()),
+                 base::flat_map<std::string, std::string>(), kTestUserEmail);
 
   mock_app_client().test_url_loader_factory().AddResponse(kTestDriveRequestUrl,
                                                           test_response_body);
@@ -276,18 +286,20 @@
   base::RunLoop run_loop;
 
   sender()->Send(
-      GURL(kTestDriveRequestUrl), /*method=*/"GET", /*request_body=*/"",
+      GURL(kTestDriveRequestUrl),
+      /*method=*/projector::mojom::RequestType::kGet, /*request_body=*/"",
       /*use_credentials=*/false, /*use_api_key=*/false,
       base::BindOnce(
-          [](base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_FALSE(success);
+          [](base::RepeatingClosure quit_closure,
+             const std::string& response_body,
+             projector::mojom::XhrResponseCode response_code) {
             EXPECT_EQ("", response_body);
-            EXPECT_EQ("INVALID_ACCOUNT_EMAIL", error);
+            EXPECT_EQ(response_code,
+                      projector::mojom::XhrResponseCode::kInvalidAccountEmail);
             quit_closure.Run();
           },
           run_loop.QuitClosure()),
-      /*headers=*/base::Value::Dict(),
+      /*headers=*/base::flat_map<std::string, std::string>(),
       /*account_email*/ kInvalidTestUserEmail);
 
   run_loop.Run();
@@ -301,20 +313,22 @@
 
   const std::string& test_response_body = "{}";
   sender()->Send(
-      GURL(kTestDriveRequestUrl), "GET", /*request_body=*/"",
+      GURL(kTestDriveRequestUrl), projector::mojom::RequestType::kGet,
+      /*request_body=*/"",
       /*use_credentials=*/false,
       /*use_api_key=*/false,
       base::BindOnce(
           [](const std::string& expected_response_body,
-             base::RepeatingClosure quit_closure, bool success,
-             const std::string& response_body, const std::string& error) {
-            EXPECT_TRUE(success);
+             base::RepeatingClosure quit_closure,
+             const std::string& response_body,
+             projector::mojom::XhrResponseCode response_code) {
             EXPECT_EQ(expected_response_body, response_body);
-            EXPECT_EQ("", error);
+            EXPECT_EQ(response_code,
+                      projector::mojom::XhrResponseCode::kSuccess);
             quit_closure.Run();
           },
           test_response_body, run_loop.QuitClosure()),
-      base::Value::Dict(), kTestUserSecondaryEmail);
+      base::flat_map<std::string, std::string>(), kTestUserSecondaryEmail);
 
   mock_app_client().test_url_loader_factory().AddResponse(kTestDriveRequestUrl,
                                                           test_response_body);
diff --git a/ash/webui/projector_app/test/untrusted_projector_page_handler_impl_unittest.cc b/ash/webui/projector_app/test/untrusted_projector_page_handler_impl_unittest.cc
index edaae5e7..b0fda148 100644
--- a/ash/webui/projector_app/test/untrusted_projector_page_handler_impl_unittest.cc
+++ b/ash/webui/projector_app/test/untrusted_projector_page_handler_impl_unittest.cc
@@ -20,12 +20,24 @@
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest-death-test.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace ash {
 
 namespace {
 
+constexpr char kTestUserEmail[] = "testuser1@gmail.com";
+constexpr char kTestXhrUrl[] =
+    "https://www.googleapis.com/drive/v3/files/fileID";
+constexpr char kTestXhrUnsupportedUrl[] = "https://www.example.com";
+constexpr ash::projector::mojom::RequestType kTestXhrMethod =
+    ash::projector::mojom::RequestType::kPost;
+constexpr char kTestXhrRequestBody[] = "{}";
+constexpr char kTestXhrHeaderKey[] = "X-Goog-Drive-Resource-Keys";
+constexpr char kTestXhrHeaderValue[] = "resource-key";
+constexpr char kTestResponseBody[] = "{}";
+
 // MOCK the Projector page instance in the WebUI renderer.
 class MockUntrustedProjectorPageJs
     : public projector::mojom::UntrustedProjectorPage {
@@ -304,4 +316,111 @@
   EXPECT_FALSE(base::SafeBaseName::Create(failing_path_3));
 }
 
+TEST_F(UntrustedProjectorPageHandlerImplUnitTest, SendXhr) {
+  mock_app_client().test_url_loader_factory().AddResponse(kTestXhrUrl,
+                                                          kTestResponseBody);
+  const base::flat_map<std::string, std::string> headers{
+      {std::string(kTestXhrHeaderKey), std::string(kTestXhrHeaderValue)}};
+
+  base::test::TestFuture<projector::mojom::XhrResponsePtr>
+      send_xhr_request_future;
+  page().page_handler()->SendXhr(
+      GURL(kTestXhrUrl), kTestXhrMethod, kTestXhrRequestBody,
+      /*use_credentials=*/true,
+      /*use_api_key=*/false, headers,
+      /*email=*/absl::nullopt, send_xhr_request_future.GetCallback());
+  mock_app_client().WaitForAccessRequest(kTestUserEmail);
+
+  const auto& response = send_xhr_request_future.Get();
+  EXPECT_EQ(response->response, kTestResponseBody);
+  EXPECT_EQ(response->response_code,
+            projector::mojom::XhrResponseCode::kSuccess);
+}
+
+TEST_F(UntrustedProjectorPageHandlerImplUnitTest, SendXhrEmptyEmail) {
+  mock_app_client().test_url_loader_factory().AddResponse(kTestXhrUrl,
+                                                          kTestResponseBody);
+  const base::flat_map<std::string, std::string> headers{
+      {std::string(kTestXhrHeaderKey), std::string(kTestXhrHeaderValue)}};
+
+  base::test::TestFuture<projector::mojom::XhrResponsePtr>
+      send_xhr_request_future;
+  page().page_handler()->SendXhr(
+      GURL(kTestXhrUrl), kTestXhrMethod, kTestXhrRequestBody,
+      /*use_credentials=*/true,
+      /*use_api_key=*/false, headers,
+      /*email=*/"", send_xhr_request_future.GetCallback());
+  mock_app_client().WaitForAccessRequest(kTestUserEmail);
+
+  const auto& response = send_xhr_request_future.Get();
+  EXPECT_EQ(response->response, kTestResponseBody);
+  EXPECT_EQ(response->response_code,
+            projector::mojom::XhrResponseCode::kSuccess);
+}
+
+TEST_F(UntrustedProjectorPageHandlerImplUnitTest, SendXhrWithEmail) {
+  mock_app_client().test_url_loader_factory().AddResponse(kTestXhrUrl,
+                                                          kTestResponseBody);
+  const base::flat_map<std::string, std::string> headers{
+      {std::string(kTestXhrHeaderKey), std::string(kTestXhrHeaderValue)}};
+  base::test::TestFuture<projector::mojom::XhrResponsePtr>
+      send_xhr_request_future;
+  page().page_handler()->SendXhr(GURL(kTestXhrUrl), kTestXhrMethod,
+                                 kTestXhrRequestBody,
+                                 /*use_credentials=*/true,
+                                 /*use_api_key=*/false, headers, kTestUserEmail,
+                                 send_xhr_request_future.GetCallback());
+  mock_app_client().WaitForAccessRequest(kTestUserEmail);
+
+  const auto& response = send_xhr_request_future.Get();
+  EXPECT_EQ(response->response, kTestResponseBody);
+  EXPECT_EQ(response->response_code,
+            projector::mojom::XhrResponseCode::kSuccess);
+}
+
+TEST_F(UntrustedProjectorPageHandlerImplUnitTest, SendXhrFailed) {
+  constexpr char kTestErrorResponseBody[] = "error";
+  mock_app_client().test_url_loader_factory().AddResponse(
+      /*url=*/kTestXhrUrl,
+      /*content=*/kTestErrorResponseBody,
+      /*status=*/net::HttpStatusCode::HTTP_NOT_FOUND);
+  const base::flat_map<std::string, std::string> headers{
+      {std::string(kTestXhrHeaderKey), std::string(kTestXhrHeaderValue)}};
+  base::test::TestFuture<projector::mojom::XhrResponsePtr>
+      send_xhr_request_future;
+  page().page_handler()->SendXhr(GURL(kTestXhrUrl), kTestXhrMethod,
+                                 kTestXhrRequestBody,
+                                 /*use_credentials=*/true,
+                                 /*use_api_key=*/false, headers, kTestUserEmail,
+                                 send_xhr_request_future.GetCallback());
+
+  mock_app_client().WaitForAccessRequest(kTestUserEmail);
+  const auto& response = send_xhr_request_future.Get();
+  EXPECT_EQ(response->response, kTestErrorResponseBody);
+  EXPECT_EQ(response->response_code,
+            projector::mojom::XhrResponseCode::kXhrFetchFailure);
+}
+
+TEST_F(UntrustedProjectorPageHandlerImplUnitTest, SendXhrWithUnSupportedUrl) {
+  auto crashing_lambda_test = [&]() {
+    const base::flat_map<std::string, std::string> headers{
+        {std::string(kTestXhrHeaderKey), std::string(kTestXhrHeaderValue)}};
+
+    base::test::TestFuture<projector::mojom::XhrResponsePtr>
+        send_xhr_request_future;
+
+    page().page_handler()->SendXhr(
+        GURL(kTestXhrUnsupportedUrl), kTestXhrMethod, kTestXhrRequestBody,
+        /*use_credentials=*/true,
+        /*use_api_key=*/false, headers, kTestUserEmail,
+        send_xhr_request_future.GetCallback());
+
+    const auto& response = send_xhr_request_future.Get();
+    EXPECT_EQ(response->response_code,
+              projector::mojom::XhrResponseCode::kUnsupportedURL);
+  };
+
+  EXPECT_DEATH_IF_SUPPORTED(crashing_lambda_test(), "");
+}
+
 }  // namespace ash
diff --git a/ash/webui/projector_app/untrusted_projector_page_handler_impl.cc b/ash/webui/projector_app/untrusted_projector_page_handler_impl.cc
index 7ca2f7b..c737f0e 100644
--- a/ash/webui/projector_app/untrusted_projector_page_handler_impl.cc
+++ b/ash/webui/projector_app/untrusted_projector_page_handler_impl.cc
@@ -10,9 +10,11 @@
 #include "ash/public/cpp/projector/projector_new_screencast_precondition.h"
 #include "ash/webui/projector_app/mojom/untrusted_projector.mojom.h"
 #include "ash/webui/projector_app/projector_app_client.h"
+#include "ash/webui/projector_app/projector_xhr_sender.h"
 #include "ash/webui/projector_app/public/mojom/projector_types.mojom.h"
 #include "base/files/safe_base_name.h"
 #include "components/prefs/pref_service.h"
+#include "url/gurl.h"
 
 namespace ash {
 
@@ -71,7 +73,8 @@
     PrefService* pref_service)
     : receiver_(this, std::move(receiver)),
       projector_remote_(std::move(projector_remote)),
-      pref_service_(pref_service) {
+      pref_service_(pref_service),
+      xhr_sender_(ProjectorAppClient::Get()->GetUrlLoaderFactory()) {
   ProjectorAppClient::Get()->AddObserver(this);
 }
 
@@ -188,4 +191,43 @@
   std::move(callback).Run(/*success=*/true);
 }
 
+void UntrustedProjectorPageHandlerImpl::SendXhr(
+    const GURL& url,
+    projector::mojom::RequestType method,
+    const absl::optional<std::string>& request_body,
+    bool use_credentials,
+    bool use_api_key,
+    const absl::optional<base::flat_map<std::string, std::string>>& headers,
+    const absl::optional<std::string>& account_email,
+    SendXhrCallback callback) {
+  CHECK(url.is_valid());
+  xhr_sender_.Send(
+      url, method, request_body, use_credentials, use_api_key,
+      base::BindOnce(&UntrustedProjectorPageHandlerImpl::OnXhrRequestCompleted,
+                     GetWeakPtr(), std::move(callback)),
+      headers, account_email);
+}
+
+base::WeakPtr<UntrustedProjectorPageHandlerImpl>
+UntrustedProjectorPageHandlerImpl::GetWeakPtr() {
+  return weak_ptr_factory_.GetWeakPtr();
+}
+
+void UntrustedProjectorPageHandlerImpl::OnXhrRequestCompleted(
+    SendXhrCallback callback,
+    const std::string& response_body,
+    projector::mojom::XhrResponseCode response_code) {
+  // If the request made is an unsupported url, then
+  // crash the renderer.
+  if (response_code == projector::mojom::XhrResponseCode::kUnsupportedURL) {
+    receiver_.ReportBadMessage("Unsupported url requested.");
+    return;
+  }
+
+  auto response = projector::mojom::XhrResponse::New();
+  response->response = response_body;
+  response->response_code = response_code;
+  std::move(callback).Run(std::move(response));
+}
+
 }  // namespace ash
diff --git a/ash/webui/projector_app/untrusted_projector_page_handler_impl.h b/ash/webui/projector_app/untrusted_projector_page_handler_impl.h
index 2d9438f..255cb09d 100644
--- a/ash/webui/projector_app/untrusted_projector_page_handler_impl.h
+++ b/ash/webui/projector_app/untrusted_projector_page_handler_impl.h
@@ -7,13 +7,16 @@
 
 #include "ash/webui/projector_app/mojom/untrusted_projector.mojom.h"
 #include "ash/webui/projector_app/projector_app_client.h"
+#include "ash/webui/projector_app/projector_xhr_sender.h"
 #include "base/files/safe_base_name.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
 #include "components/prefs/pref_service.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
+#include "url/gurl.h"
 
 namespace ash {
 
@@ -58,6 +61,25 @@
   void OpenFeedbackDialog(OpenFeedbackDialogCallback callback) override;
   void StartProjectorSession(const base::SafeBaseName& storage_dir_name,
                              StartProjectorSessionCallback callback) override;
+  void SendXhr(
+      const GURL& url,
+      projector::mojom::RequestType method,
+      const absl::optional<std::string>& request_body,
+      bool use_credentials,
+      bool use_api_key,
+      const absl::optional<base::flat_map<std::string, std::string>>& headers,
+      const absl::optional<std::string>& account_email,
+      SendXhrCallback callback) override;
+
+ protected:
+  base::WeakPtr<UntrustedProjectorPageHandlerImpl> GetWeakPtr();
+
+  // Called when the XHR request is completed. Runs the callback with the
+  // results.
+  virtual void OnXhrRequestCompleted(
+      SendXhrCallback callback,
+      const std::string& response_body,
+      projector::mojom::XhrResponseCode response_code);
 
  private:
   mojo::Receiver<projector::mojom::UntrustedProjectorPageHandler> receiver_;
@@ -65,6 +87,10 @@
 
   // Primary user pref service.
   const raw_ptr<PrefService, ExperimentalAsh> pref_service_;
+  ProjectorXhrSender xhr_sender_;
+
+  base::WeakPtrFactory<UntrustedProjectorPageHandlerImpl> weak_ptr_factory_{
+      this};
 };
 
 }  // namespace ash
diff --git a/ash/wm/desks/desk_bar_view_base.cc b/ash/wm/desks/desk_bar_view_base.cc
index ec2c3e7..e4ce74b 100644
--- a/ash/wm/desks/desk_bar_view_base.cc
+++ b/ash/wm/desks/desk_bar_view_base.cc
@@ -928,7 +928,7 @@
 
   library_button_->SetVisible(should_show_ui);
   if (should_show_ui) {
-    if (overview_grid_->WillShowSavedDeskLibrary()) {
+    if (overview_grid_->IsShowingSavedDeskLibrary()) {
       library_button_->UpdateState(CrOSNextDeskIconButton::State::kActive);
     } else {
       library_button_->UpdateState(CrOSNextDeskIconButton::State::kExpanded);
diff --git a/ash/wm/desks/desk_mini_view_animations.cc b/ash/wm/desks/desk_mini_view_animations.cc
index b49a8ff8..4e6b8c5 100644
--- a/ash/wm/desks/desk_mini_view_animations.cc
+++ b/ash/wm/desks/desk_mini_view_animations.cc
@@ -513,7 +513,7 @@
   auto* library_button = bar_view->library_button();
 
   if (library_button) {
-    if (bar_view->overview_grid()->WillShowSavedDeskLibrary()) {
+    if (bar_view->overview_grid()->IsShowingSavedDeskLibrary()) {
       // For library button, when it's at zero state and clicked, the desks bar
       // will expand, the overview grid will show the saved desk library, the
       // library button should be activated and focused.
diff --git a/ash/wm/desks/templates/saved_desk_presenter.cc b/ash/wm/desks/templates/saved_desk_presenter.cc
index 08feccedf..d8ef710 100644
--- a/ash/wm/desks/templates/saved_desk_presenter.cc
+++ b/ash/wm/desks/templates/saved_desk_presenter.cc
@@ -557,8 +557,8 @@
   const base::Uuid uuid = saved_desk->uuid();
 
   auto* overview_controller = Shell::Get()->overview_controller();
-  if (saved_desk_type == DeskTemplateType::kSaveAndRecall) {
-    if (overview_controller->InOverviewSession()) {
+  if (overview_controller->InOverviewSession()) {
+    if (saved_desk_type == DeskTemplateType::kSaveAndRecall) {
       auto* overview_session = overview_controller->overview_session();
       OverviewGrid* overview_grid =
           overview_session->GetGridWithRootWindow(root_window);
@@ -570,6 +570,13 @@
 
       SavedDeskLibraryView* library = overview_grid->GetSavedDeskLibraryView();
       library->AnimateDeskLaunch(uuid, mini_view);
+    } else if (saved_desk_type == DeskTemplateType::kTemplate) {
+      // For a desk template launch, we will stay in overview mode and hide the
+      // library. The overview grid will show and get populated with launched
+      // apps.
+      for (auto& overview_grid : overview_session_->grid_list()) {
+        overview_grid->HideSavedDeskLibrary(/*exit_overview=*/false);
+      }
     }
   }
 
diff --git a/ash/wm/overview/overview_grid.cc b/ash/wm/overview/overview_grid.cc
index bfac56a..ce7f834 100644
--- a/ash/wm/overview/overview_grid.cc
+++ b/ash/wm/overview/overview_grid.cc
@@ -758,8 +758,9 @@
   if (reposition)
     PositionWindows(should_animate, ignored_items);
 
-  if (IsShowingSavedDeskLibrary() || WillShowSavedDeskLibrary())
+  if (IsShowingSavedDeskLibrary()) {
     item->HideForSavedDeskLibrary(/*animate=*/false);
+  }
 }
 
 void OverviewGrid::AppendItem(aura::Window* window,
@@ -1893,12 +1894,9 @@
 }
 
 bool OverviewGrid::IsShowingSavedDeskLibrary() const {
-  return saved_desk_library_widget_ && saved_desk_library_widget_->IsVisible();
-}
-
-bool OverviewGrid::WillShowSavedDeskLibrary() const {
-  return saved_desk_library_widget_ && saved_desk_library_widget_->GetLayer() &&
-         saved_desk_library_widget_->GetLayer()->GetTargetVisibility() != 0.f;
+  return saved_desk_library_widget_ &&
+         saved_desk_library_widget_->IsVisible() &&
+         saved_desk_library_widget_->GetLayer()->GetTargetOpacity() == 1.0f;
 }
 
 bool OverviewGrid::IsSavedDeskNameBeingModified() const {
diff --git a/ash/wm/overview/overview_grid.h b/ash/wm/overview/overview_grid.h
index 0117152..a77516f 100644
--- a/ash/wm/overview/overview_grid.h
+++ b/ash/wm/overview/overview_grid.h
@@ -343,12 +343,10 @@
   // save desk buttons if we are not exiting overview.
   void HideSavedDeskLibrary(bool exit_overview);
 
-  // True if the saved desk library is shown.
+  // True if the saved desk library is shown, or in the process of animating to
+  // be shown.
   bool IsShowingSavedDeskLibrary() const;
 
-  // True if the saved desk library will be shown shortly.
-  bool WillShowSavedDeskLibrary() const;
-
   // Returns true if any saved desk name is being modified in its item view on
   // this grid.
   bool IsSavedDeskNameBeingModified() const;
diff --git a/ash/wm/overview/overview_session.cc b/ash/wm/overview/overview_session.cc
index 2084dd6..006780f 100644
--- a/ash/wm/overview/overview_session.cc
+++ b/ash/wm/overview/overview_session.cc
@@ -1124,11 +1124,6 @@
                             : grid_list_.front()->IsShowingSavedDeskLibrary();
 }
 
-bool OverviewSession::WillShowSavedDeskLibrary() const {
-  return grid_list_.empty() ? false
-                            : grid_list_.front()->WillShowSavedDeskLibrary();
-}
-
 bool OverviewSession::ShouldEnterWithoutAnimations() const {
   return enter_exit_overview_type_ == OverviewEnterExitType::kImmediateEnter ||
          enter_exit_overview_type_ ==
@@ -1562,8 +1557,7 @@
   // `ShowInactive()` instead of `ActivateWindow()` to show the widget.
   // When the saved desk library is on, do not switch focus to avoid unexpected
   // name commit.
-  bool saved_desk_grid_should_keep_focus =
-      IsShowingSavedDeskLibrary() || WillShowSavedDeskLibrary();
+  bool saved_desk_grid_should_keep_focus = IsShowingSavedDeskLibrary();
   if (saved_desk_grid_should_keep_focus)
     overview_focus_widget_->ShowInactive();
   else
diff --git a/ash/wm/overview/overview_session.h b/ash/wm/overview/overview_session.h
index 137b05b..3d9b38c1 100644
--- a/ash/wm/overview/overview_session.h
+++ b/ash/wm/overview/overview_session.h
@@ -306,9 +306,6 @@
   // True if the saved desk library is shown.
   bool IsShowingSavedDeskLibrary() const;
 
-  // True if the saved desk library will be shown shortly.
-  bool WillShowSavedDeskLibrary() const;
-
   // True if we want to enter overview without animations.
   bool ShouldEnterWithoutAnimations() const;
 
diff --git a/base/strings/string_number_conversions_internal.h b/base/strings/string_number_conversions_internal.h
index f430857..3642ec2c 100644
--- a/base/strings/string_number_conversions_internal.h
+++ b/base/strings/string_number_conversions_internal.h
@@ -5,10 +5,8 @@
 #ifndef BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_
 #define BASE_STRINGS_STRING_NUMBER_CONVERSIONS_INTERNAL_H_
 
-#include <ctype.h>
 #include <errno.h>
 #include <stdlib.h>
-#include <wctype.h>
 
 #include <limits>
 
@@ -72,32 +70,6 @@
   return absl::nullopt;
 }
 
-// There is an IsUnicodeWhitespace for wchars defined in string_util.h, but it
-// is locale independent, whereas the functions we are replacing were
-// locale-dependent. TBD what is desired, but for the moment let's not
-// introduce a change in behaviour.
-template <typename CHAR>
-class WhitespaceHelper {};
-
-template <>
-class WhitespaceHelper<char> {
- public:
-  static bool Invoke(char c) {
-    return 0 != isspace(static_cast<unsigned char>(c));
-  }
-};
-
-template <>
-class WhitespaceHelper<char16_t> {
- public:
-  static bool Invoke(char16_t c) { return 0 != iswspace(c); }
-};
-
-template <typename CHAR>
-bool LocalIsWhitespace(CHAR c) {
-  return WhitespaceHelper<CHAR>::Invoke(c);
-}
-
 template <typename Number, int kBase>
 class StringToNumberParser {
  public:
@@ -188,7 +160,7 @@
   auto begin = input.begin();
   auto end = input.end();
 
-  while (begin != end && LocalIsWhitespace(*begin)) {
+  while (begin != end && IsAsciiWhitespace(*begin)) {
     has_leading_whitespace = true;
     ++begin;
   }
@@ -272,7 +244,11 @@
   //  - If the entire string was not processed, there are either characters
   //    remaining in the string after a parsed number, or the string does not
   //    begin with a parseable number.
-  //  - If the first character is a space, there was leading whitespace
+  //  - If the first character is a space, there was leading whitespace. Note
+  //    that this checks using IsWhitespace(), which behaves differently for
+  //    wide and narrow characters -- that is intentional and matches the
+  //    behavior of the double_conversion library's whitespace-skipping
+  //    algorithm.
   return !input.empty() && output != HUGE_VAL && output != -HUGE_VAL &&
          static_cast<size_t>(processed_characters_count) == input.size() &&
          !IsWhitespace(input[0]);
diff --git a/base/strings/string_number_conversions_win.cc b/base/strings/string_number_conversions_win.cc
index eed83c5..8caa0ce 100644
--- a/base/strings/string_number_conversions_win.cc
+++ b/base/strings/string_number_conversions_win.cc
@@ -39,16 +39,6 @@
   return internal::DoubleToStringT<std::wstring>(value);
 }
 
-namespace internal {
-
-template <>
-class WhitespaceHelper<wchar_t> {
- public:
-  static bool Invoke(wchar_t c) { return 0 != iswspace(c); }
-};
-
-}  // namespace internal
-
 bool StringToInt(WStringPiece input, int* output) {
   return internal::StringToIntImpl(input, *output);
 }
diff --git a/base/threading/platform_thread_mac.mm b/base/threading/platform_thread_mac.mm
index fa192b70..f6e0dc9 100644
--- a/base/threading/platform_thread_mac.mm
+++ b/base/threading/platform_thread_mac.mm
@@ -93,6 +93,9 @@
 #endif
 );
 
+const Feature kUserInteractiveCompositingMac{"UserInteractiveCompositingMac",
+                                             FEATURE_DISABLED_BY_DEFAULT};
+
 namespace {
 
 bool IsOptimizedRealtimeThreadingMacEnabled() {
@@ -118,6 +121,8 @@
 // (kOptimizedRealtimeThreadingMacBusy, 1].
 const FeatureParam<double> kOptimizedRealtimeThreadingMacBusyLimit{
     &kOptimizedRealtimeThreadingMac, "busy_limit", 1.0};
+std::atomic<bool> g_user_interactive_compositing(
+    kUserInteractiveCompositingMac.default_state == FEATURE_ENABLED_BY_DEFAULT);
 
 namespace {
 
@@ -154,6 +159,8 @@
     g_time_constraints.store(TimeConstraints::ReadFromFeatureParams());
     g_use_optimized_realtime_threading.store(
         IsOptimizedRealtimeThreadingMacEnabled());
+    g_user_interactive_compositing.store(
+        FeatureList::IsEnabled(kUserInteractiveCompositingMac));
   }
 }
 
@@ -320,13 +327,18 @@
       pthread_set_qos_class_self_np(QOS_CLASS_UTILITY, 0);
       break;
     case ThreadType::kDefault:
-      // TODO(1329208): Experiment with prioritizing kCompositing on Mac like on
-      // other platforms.
-      [[fallthrough]];
-    case ThreadType::kCompositing:
       priority = ThreadPriorityForTest::kNormal;
       pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
       break;
+    case ThreadType::kCompositing:
+      if (g_user_interactive_compositing.load(std::memory_order_relaxed)) {
+        priority = ThreadPriorityForTest::kDisplay;
+        pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
+      } else {
+        priority = ThreadPriorityForTest::kNormal;
+        pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0);
+      }
+      break;
     case ThreadType::kDisplayCritical: {
       priority = ThreadPriorityForTest::kDisplay;
       pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
diff --git a/base/threading/threading_features.h b/base/threading/threading_features.h
index 856c9efe..a682df4 100644
--- a/base/threading/threading_features.h
+++ b/base/threading/threading_features.h
@@ -23,7 +23,7 @@
     kOptimizedRealtimeThreadingMacBusy;
 extern const BASE_EXPORT FeatureParam<double>
     kOptimizedRealtimeThreadingMacBusyLimit;
-extern const BASE_EXPORT Feature kUseThreadQoSMac;
+extern const BASE_EXPORT Feature kUserInteractiveCompositingMac;
 #endif
 
 #if BUILDFLAG(IS_WIN)
diff --git a/build/android/gyp/create_app_bundle.py b/build/android/gyp/create_app_bundle.py
index 12826086..6bef20b1 100755
--- a/build/android/gyp/create_app_bundle.py
+++ b/build/android/gyp/create_app_bundle.py
@@ -94,10 +94,6 @@
       '--rtxt-out-path', help='Path to combined R.txt file for bundle.')
   parser.add_argument('--uncompressed-assets', action='append',
                       help='GN-list of uncompressed assets.')
-  parser.add_argument(
-      '--compress-shared-libraries',
-      action='store_true',
-      help='Whether to store native libraries compressed.')
   parser.add_argument('--compress-dex',
                       action='store_true',
                       help='Compress .dex files')
@@ -195,15 +191,13 @@
 
 
 def _GenerateBundleConfigJson(uncompressed_assets, compress_dex,
-                              compress_shared_libraries, split_dimensions,
-                              base_master_resource_ids):
+                              split_dimensions, base_master_resource_ids):
   """Generate a dictionary that can be written to a JSON BuildConfig.
 
   Args:
     uncompressed_assets: A list or set of file paths under assets/ that always
       be stored uncompressed.
     compressed_dex: Boolean, whether to compress .dex.
-    compress_shared_libraries: Boolean, whether to compress native libs.
     split_dimensions: list of split dimensions.
     base_master_resource_ids: Optional list of 32-bit resource IDs to keep
       inside the base module, even when split dimensions are enabled.
@@ -242,7 +236,7 @@
               'splitDimension': split_dimensions,
           },
           'uncompressNativeLibraries': {
-              'enabled': not compress_shared_libraries,
+              'enabled': True,
           },
           'uncompressDexFiles': {
               'enabled': True,  # Applies only for P+.
@@ -542,7 +536,6 @@
     logging.info('Creating BundleConfig.pb.json')
     bundle_config = _GenerateBundleConfigJson(options.uncompressed_assets,
                                               options.compress_dex,
-                                              options.compress_shared_libraries,
                                               split_dimensions,
                                               base_master_resource_ids)
 
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index e4e240a0..26cd2e8b 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -4828,9 +4828,6 @@
   #    proguard_android_sdk_dep: Optional. android_system_java_prebuilt() target
   #      used as a library jar for synchronized proguarding.
   #
-  #    compress_shared_libraries: Optional. Whether to compress shared libraries
-  #      such that they are extracted upon install.
-  #
   #    system_image_locale_allowlist: List of locales that should be included
   #      on system APKs generated from this bundle.
   #
@@ -5332,10 +5329,6 @@
       if (_split_dimensions != []) {
         args += [ "--split-dimensions=$_split_dimensions" ]
       }
-      if (defined(invoker.compress_shared_libraries) &&
-          invoker.compress_shared_libraries) {
-        args += [ "--compress-shared-libraries" ]
-      }
 
       # Android P+ support loading from stored dex.
       if (_min_sdk_version < 27) {
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index 462bccc..4f1b825 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -76,6 +76,13 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 
 namespace {
+
+constexpr base::FeatureParam<int> kPurgeInterval{&kPurgeOldCacheEntriesOnTimer,
+                                                 "seconds", 30};
+
+constexpr base::FeatureParam<int> kPurgeMaxAge{&kPurgeOldCacheEntriesOnTimer,
+                                               "seconds", 30};
+
 // The number or entries to keep in the cache, depending on the memory state of
 // the system. This limit can be breached by in-use cache items, which cannot
 // be deleted.
@@ -1218,6 +1225,10 @@
       max_working_set_bytes_(max_working_set_bytes),
       max_working_set_items_(kMaxItemsInWorkingSet),
       dark_mode_filter_(dark_mode_filter) {
+  if (base::SequencedTaskRunner::HasCurrentDefault()) {
+    task_runner_ = base::SequencedTaskRunner::GetCurrentDefault();
+  }
+
   DCHECK_NE(generator_client_id_, PaintImage::kDefaultGeneratorClientId);
   // Note that to compute |allow_accelerated_jpeg_decodes_| and
   // |allow_accelerated_webp_decodes_|, the last thing we check is the feature
@@ -1234,10 +1245,6 @@
       context_->ContextSupport()->IsWebPDecodeAccelerationSupported() &&
       base::FeatureList::IsEnabled(features::kVaapiWebPImageDecodeAcceleration);
 
-  // The timer needs to run its task on the same thread that it is destroyed on,
-  // so we explicitly set the TaskRunner here.
-  timer_.SetTaskRunner(base::SequencedTaskRunner::GetCurrentDefault());
-
   {
     // TODO(crbug.com/1110007): We shouldn't need to lock to get capabilities.
     absl::optional<viz::RasterContextProvider::ScopedRasterContextLock>
@@ -1729,7 +1736,9 @@
 
 void GpuImageDecodeCache::PurgeOldCacheEntriesCallback() {
   base::AutoLock locker(lock_);
-  DoPurgeOldCacheEntries(kPurgeMaxAge);
+  DoPurgeOldCacheEntries(get_max_purge_age());
+
+  has_pending_purge_task_ = false;
 
   // If the cache is empty, we stop posting the task, to avoid endless wakeups.
   if (persistent_cache_.empty()) {
@@ -1744,14 +1753,14 @@
     return;
   }
 
-  // |base::Unretained(this)| is fine in this case, since |timer_| is a member
-  // of |this|, (so destroying |this| will also destroy |timer_| and cancel the
-  // task), and the task will be run on the same thread that |this| is destroyed
-  // on.
-  timer_.Start(
-      FROM_HERE, GpuImageDecodeCache::kPurgeInterval,
-      base::BindOnce(&GpuImageDecodeCache::PurgeOldCacheEntriesCallback,
-                     base::Unretained(this)));
+  if (task_runner_) {
+    task_runner_->PostDelayedTask(
+        FROM_HERE,
+        base::BindOnce(&GpuImageDecodeCache::PurgeOldCacheEntriesCallback,
+                       weak_ptr_factory_.GetWeakPtr()),
+        get_purge_interval());
+    has_pending_purge_task_ = true;
+  }
 }
 
 size_t GpuImageDecodeCache::GetMaximumMemoryLimitBytes() const {
@@ -3705,4 +3714,12 @@
   return nullptr;
 }
 
+base::TimeDelta GpuImageDecodeCache::get_purge_interval() {
+  return base::Seconds(kPurgeInterval.Get());
+}
+
+base::TimeDelta GpuImageDecodeCache::get_max_purge_age() {
+  return base::Seconds(kPurgeMaxAge.Get());
+}
+
 }  // namespace cc
diff --git a/cc/tiles/gpu_image_decode_cache.h b/cc/tiles/gpu_image_decode_cache.h
index dc1f5b2e..426a260 100644
--- a/cc/tiles/gpu_image_decode_cache.h
+++ b/cc/tiles/gpu_image_decode_cache.h
@@ -20,6 +20,7 @@
 #include "base/memory/memory_pressure_listener.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/raw_ptr_exclusion.h"
+#include "base/memory/weak_ptr.h"
 #include "base/synchronization/lock.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -159,8 +160,8 @@
   // Returns the GL texture ID backing the given SkImage.
   static GrGLuint GlIdFromSkImage(const SkImage* image);
 
-  static constexpr base::TimeDelta kPurgeInterval = base::Seconds(30);
-  static constexpr base::TimeDelta kPurgeMaxAge = base::Seconds(30);
+  static base::TimeDelta get_purge_interval();
+  static base::TimeDelta get_max_purge_age();
 
   // ImageDecodeCache overrides.
 
@@ -244,13 +245,6 @@
     return has_pending_purge_task();
   }
 
-  void SetTimerTaskRunnerForTesting(
-      scoped_refptr<base::SequencedTaskRunner> task_runner)
-      LOCKS_EXCLUDED(lock_) {
-    base::AutoLock locker(lock_);
-    timer_.SetTaskRunner(task_runner);
-  }
-
   // Updating the |last_use| field of the associated |ImageData|.
   void TouchCacheEntryForTesting(const DrawImage& draw_image)
       LOCKS_EXCLUDED(lock_);
@@ -905,7 +899,7 @@
       const ImageTaskMap& task_map);
 
   bool has_pending_purge_task() const EXCLUSIVE_LOCKS_REQUIRED(lock_) {
-    return timer_.IsRunning();
+    return has_pending_purge_task_;
   }
 
   const SkColorType color_type_;
@@ -918,12 +912,14 @@
   SkYUVAPixmapInfo::SupportedDataTypes yuva_supported_data_types_;
   const bool enable_clipped_image_scaling_;
 
+  scoped_refptr<base::SequencedTaskRunner> task_runner_ = nullptr;
+
   // All members below this point must only be accessed while holding |lock_|.
   // The exception are const members like |normal_max_cache_bytes_| that can
   // be accessed without a lock since they are thread safe.
   mutable base::Lock lock_;
 
-  base::OneShotTimer timer_ GUARDED_BY(lock_);
+  bool has_pending_purge_task_ GUARDED_BY(lock_) = false;
 
   PersistentCache persistent_cache_ GUARDED_BY(lock_);
 
@@ -976,6 +972,7 @@
   std::vector<uint32_t> ids_pending_deletion_;
 
   std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
+  base::WeakPtrFactory<GpuImageDecodeCache> weak_ptr_factory_{this};
 };
 
 }  // namespace cc
diff --git a/cc/tiles/gpu_image_decode_cache_unittest.cc b/cc/tiles/gpu_image_decode_cache_unittest.cc
index af0485c..355fd13 100644
--- a/cc/tiles/gpu_image_decode_cache_unittest.cc
+++ b/cc/tiles/gpu_image_decode_cache_unittest.cc
@@ -4596,9 +4596,12 @@
   void SetUp() override {
     GpuImageDecodeCacheTest::SetUp();
 
-    cache_ = CreateCache();
     task_runner_ = base::MakeRefCounted<base::TestMockTimeTaskRunner>();
-    cache_->SetTimerTaskRunnerForTesting(task_runner_);
+    current_default_handle_ = std::make_unique<
+        base::SingleThreadTaskRunner::CurrentHandleOverrideForTesting>(
+        task_runner_);
+
+    cache_ = CreateCache();
     client_id_ = cache_->GenerateClientId();
 
     // We can't convert a lambda with capture to a raw function pointer, so we
@@ -4630,8 +4633,10 @@
     }
   }
 
+  std::unique_ptr<base::SingleThreadTaskRunner::CurrentHandleOverrideForTesting>
+      current_default_handle_ = nullptr;
+  std::unique_ptr<GpuImageDecodeCache> cache_ = nullptr;
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
-  std::unique_ptr<GpuImageDecodeCache> cache_;
   uint32_t client_id_;
   std::unique_ptr<base::subtle::ScopedTimeClockOverrides> time_override_;
 };
@@ -4648,13 +4653,13 @@
   ASSERT_EQ(cache_->GetNumCacheEntriesForTesting(), 1u);
   ASSERT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval / 2);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval() / 2);
 
   // We haven't fast forwarded enough, so the entry is still in the cache.
   EXPECT_EQ(cache_->GetNumCacheEntriesForTesting(), 1u);
   EXPECT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval());
 
   // Cache has been emptied
   EXPECT_EQ(cache_->GetNumCacheEntriesForTesting(), 0u);
@@ -4674,13 +4679,13 @@
   ASSERT_EQ(cache_->GetNumCacheEntriesForTesting(), 3u);
   ASSERT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval / 2);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval() / 2);
 
   // We haven't fast forwarded enough, so the entry is still in the cache.
   EXPECT_EQ(cache_->GetNumCacheEntriesForTesting(), 3u);
   EXPECT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval());
 
   // Cache has been emptied
   EXPECT_EQ(cache_->GetNumCacheEntriesForTesting(), 0u);
@@ -4701,7 +4706,7 @@
   ASSERT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
   // Time is now 15s.
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval / 2);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval() / 2);
 
   // No task posted, since we already have a task.
   CreateAndUnrefImage(4);
@@ -4712,7 +4717,7 @@
   ASSERT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
   // Time is now 30s, our task runs, and posts a new one.
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval / 2);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval() / 2);
 
   // The original images are purged, the newer ones are not, since they are only
   // 15s old.
@@ -4720,7 +4725,7 @@
   EXPECT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
   // Time is now 45s, second batch of images is now 30s old.
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval / 2);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval() / 2);
 
   // The images are old enough to be purged, but the task to purge them has not
   // run yet.
@@ -4728,7 +4733,7 @@
   EXPECT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
   // Time is now 60s, images are 45s old.
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval / 2);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval() / 2);
 
   // Cache has been emptied
   EXPECT_EQ(cache_->GetNumCacheEntriesForTesting(), 0u);
@@ -4749,7 +4754,7 @@
   ASSERT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
   // Time is now 30s, cache is emptied.
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval());
   ASSERT_EQ(cache_->GetNumCacheEntriesForTesting(), 0u);
   ASSERT_FALSE(cache_->HasPendingPurgeTaskForTesting());
 
@@ -4759,7 +4764,7 @@
   ASSERT_EQ(cache_->GetNumCacheEntriesForTesting(), 4u);
   ASSERT_TRUE(cache_->HasPendingPurgeTaskForTesting());
 
-  FastForwardBy(GpuImageDecodeCache::kPurgeInterval);
+  FastForwardBy(GpuImageDecodeCache::get_purge_interval());
 
   // Cache has been emptied
   EXPECT_EQ(cache_->GetNumCacheEntriesForTesting(), 0u);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
index f6a3fbb..b2fa81f9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkBridge.java
@@ -14,6 +14,7 @@
 
 import com.google.protobuf.InvalidProtocolBufferException;
 
+import org.chromium.base.Callback;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ObserverList;
 import org.chromium.base.ThreadUtils;
@@ -88,6 +89,16 @@
     }
 
     /**
+     * Gets the url for an image representing the given url.
+     * @param url The url to fetch the image for.
+     * @param callback The callback which will receive the image url.
+     */
+    public void getImageUrlForBookmark(GURL url, Callback<GURL> callback) {
+        BookmarkBridgeJni.get().getImageUrlForBookmark(
+                mNativeBookmarkBridge, BookmarkBridge.this, url, callback);
+    }
+
+    /**
      * @param tab Tab whose current URL is checked against.
      * @return {@code true} if the current Tab URL has a bookmark associated with it. If the
      *         bookmark backend is not loaded, return {@code false}.
@@ -922,6 +933,8 @@
     @NativeMethods
     public interface Natives {
         BookmarkModel getForProfile(Profile profile);
+        void getImageUrlForBookmark(long nativeBookmarkBridge, BookmarkBridge caller, GURL url,
+                Callback<GURL> callback);
         BookmarkId getBookmarkIdForWebContents(long nativeBookmarkBridge, BookmarkBridge caller,
                 WebContents webContents, boolean onlyEditable);
         BookmarkItem getBookmarkById(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
index 8e45466..81d03e5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerCoordinator.java
@@ -142,7 +142,9 @@
                 mSelectableListLayout, mSelectionDelegate, mRecyclerView,
                 dragReorderableRecyclerViewAdapter, largeIconBridge, isDialogUi, isIncognito,
                 mBackPressStateSupplier, mProfile, bookmarkUndoController, modelList,
-                mBookmarkUiPrefs, this::hideKeyboard);
+                mBookmarkUiPrefs, this::hideKeyboard,
+                ImageFetcherFactory.createImageFetcher(
+                        ImageFetcherConfig.DISK_CACHE_ONLY, mProfile.getProfileKey()));
         mPromoHeaderManager = mMediator.getPromoHeaderManager();
 
         bookmarkDelegateSupplier.set(/*bookmarkDelegate=*/mMediator);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
index 6d3ada74..c3fdeaa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediator.java
@@ -7,7 +7,9 @@
 import static org.chromium.components.browser_ui.widget.listmenu.BasicListMenu.buildMenuListItem;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -17,6 +19,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.RecyclerView;
 
+import org.chromium.base.Callback;
 import org.chromium.base.ObserverList;
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -26,6 +29,7 @@
 import org.chromium.chrome.browser.bookmarks.BookmarkListEntry.ViewType;
 import org.chromium.chrome.browser.bookmarks.BookmarkRow.Location;
 import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.BookmarkRowDisplayPref;
+import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.BookmarkRowSortOrder;
 import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.Observer;
 import org.chromium.chrome.browser.bookmarks.BookmarkUiState.BookmarkUiMode;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
@@ -50,6 +54,7 @@
 import org.chromium.components.browser_ui.widget.selectable_list.SelectionDelegate.SelectionObserver;
 import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.feature_engagement.EventConstants;
+import org.chromium.components.image_fetcher.ImageFetcher;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
 import org.chromium.ui.modelutil.MVCListAdapter.ListItem;
 import org.chromium.ui.modelutil.MVCListAdapter.ModelList;
@@ -292,6 +297,11 @@
                         mBookmarkQueryHandler.buildBookmarkListForParent(getCurrentFolderId()));
             }
         }
+
+        @Override
+        public void onBookmarkRowSortOrderChanged(@BookmarkRowSortOrder int sortOrder) {
+            refresh();
+        }
     };
 
     private final ObserverList<BookmarkUiObserver> mUiObservers = new ObserverList<>();
@@ -322,6 +332,7 @@
     private final int mFetchFaviconSize;
     private final int mDisplayFaviconSize;
     private final Runnable mHideKeyboardRunnable;
+    private final ImageFetcher mImageFetcher;
 
     // Whether this instance has been destroyed.
     private boolean mIsDestroyed;
@@ -345,7 +356,8 @@
             LargeIconBridge largeIconBridge, boolean isDialogUi, boolean isIncognito,
             ObservableSupplierImpl<Boolean> backPressStateSupplier, Profile profile,
             BookmarkUndoController bookmarkUndoController, ModelList modelList,
-            BookmarkUiPrefs bookmarkUiPrefs, Runnable hideKeyboardRunnable) {
+            BookmarkUiPrefs bookmarkUiPrefs, Runnable hideKeyboardRunnable,
+            ImageFetcher imageFetcher) {
         mContext = context;
         mBookmarkModel = bookmarkModel;
         mBookmarkModel.addObserver(mBookmarkModelObserver);
@@ -378,6 +390,7 @@
         mBookmarkUiPrefs = bookmarkUiPrefs;
         mBookmarkUiPrefs.addObserver(mBookmarkUiPrefsObserver);
         mHideKeyboardRunnable = hideKeyboardRunnable;
+        mImageFetcher = imageFetcher;
 
         final @BookmarkRowDisplayPref int displayPref =
                 mBookmarkUiPrefs.getBookmarkRowDisplayPref();
@@ -1070,24 +1083,85 @@
     // ImprovedBookmarkRow methods.
 
     private void resolveIconForBookmark(BookmarkItem item, PropertyModel model) {
+        boolean useImages = BookmarkFeatures.isAndroidImprovedBookmarksEnabled()
+                && mBookmarkUiPrefs.getBookmarkRowDisplayPref() == BookmarkRowDisplayPref.VISUAL;
         if (item.isFolder()) {
-            if (BookmarkFeatures.isAndroidImprovedBookmarksEnabled()
-                    && mBookmarkUiPrefs.getBookmarkRowDisplayPref()
-                            == BookmarkRowDisplayPref.VISUAL) {
+            if (useImages) {
                 model.set(ImprovedBookmarkRowProperties.FOLDER_CHILD_COUNT,
                         BookmarkUtils.getChildCountForDisplay(item.getId(), mBookmarkModel));
 
-                // TODO(crbug.com/1434538): Add images to folder once image service is available.
                 // TODO(crbug.com/1440863): Support reading list special placeholder case.
                 model.set(ImprovedBookmarkRowProperties.FOLDER_DRAWABLES, new Pair<>(null, null));
+                // TODO(crbug.com/1444251): Extract fetching logic to standalone class.
+                resolveFolderDrawables(model, /*primaryDrawable=*/null, /*secondaryDrawable=*/null,
+                        mBookmarkModel.getChildIds(item.getId()), /*index=*/0);
+
             } else {
                 model.set(ImprovedBookmarkRowProperties.BOOKMARK_DRAWABLE,
                         BookmarkUtils.getFolderIcon(mContext, item.getId().getType()));
             }
+        } else {
+            if (useImages) {
+                // TODO(crbug.com/1444251): Extract fetching logic to standalone class.
+                getBookmarkDrawable(item.getId(), (drawable) -> {
+                    if (drawable == null) {
+                        resolveFaviconForBookmark(item, model);
+                    } else {
+                        model.set(ImprovedBookmarkRowProperties.BOOKMARK_DRAWABLE, drawable);
+                    }
+                });
+            } else {
+                resolveFaviconForBookmark(item, model);
+            }
+        }
+    }
 
+    private void resolveFolderDrawables(PropertyModel model, Drawable primaryDrawable,
+            Drawable secondaryDrawable, List<BookmarkId> childIds, int index) {
+        if (index == childIds.size() || (primaryDrawable != null && secondaryDrawable != null)) {
+            model.set(ImprovedBookmarkRowProperties.FOLDER_DRAWABLES,
+                    new Pair<>(primaryDrawable, secondaryDrawable));
             return;
         }
 
+        BookmarkItem item = mBookmarkModel.getBookmarkById(childIds.get(index));
+        getBookmarkDrawable(childIds.get(index), (drawable) -> {
+            Drawable newPrimaryDrawable = primaryDrawable;
+            Drawable newSecondaryDrawable = secondaryDrawable;
+            if (newPrimaryDrawable == null) {
+                newPrimaryDrawable = drawable;
+            } else {
+                newSecondaryDrawable = drawable;
+            }
+            resolveFolderDrawables(
+                    model, newPrimaryDrawable, newSecondaryDrawable, childIds, index + 1);
+        });
+    }
+
+    private void getBookmarkDrawable(BookmarkId id, Callback<BitmapDrawable> callback) {
+        BookmarkItem item = mBookmarkModel.getBookmarkById(id);
+        mBookmarkModel.getImageUrlForBookmark(item.getUrl(), (imageUrl) -> {
+            if (imageUrl == null) {
+                callback.onResult(null);
+                return;
+            }
+
+            Resources res = mContext.getResources();
+            int size = BookmarkUtils.getDisplayIconSize(
+                    res, mBookmarkUiPrefs.getBookmarkRowDisplayPref());
+            mImageFetcher.fetchImage(ImageFetcher.Params.create(imageUrl,
+                                             ImageFetcher.POWER_BOOKMARKS_CLIENT_NAME, size, size),
+                    (image) -> {
+                        if (image == null) {
+                            callback.onResult(null);
+                        } else {
+                            callback.onResult(new BitmapDrawable(res, image));
+                        }
+                    });
+        });
+    }
+
+    private void resolveFaviconForBookmark(BookmarkItem item, PropertyModel model) {
         mLargeIconBridge.getLargeIconForUrl(item.getUrl(), mFetchFaviconSize,
                 (Bitmap icon, int fallbackColor, boolean isFallbackColorDefault, int iconType) -> {
                     Drawable iconDrawable = FaviconUtils.getIconDrawableWithoutFilter(icon,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java
index 8d6e860..a4fdfdc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkModel.java
@@ -14,7 +14,9 @@
 import org.chromium.components.bookmarks.BookmarkType;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A class that encapsulates {@link BookmarkBridge} and provides extra features such as undo, large
@@ -115,9 +117,12 @@
      * bookmark list. The bookmarks are appended at the end.
      */
     public void moveBookmarks(List<BookmarkId> bookmarkIds, BookmarkId newParentId) {
+        Set<BookmarkId> existingChildren = new HashSet<>(getChildIds(newParentId));
         int appendIndex = getChildCount(newParentId);
-        for (int i = 0; i < bookmarkIds.size(); ++i) {
-            moveBookmark(bookmarkIds.get(i), newParentId, appendIndex + i);
+        for (BookmarkId child : bookmarkIds) {
+            if (!existingChildren.contains(child)) {
+                moveBookmark(child, newParentId, appendIndex++);
+            }
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandler.java
index 9c82cd24..c0245a7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandler.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.bookmarks;
 
+import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.BookmarkRowSortOrder;
 import org.chromium.components.bookmarks.BookmarkId;
 import org.chromium.components.bookmarks.BookmarkItem;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
@@ -11,6 +12,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /** New implementation of {@link BookmarkQueryHandler} that expands the root. */
 public class ImprovedBookmarkQueryHandler implements BookmarkQueryHandler {
@@ -49,17 +51,27 @@
 
     @Override
     public List<BookmarkListEntry> buildBookmarkListForParent(BookmarkId parentId) {
-        if (parentId.equals(mBookmarkModel.getRootFolderId())) {
-            return buildBookmarkListForRootView();
+        final List<BookmarkListEntry> bookmarkListEntries;
+        if (Objects.equals(parentId, mBookmarkModel.getRootFolderId())) {
+            bookmarkListEntries = buildBookmarkListForRootView();
         } else {
-            return mBasicBookmarkQueryHandler.buildBookmarkListForParent(parentId);
+            bookmarkListEntries = mBasicBookmarkQueryHandler.buildBookmarkListForParent(parentId);
         }
+
+        // Don't do anything for ReadingList, they're already sorted with a different mechanism.
+        if (!Objects.equals(parentId, mBookmarkModel.getReadingListFolder())) {
+            sortByStoredPref(bookmarkListEntries);
+        }
+
+        return bookmarkListEntries;
     }
 
     @Override
     public List<BookmarkListEntry> buildBookmarkListForSearch(String query) {
-        // TODO(https://crbug.com/1439584): Sort based on selection in toolbar.
-        return mBasicBookmarkQueryHandler.buildBookmarkListForSearch(query);
+        List<BookmarkListEntry> bookmarkListEntries =
+                mBasicBookmarkQueryHandler.buildBookmarkListForSearch(query);
+        sortByStoredPref(bookmarkListEntries);
+        return bookmarkListEntries;
     }
 
     private List<BookmarkListEntry> buildBookmarkListForRootView() {
@@ -71,7 +83,6 @@
         }
         bookmarkListEntries.add(listEntryFromIdForRootView(mBookmarkModel.getReadingListFolder()));
         bookmarkListEntries.add(listEntryFromIdForRootView(mBookmarkModel.getDesktopFolderId()));
-        Collections.sort(bookmarkListEntries, this::compareBookmarkListEntry);
         return bookmarkListEntries;
     }
 
@@ -84,23 +95,42 @@
                 bookmarkItem, powerBookmarkMeta, mBookmarkUiPrefs.getBookmarkRowDisplayPref());
     }
 
-    private int compareBookmarkListEntry(BookmarkListEntry entry1, BookmarkListEntry entry2) {
-        BookmarkItem item1 = entry1.getBookmarkItem();
-        BookmarkItem item2 = entry2.getBookmarkItem();
+    private void sortByStoredPref(List<BookmarkListEntry> bookmarkListEntries) {
+        final @BookmarkRowSortOrder int sortOrder = mBookmarkUiPrefs.getBookmarkRowSortOrder();
+        Collections.sort(
+                bookmarkListEntries, (BookmarkListEntry entry1, BookmarkListEntry entry2) -> {
+                    BookmarkItem item1 = entry1.getBookmarkItem();
+                    BookmarkItem item2 = entry2.getBookmarkItem();
 
-        // Sort folders before urls.
-        int folderComparison = Boolean.compare(item2.isFolder(), item1.isFolder());
-        if (folderComparison != 0) {
-            return folderComparison;
+                    // Sort folders before urls.
+                    int folderComparison = Boolean.compare(item2.isFolder(), item1.isFolder());
+                    if (folderComparison != 0) {
+                        return folderComparison;
+                    }
+
+                    int titleComparison = sortCompare(item1, item2, sortOrder);
+                    if (titleComparison != 0) {
+                        return titleComparison;
+                    }
+
+                    // Fall back to id in case other fields tie. Order will be arbitrary but
+                    // consistent.
+                    return Long.compare(item1.getId().getId(), item2.getId().getId());
+                });
+    }
+
+    private int sortCompare(
+            BookmarkItem item1, BookmarkItem item2, @BookmarkRowSortOrder int sortOrder) {
+        switch (sortOrder) {
+            case BookmarkRowSortOrder.CHRONOLOGICAL:
+                return Long.compare(item1.getDateAdded(), item2.getDateAdded());
+            case BookmarkRowSortOrder.REVERSE_CHRONOLOGICAL:
+                return Long.compare(item2.getDateAdded(), item1.getDateAdded());
+            case BookmarkRowSortOrder.ALPHABETICAL:
+                return item1.getTitle().compareToIgnoreCase(item2.getTitle());
+            case BookmarkRowSortOrder.REVERSE_ALPHABETICAL:
+                return item2.getTitle().compareToIgnoreCase(item1.getTitle());
         }
-
-        // TODO(https://crbug.com/1439584): Sort based on selection in toolbar.
-        int titleComparison = item1.getTitle().compareToIgnoreCase(item2.getTitle());
-        if (titleComparison != 0) {
-            return titleComparison;
-        }
-
-        // Fall back to id in case other fields tie. Order will be arbitrary but consistent.
-        return Long.compare(item1.getId().getId(), item2.getId().getId());
+        return 0;
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
index e844ce11..3fbdb89 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperManager.java
@@ -740,10 +740,6 @@
         return mWidth;
     }
 
-    public int getOrientation() {
-        return mOrientation;
-    }
-
     public @ColorInt int getBackgroundColor() {
         return TabUiThemeUtil.getTabStripBackgroundColor(mContext, mIsIncognito);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
index 0581fa28..3ac723a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/scene_layer/TabStripSceneLayer.java
@@ -5,7 +5,6 @@
 package org.chromium.chrome.browser.compositor.scene_layer;
 
 import android.content.Context;
-import android.os.Build;
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.VisibleForTesting;
@@ -33,9 +32,6 @@
     private static boolean sTestFlag;
     private long mNativePtr;
     private final float mDpToPx;
-    private SceneLayer mChildSceneLayer;
-    private int mOrientation;
-    private int mNumReaddBackground;
 
     public TabStripSceneLayer(Context context) {
         mDpToPx = context.getResources().getDisplayMetrics().density;
@@ -92,27 +88,12 @@
         TabStripSceneLayerJni.get().finishBuildingFrame(mNativePtr, TabStripSceneLayer.this);
     }
 
-    private boolean shouldReadBackground(int orientation) {
-        // Sometimes layer trees do not get updated on rotation on Nexus 10.
-        // This is a workaround that reads the background to prevent it.
-        // See https://crbug.com/503930 for more.
-        if (Build.MODEL == null || !Build.MODEL.contains("Nexus 10")) return false;
-        if (mOrientation != orientation) {
-            // This is a random number. Empirically this is enough.
-            mNumReaddBackground = 10;
-            mOrientation = orientation;
-        }
-        mNumReaddBackground--;
-        return mNumReaddBackground >= 0;
-    }
-
     private void pushButtonsAndBackground(StripLayoutHelperManager layoutHelper,
             ResourceManager resourceManager, float yOffset) {
         final int width = Math.round(layoutHelper.getWidth() * mDpToPx);
         final int height = Math.round(layoutHelper.getHeight() * mDpToPx);
         TabStripSceneLayerJni.get().updateTabStripLayer(mNativePtr, TabStripSceneLayer.this, width,
-                height, yOffset * mDpToPx, shouldReadBackground(layoutHelper.getOrientation()),
-                layoutHelper.getBackgroundColor());
+                height, yOffset * mDpToPx, layoutHelper.getBackgroundColor());
 
         TintedCompositorButton newTabButton = layoutHelper.getNewTabButton();
         CompositorButton modelSelectorButton = layoutHelper.getModelSelectorButton();
@@ -201,8 +182,7 @@
                 long nativeTabStripSceneLayer, TabStripSceneLayer caller, boolean visible);
         void finishBuildingFrame(long nativeTabStripSceneLayer, TabStripSceneLayer caller);
         void updateTabStripLayer(long nativeTabStripSceneLayer, TabStripSceneLayer caller,
-                int width, int height, float yOffset, boolean shouldReadBackground,
-                @ColorInt int backgroundColor);
+                int width, int height, float yOffset, @ColorInt int backgroundColor);
         void updateNewTabButton(long nativeTabStripSceneLayer, TabStripSceneLayer caller,
                 int resourceId, int backgroundResourceId, float x, float y, float touchTargetOffset,
                 boolean visible, int tint, int backgroundTint, float buttonAlpha,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkModelTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkModelTest.java
index db161003..beaa7f3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkModelTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkModelTest.java
@@ -26,6 +26,8 @@
 import org.chromium.url.GURL;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.Semaphore;
@@ -154,12 +156,36 @@
     @SmallTest
     @UiThreadTest
     @Feature({"Bookmark"})
+    public void testMoveBookmarksMixed() {
+        // Inspired by https://crbug.com/1441847 where a move during a search would have bookmarks
+        // from a mixed set of parent folders. Need to be able to handle interleaving url bookmarks
+        // where only some of which are in the same destination folder.
+        BookmarkId folderA = mBookmarkModel.addFolder(mMobileNode, 0, "fa");
+        BookmarkId folderC = mBookmarkModel.addFolder(mMobileNode, 0, "fc");
+        BookmarkId bookmarkA = addBookmark(folderA, 0, "a", A_COM);
+        BookmarkId bookmarkB = addBookmark(folderA, 1, "b", B_COM);
+        BookmarkId bookmarkC = addBookmark(folderC, 0, "c", C_COM);
+
+        List<BookmarkId> movedBookmarks = new ArrayList<>();
+        movedBookmarks.add(bookmarkA);
+        movedBookmarks.add(bookmarkC);
+        movedBookmarks.add(bookmarkB);
+        mBookmarkModel.moveBookmarks(movedBookmarks, folderC);
+
+        verifyBookmarkListNoOrder(mBookmarkModel.getChildIds(folderA), Collections.emptyList());
+        verifyBookmarkListNoOrder(mBookmarkModel.getChildIds(folderC), movedBookmarks);
+    }
+
+    @Test
+    @SmallTest
+    @UiThreadTest
+    @Feature({"Bookmark"})
     public void testDeleteBookmarks() {
         BookmarkId bookmarkA = addBookmark(mDesktopNode, 0, "a", A_COM);
         BookmarkId bookmarkB = addBookmark(mOtherNode, 0, "b", B_COM);
         BookmarkId bookmarkC = addBookmark(mMobileNode, 0, "c", C_COM);
 
-        // Dete a single bookmark
+        // Delete a single bookmark.
         mBookmarkModel.deleteBookmarks(bookmarkA);
         Assert.assertNull(mBookmarkModel.getBookmarkById(bookmarkA));
         Assert.assertNotNull(mBookmarkModel.getBookmarkById(bookmarkB));
@@ -294,8 +320,8 @@
      * Before using this helper method, always make sure @param listToVerify does not contain
      * duplicates.
      */
-    private void verifyBookmarkListNoOrder(List<BookmarkId> listToVerify,
-            HashSet<BookmarkId> expectedIds) {
+    private void verifyBookmarkListNoOrder(
+            List<BookmarkId> listToVerify, Collection<BookmarkId> expectedIds) {
         HashSet<BookmarkId> expectedIdsCopy = new HashSet<>(expectedIds);
         Assert.assertEquals(expectedIdsCopy.size(), listToVerify.size());
         for (BookmarkId id : listToVerify) {
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
index 2678725..3e2e3a67 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/BookmarkManagerMediatorTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -42,6 +43,7 @@
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
 
+import org.chromium.base.Callback;
 import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
@@ -71,6 +73,7 @@
 import org.chromium.components.favicon.LargeIconBridge;
 import org.chromium.components.favicon.LargeIconBridge.LargeIconCallback;
 import org.chromium.components.feature_engagement.Tracker;
+import org.chromium.components.image_fetcher.ImageFetcher;
 import org.chromium.components.payments.CurrencyFormatter;
 import org.chromium.components.payments.CurrencyFormatterJni;
 import org.chromium.components.power_bookmarks.PowerBookmarkMeta;
@@ -136,7 +139,7 @@
     @Mock
     private BookmarkUndoController mBookmarkUndoController;
     @Mock
-    AccessibilityManager mAccessibilityManager;
+    private AccessibilityManager mAccessibilityManager;
     @Mock
     private Runnable mHideKeyboardRunnable;
     @Mock
@@ -145,6 +148,8 @@
     private CurrencyFormatter.Natives mCurrencyFormatterJniMock;
     @Mock
     private Tracker mTracker;
+    @Mock
+    private ImageFetcher mImageFetcher;
 
     @Captor
     private ArgumentCaptor<BookmarkModelObserver> mBookmarkModelObserverArgumentCaptor;
@@ -214,6 +219,7 @@
 
             // Setup BookmarkModel.
             doReturn(mRootFolderId).when(mBookmarkModel).getRootFolderId();
+            doReturn(mReadingListFolderId).when(mBookmarkModel).getReadingListFolder();
             doReturn(true).when(mBookmarkModel).doesBookmarkExist(any());
             doReturn(Arrays.asList(mFolderId2, mFolderId3))
                     .when(mBookmarkModel)
@@ -265,13 +271,30 @@
             doReturn(mIdentityManager).when(mSigninManager).getIdentityManager();
             AccountManagerFacadeProvider.setInstanceForTests(mAccountManagerFacade);
 
+            // Setup image fetching.
+            doAnswer((invocation) -> {
+                Callback<GURL> callback = invocation.getArgument(1);
+                callback.onResult(JUnitTestGURLs.getGURL(JUnitTestGURLs.EXAMPLE_URL));
+                return null;
+            })
+                    .when(mBookmarkModel)
+                    .getImageUrlForBookmark(any(), any());
+            doAnswer((invocation) -> {
+                Callback<Bitmap> callback = invocation.getArgument(1);
+                callback.onResult(mBitmap);
+                return null;
+            })
+                    .when(mImageFetcher)
+                    .fetchImage(any(), any());
+
             mDragReorderableRecyclerViewAdapter =
                     spy(new DragReorderableRecyclerViewAdapter(mActivity, mModelList));
             mMediator = new BookmarkManagerMediator(mActivity, mBookmarkModel, mBookmarkOpener,
                     mSelectableListLayout, mSelectionDelegate, mRecyclerView,
                     mDragReorderableRecyclerViewAdapter, mLargeIconBridge, /*isDialogUi=*/true,
                     /*isIncognito=*/false, mBackPressStateSupplier, mProfile,
-                    mBookmarkUndoController, mModelList, mBookmarkUiPrefs, mHideKeyboardRunnable);
+                    mBookmarkUndoController, mModelList, mBookmarkUiPrefs, mHideKeyboardRunnable,
+                    mImageFetcher);
             mMediator.addUiObserver(mBookmarkUiObserver);
         });
     }
@@ -491,6 +514,31 @@
         assertEquals(
                 "https://www.example.com/", model.get(ImprovedBookmarkRowProperties.DESCRIPTION));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.BOOKMARK_DRAWABLE));
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.ANDROID_IMPROVED_BOOKMARKS)
+    public void testBuildImprovedBookmarkRow_Visual() {
+        finishLoading();
+        mMediator.openFolder(mFolderId2);
+        mBookmarkUiPrefs.setBookmarkRowDisplayPref(BookmarkRowDisplayPref.VISUAL);
+        assertEquals(1, mModelList.size());
+
+        ListItem item = mMediator.buildImprovedBookmarkRow(
+                BookmarkListEntry.createBookmarkEntry(
+                        mBookmarkItem21, null, mBookmarkUiPrefs.getBookmarkRowDisplayPref()),
+                0);
+        assertEquals(ViewType.IMPROVED_BOOKMARK_VISUAL, item.type);
+
+        PropertyModel model = item.model;
+        assertNotNull(model);
+        assertEquals(mBookmarkItem21,
+                model.get(BookmarkManagerProperties.BOOKMARK_LIST_ENTRY).getBookmarkItem());
+        assertEquals(mBookmarkId21, model.get(BookmarkManagerProperties.BOOKMARK_ID));
+        assertEquals(mBookmarkItem21.getTitle(), model.get(ImprovedBookmarkRowProperties.TITLE));
+        assertEquals(
+                "https://www.example.com/", model.get(ImprovedBookmarkRowProperties.DESCRIPTION));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.BOOKMARK_DRAWABLE));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.POPUP_LISTENER));
         assertEquals(false, model.get(ImprovedBookmarkRowProperties.SELECTION_ACTIVE));
         assertEquals(false, model.get(ImprovedBookmarkRowProperties.DRAG_ENABLED));
@@ -554,4 +602,108 @@
         assertEquals(true, model.get(ImprovedBookmarkRowProperties.EDITABLE));
         assertNotNull(model.get(ImprovedBookmarkRowProperties.OPEN_BOOKMARK_CALLBACK));
     }
-}
\ No newline at end of file
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.ANDROID_IMPROVED_BOOKMARKS)
+    public void testBuildImprovedBookmarkRow_FolderVisual() {
+        finishLoading();
+        mMediator.openFolder(mFolderId1);
+        mBookmarkUiPrefs.setBookmarkRowDisplayPref(BookmarkRowDisplayPref.VISUAL);
+        assertEquals(2, mModelList.size());
+
+        ListItem item = mModelList.get(0);
+        assertEquals(ViewType.IMPROVED_BOOKMARK_VISUAL, item.type);
+
+        PropertyModel model = item.model;
+        assertNotNull(model);
+        assertEquals(mFolderItem2,
+                model.get(BookmarkManagerProperties.BOOKMARK_LIST_ENTRY).getBookmarkItem());
+        assertEquals(mFolderId2, model.get(BookmarkManagerProperties.BOOKMARK_ID));
+        assertEquals(mFolderItem2.getTitle(), model.get(ImprovedBookmarkRowProperties.TITLE));
+        assertEquals("1 bookmark", model.get(ImprovedBookmarkRowProperties.DESCRIPTION));
+        assertNull(model.get(ImprovedBookmarkRowProperties.BOOKMARK_DRAWABLE));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.FOLDER_DRAWABLES));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.POPUP_LISTENER));
+        assertEquals(false, model.get(ImprovedBookmarkRowProperties.SELECTION_ACTIVE));
+        assertEquals(false, model.get(ImprovedBookmarkRowProperties.DRAG_ENABLED));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.LIST_MENU));
+        assertEquals(true, model.get(ImprovedBookmarkRowProperties.EDITABLE));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.OPEN_BOOKMARK_CALLBACK));
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.ANDROID_IMPROVED_BOOKMARKS)
+    public void testBuildImprovedBookmarkRow_FolderVisual_NullUrl() {
+        doAnswer((invocation) -> {
+            Callback<GURL> callback = invocation.getArgument(1);
+            callback.onResult(null);
+            return null;
+        })
+                .when(mBookmarkModel)
+                .getImageUrlForBookmark(any(), any());
+
+        finishLoading();
+        mMediator.openFolder(mFolderId1);
+        mBookmarkUiPrefs.setBookmarkRowDisplayPref(BookmarkRowDisplayPref.VISUAL);
+        assertEquals(2, mModelList.size());
+
+        ListItem item = mModelList.get(0);
+        assertEquals(ViewType.IMPROVED_BOOKMARK_VISUAL, item.type);
+
+        PropertyModel model = item.model;
+        assertNotNull(model);
+        assertEquals(mFolderItem2,
+                model.get(BookmarkManagerProperties.BOOKMARK_LIST_ENTRY).getBookmarkItem());
+        assertEquals(mFolderId2, model.get(BookmarkManagerProperties.BOOKMARK_ID));
+        assertEquals(mFolderItem2.getTitle(), model.get(ImprovedBookmarkRowProperties.TITLE));
+        assertEquals("1 bookmark", model.get(ImprovedBookmarkRowProperties.DESCRIPTION));
+        assertNull(model.get(ImprovedBookmarkRowProperties.BOOKMARK_DRAWABLE));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.FOLDER_DRAWABLES));
+        assertNull(model.get(ImprovedBookmarkRowProperties.FOLDER_DRAWABLES).first);
+        assertNull(model.get(ImprovedBookmarkRowProperties.FOLDER_DRAWABLES).second);
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.POPUP_LISTENER));
+        assertEquals(false, model.get(ImprovedBookmarkRowProperties.SELECTION_ACTIVE));
+        assertEquals(false, model.get(ImprovedBookmarkRowProperties.DRAG_ENABLED));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.LIST_MENU));
+        assertEquals(true, model.get(ImprovedBookmarkRowProperties.EDITABLE));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.OPEN_BOOKMARK_CALLBACK));
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.ANDROID_IMPROVED_BOOKMARKS)
+    public void testBuildImprovedBookmarkRow_FolderVisual_NullImage() {
+        doAnswer((invocation) -> {
+            Callback<Bitmap> callback = invocation.getArgument(1);
+            callback.onResult(null);
+            return null;
+        })
+                .when(mImageFetcher)
+                .fetchImage(any(), any());
+
+        finishLoading();
+        mMediator.openFolder(mFolderId1);
+        mBookmarkUiPrefs.setBookmarkRowDisplayPref(BookmarkRowDisplayPref.VISUAL);
+        assertEquals(2, mModelList.size());
+
+        ListItem item = mModelList.get(0);
+        assertEquals(ViewType.IMPROVED_BOOKMARK_VISUAL, item.type);
+
+        PropertyModel model = item.model;
+        assertNotNull(model);
+        assertEquals(mFolderItem2,
+                model.get(BookmarkManagerProperties.BOOKMARK_LIST_ENTRY).getBookmarkItem());
+        assertEquals(mFolderId2, model.get(BookmarkManagerProperties.BOOKMARK_ID));
+        assertEquals(mFolderItem2.getTitle(), model.get(ImprovedBookmarkRowProperties.TITLE));
+        assertEquals("1 bookmark", model.get(ImprovedBookmarkRowProperties.DESCRIPTION));
+        assertNull(model.get(ImprovedBookmarkRowProperties.BOOKMARK_DRAWABLE));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.FOLDER_DRAWABLES));
+        assertNull(model.get(ImprovedBookmarkRowProperties.FOLDER_DRAWABLES).first);
+        assertNull(model.get(ImprovedBookmarkRowProperties.FOLDER_DRAWABLES).second);
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.POPUP_LISTENER));
+        assertEquals(false, model.get(ImprovedBookmarkRowProperties.SELECTION_ACTIVE));
+        assertEquals(false, model.get(ImprovedBookmarkRowProperties.DRAG_ENABLED));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.LIST_MENU));
+        assertEquals(true, model.get(ImprovedBookmarkRowProperties.EDITABLE));
+        assertNotNull(model.get(ImprovedBookmarkRowProperties.OPEN_BOOKMARK_CALLBACK));
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandlerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandlerTest.java
index 65024020..7ab47c4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandlerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/ImprovedBookmarkQueryHandlerTest.java
@@ -5,19 +5,27 @@
 package org.chromium.chrome.browser.bookmarks;
 
 import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
 import static org.chromium.chrome.browser.bookmarks.SharedBookmarkModelMocks.DESKTOP_BOOKMARK_ID;
 import static org.chromium.chrome.browser.bookmarks.SharedBookmarkModelMocks.FOLDER_BOOKMARK_ID_A;
 import static org.chromium.chrome.browser.bookmarks.SharedBookmarkModelMocks.READING_LIST_BOOKMARK_ID;
 import static org.chromium.chrome.browser.bookmarks.SharedBookmarkModelMocks.ROOT_BOOKMARK_ID;
 import static org.chromium.chrome.browser.bookmarks.SharedBookmarkModelMocks.URL_BOOKMARK_ID_A;
+import static org.chromium.chrome.browser.bookmarks.SharedBookmarkModelMocks.URL_BOOKMARK_ID_B;
+import static org.chromium.chrome.browser.bookmarks.SharedBookmarkModelMocks.URL_BOOKMARK_ID_C;
 import static org.chromium.chrome.browser.bookmarks.SharedBookmarkModelMocks.URL_BOOKMARK_ID_D;
 import static org.chromium.chrome.browser.bookmarks.SharedBookmarkModelMocks.URL_BOOKMARK_ID_E;
 
+import androidx.annotation.Nullable;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatchers;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
@@ -25,10 +33,14 @@
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.chrome.browser.bookmarks.BookmarkUiPrefs.BookmarkRowSortOrder;
 import org.chromium.chrome.browser.feature_engagement.TrackerFactory;
 import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.bookmarks.BookmarkId;
+import org.chromium.components.bookmarks.BookmarkItem;
 import org.chromium.components.feature_engagement.Tracker;
 
+import java.util.Arrays;
 import java.util.List;
 
 /** Unit tests for {@link ImprovedBookmarkQueryHandler}. */
@@ -60,14 +72,89 @@
     }
 
     @Test
-    public void testBuildBookmarkListForParent_rootFolder() {
+    public void testBuildBookmarkListForParent_rootFolder_chronological() {
+        doReturn(BookmarkRowSortOrder.CHRONOLOGICAL)
+                .when(mBookmarkUiPrefs)
+                .getBookmarkRowSortOrder();
+
         List<BookmarkListEntry> result = mHandler.buildBookmarkListForParent(ROOT_BOOKMARK_ID);
-        assertEquals(6, result.size());
-        assertEquals(DESKTOP_BOOKMARK_ID, result.get(0).getBookmarkItem().getId());
-        assertEquals(FOLDER_BOOKMARK_ID_A, result.get(1).getBookmarkItem().getId());
-        assertEquals(READING_LIST_BOOKMARK_ID, result.get(2).getBookmarkItem().getId());
-        assertEquals(URL_BOOKMARK_ID_A, result.get(3).getBookmarkItem().getId());
-        assertEquals(URL_BOOKMARK_ID_D, result.get(4).getBookmarkItem().getId());
-        assertEquals(URL_BOOKMARK_ID_E, result.get(5).getBookmarkItem().getId());
+        List<BookmarkId> expected = Arrays.asList(DESKTOP_BOOKMARK_ID, READING_LIST_BOOKMARK_ID,
+                FOLDER_BOOKMARK_ID_A, URL_BOOKMARK_ID_A, URL_BOOKMARK_ID_D, URL_BOOKMARK_ID_E);
+        verifyBookmarkIds(expected, result);
+    }
+
+    @Test
+    public void testBuildBookmarkListForParent_rootFolder_reverseChronological() {
+        doReturn(BookmarkRowSortOrder.REVERSE_CHRONOLOGICAL)
+                .when(mBookmarkUiPrefs)
+                .getBookmarkRowSortOrder();
+
+        List<BookmarkListEntry> result = mHandler.buildBookmarkListForParent(ROOT_BOOKMARK_ID);
+        List<BookmarkId> expected = Arrays.asList(FOLDER_BOOKMARK_ID_A, READING_LIST_BOOKMARK_ID,
+                DESKTOP_BOOKMARK_ID, URL_BOOKMARK_ID_E, URL_BOOKMARK_ID_D, URL_BOOKMARK_ID_A);
+        verifyBookmarkIds(expected, result);
+    }
+
+    @Test
+    public void testBuildBookmarkListForParent_rootFolder_alphabetical() {
+        doReturn(BookmarkRowSortOrder.ALPHABETICAL)
+                .when(mBookmarkUiPrefs)
+                .getBookmarkRowSortOrder();
+
+        List<BookmarkListEntry> result = mHandler.buildBookmarkListForParent(ROOT_BOOKMARK_ID);
+        List<BookmarkId> expected = Arrays.asList(DESKTOP_BOOKMARK_ID, FOLDER_BOOKMARK_ID_A,
+                READING_LIST_BOOKMARK_ID, URL_BOOKMARK_ID_A, URL_BOOKMARK_ID_D, URL_BOOKMARK_ID_E);
+        verifyBookmarkIds(expected, result);
+    }
+
+    @Test
+    public void testBuildBookmarkListForParent_rootFolder_reverseAlphabetical() {
+        doReturn(BookmarkRowSortOrder.REVERSE_ALPHABETICAL)
+                .when(mBookmarkUiPrefs)
+                .getBookmarkRowSortOrder();
+
+        List<BookmarkListEntry> result = mHandler.buildBookmarkListForParent(ROOT_BOOKMARK_ID);
+        List<BookmarkId> expected = Arrays.asList(READING_LIST_BOOKMARK_ID, FOLDER_BOOKMARK_ID_A,
+                DESKTOP_BOOKMARK_ID, URL_BOOKMARK_ID_E, URL_BOOKMARK_ID_D, URL_BOOKMARK_ID_A);
+        verifyBookmarkIds(expected, result);
+    }
+
+    @Test
+    public void testBuildBookmarkListForParent_readingList() {
+        List<BookmarkListEntry> result =
+                mHandler.buildBookmarkListForParent(READING_LIST_BOOKMARK_ID);
+        List<BookmarkId> expected = Arrays.asList(null, URL_BOOKMARK_ID_E, null, URL_BOOKMARK_ID_D);
+        verifyBookmarkIds(expected, result);
+        verify(mBookmarkUiPrefs, never()).getBookmarkRowSortOrder();
+    }
+
+    @Test
+    public void testBuildBookmarkListForParent() {
+        doReturn(BookmarkRowSortOrder.ALPHABETICAL)
+                .when(mBookmarkUiPrefs)
+                .getBookmarkRowSortOrder();
+        // Order these initially in a non-alphabetical order.
+        List<BookmarkId> queryIds = Arrays.asList(URL_BOOKMARK_ID_D, URL_BOOKMARK_ID_A,
+                URL_BOOKMARK_ID_C, URL_BOOKMARK_ID_B, URL_BOOKMARK_ID_E);
+        doReturn(queryIds)
+                .when(mBookmarkModel)
+                .searchBookmarks(ArgumentMatchers.any(), ArgumentMatchers.anyInt());
+
+        List<BookmarkListEntry> result = mHandler.buildBookmarkListForSearch("Url");
+        List<BookmarkId> expected = Arrays.asList(URL_BOOKMARK_ID_A, URL_BOOKMARK_ID_B,
+                URL_BOOKMARK_ID_C, URL_BOOKMARK_ID_D, URL_BOOKMARK_ID_E);
+        verifyBookmarkIds(expected, result);
+    }
+
+    private void verifyBookmarkIds(
+            List<BookmarkId> expectedList, List<BookmarkListEntry> actualList) {
+        assertEquals(expectedList.size(), actualList.size());
+        for (int i = 0; i < expectedList.size(); ++i) {
+            final @Nullable BookmarkId expectedId = expectedList.get(i);
+            final @Nullable BookmarkItem actualBookmarkItem = actualList.get(i).getBookmarkItem();
+            final @Nullable BookmarkId actualBookmarkId =
+                    actualBookmarkItem == null ? null : actualBookmarkItem.getId();
+            assertEquals("Mismatch at index: " + i, expectedId, actualBookmarkId);
+        }
     }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/SharedBookmarkModelMocks.java b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/SharedBookmarkModelMocks.java
index 06946c0..367171391 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/SharedBookmarkModelMocks.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/bookmarks/SharedBookmarkModelMocks.java
@@ -41,26 +41,26 @@
     static final GURL URL_D = new GURL("https://www.d.com/");
     static final GURL URL_E = new GURL("https://www.e.com/");
 
-    static final BookmarkItem DESKTOP_BOOKMARK_ITEM = new BookmarkItem(DESKTOP_BOOKMARK_ID,
-            "Bookmarks bar", null, true, ROOT_BOOKMARK_ID, false, false, 0, false);
-    static final BookmarkItem OTHER_BOOKMARK_ITEM = new BookmarkItem(OTHER_BOOKMARK_ID,
-            "Other bookmarks", null, true, ROOT_BOOKMARK_ID, false, false, 0, false);
-    static final BookmarkItem MOBILE_BOOKMARK_ITEM = new BookmarkItem(MOBILE_BOOKMARK_ID,
-            "Mobile bookmarks", null, true, ROOT_BOOKMARK_ID, false, false, 0, false);
-    static final BookmarkItem READING_LIST_ITEM = new BookmarkItem(READING_LIST_BOOKMARK_ID,
-            "Reading list", null, true, ROOT_BOOKMARK_ID, false, false, 0, false);
-    static final BookmarkItem FOLDER_ITEM_A = new BookmarkItem(FOLDER_BOOKMARK_ID_A, "Folder A",
-            null, true, MOBILE_BOOKMARK_ID, true, false, 0, false);
-    static final BookmarkItem URL_ITEM_A = new BookmarkItem(
-            URL_BOOKMARK_ID_A, "Url A", URL_A, false, MOBILE_BOOKMARK_ID, true, false, 0, false);
-    static final BookmarkItem URL_ITEM_B = new BookmarkItem(
-            URL_BOOKMARK_ID_B, "Url B", URL_B, false, FOLDER_BOOKMARK_ID_A, true, false, 0, false);
-    static final BookmarkItem URL_ITEM_C = new BookmarkItem(
-            URL_BOOKMARK_ID_C, "Url C", URL_C, false, FOLDER_BOOKMARK_ID_A, true, false, 0, false);
-    static final BookmarkItem URL_ITEM_D = new BookmarkItem(URL_BOOKMARK_ID_D, "Url D", URL_D,
-            false, READING_LIST_BOOKMARK_ID, true, false, 0, true);
-    static final BookmarkItem URL_ITEM_E = new BookmarkItem(URL_BOOKMARK_ID_E, "Url E", URL_E,
-            false, READING_LIST_BOOKMARK_ID, true, false, 0, false);
+    static final BookmarkItem DESKTOP_BOOKMARK_ITEM =
+            makeFolderItem(DESKTOP_BOOKMARK_ID, "Bookmarks bar", ROOT_BOOKMARK_ID);
+    static final BookmarkItem OTHER_BOOKMARK_ITEM =
+            makeFolderItem(OTHER_BOOKMARK_ID, "Other bookmarks", ROOT_BOOKMARK_ID);
+    static final BookmarkItem MOBILE_BOOKMARK_ITEM =
+            makeFolderItem(MOBILE_BOOKMARK_ID, "Mobile bookmarks", ROOT_BOOKMARK_ID);
+    static final BookmarkItem READING_LIST_ITEM =
+            makeFolderItem(READING_LIST_BOOKMARK_ID, "Reading list", ROOT_BOOKMARK_ID);
+    static final BookmarkItem FOLDER_ITEM_A =
+            makeFolderItem(FOLDER_BOOKMARK_ID_A, "Folder A", MOBILE_BOOKMARK_ID);
+    static final BookmarkItem URL_ITEM_A =
+            makeUrlItem(URL_BOOKMARK_ID_A, "Url A", URL_A, MOBILE_BOOKMARK_ID);
+    static final BookmarkItem URL_ITEM_B =
+            makeUrlItem(URL_BOOKMARK_ID_B, "Url B", URL_B, FOLDER_BOOKMARK_ID_A);
+    static final BookmarkItem URL_ITEM_C =
+            makeUrlItem(URL_BOOKMARK_ID_C, "Url C", URL_C, FOLDER_BOOKMARK_ID_A);
+    static final BookmarkItem URL_ITEM_D =
+            makeUrlItemWithRead(URL_BOOKMARK_ID_D, "Url D", URL_D, READING_LIST_BOOKMARK_ID, true);
+    static final BookmarkItem URL_ITEM_E =
+            makeUrlItem(URL_BOOKMARK_ID_E, "Url E", URL_E, READING_LIST_BOOKMARK_ID);
 
     public static void initMocks(BookmarkModel bookmarkModel) {
         doReturn(ROOT_BOOKMARK_ID).when(bookmarkModel).getRootFolderId();
@@ -101,4 +101,22 @@
                 .when(bookmarkModel)
                 .getChildIds(READING_LIST_BOOKMARK_ID);
     }
+
+    private static BookmarkItem makeUrlItem(
+            BookmarkId id, String title, GURL url, BookmarkId parentId) {
+        return makeUrlItemWithRead(id, title, url, parentId, false);
+    }
+
+    private static BookmarkItem makeUrlItemWithRead(
+            BookmarkId id, String title, GURL url, BookmarkId parentId, boolean read) {
+        long dateAdded = id.getId();
+        return new BookmarkItem(id, title, url, false, parentId, false, false, dateAdded, read);
+    }
+
+    private static BookmarkItem makeFolderItem(BookmarkId id, String title, BookmarkId parentId) {
+        boolean isEditable = ROOT_BOOKMARK_ID.equals(parentId);
+        long dateAdded = id.getId();
+        return new BookmarkItem(
+                id, title, null, true, parentId, isEditable, false, dateAdded, false);
+    }
 }
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index 850e42d6..115b0d2 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -274,6 +274,9 @@
   <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_SETTING_DESCRIPTION" desc="Description for the memory saver mode setting">
     When on, Chromium frees up memory from inactive tabs. This gives active tabs and other apps more computer resources and keeps Chromium fast. Your inactive tabs automatically become active again when you go back to them.
   </message>
+  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_HEURISTICS_LABEL" desc="Label for the memory saver mode to discard tabs based on a set of heuristics" translateable="false">
+    Chromium decides when a tab becomes inactive
+  </message>
   <message name="IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_SETTING_DESCRIPTION" desc="Description for the energy saver mode setting">
     When on, Chromium conserves battery power by limiting background activity and visual effects, such as smooth scrolling and video frame rates.
   </message>
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index 2fd0bbe..1397730 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -288,6 +288,9 @@
   <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_SETTING_DESCRIPTION" desc="Description for the memory saver mode setting">
     When on, Chrome frees up memory from inactive tabs. This gives active tabs and other apps more computer resources and keeps Chrome fast. Your inactive tabs automatically become active again when you go back to them.
   </message>
+  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_HEURISTICS_LABEL" desc="Label for the memory saver mode to discard tabs based on a set of heuristics" translateable="false">
+    Chrome decides when a tab becomes inactive
+  </message>
   <message name="IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_SETTING_DESCRIPTION" desc="Description for the energy saver mode setting">
     When on, Chrome conserves battery power by limiting background activity and visual effects, such as smooth scrolling and video frame rates.
   </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index c47d1f5..3a4cf77 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -1531,6 +1531,12 @@
   <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_SETTING" desc="Memory saver mode setting label. This mode allows the browser to save memory from background tabs">
     Memory Saver
   </message>
+  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_ON_TIMER_LABEL" desc="Label for the memory saver mode to discard tabs on a timer" translateable="false">
+    Select when your tabs become inactive
+  </message>
+  <message name="IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_RADIO_GROUP_ARIA_LABEL" desc="Accessibility label which will be read by screen readers when focus enters the memory saver mode radio group. When memory saver mode is enabled, this radio group will be expanded and allow the user to choose between different tab discarding policies." translateable="false">
+    Memory saver options
+  </message>
   <message name="IDS_SETTINGS_PERFORMANCE_TAB_DISCARDING_EXCEPTIONS_ADD_BUTTON_ARIA_LABEL" desc="Accessibility label which will be read by screen readers when focusing the tab discarding exceptions list add button. Sites added to the list will not be discarded by memory saver.">
     Add to the "always keep these sites active" list
   </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_REVIEW_EXTENSIONS.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_REVIEW_EXTENSIONS.png.sha1
index 3362830a..5833457 100644
--- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_REVIEW_EXTENSIONS.png.sha1
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_SAFETY_CHECK_REVIEW_EXTENSIONS.png.sha1
@@ -1 +1 @@
-IDS_SETTINGS_SAFETY_CHECK_REVIEW_EXTENSIONS.png.sha1
\ No newline at end of file
+c3ca23ef4ed925e928c5224260ed47dcebd0c57d
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index bf8b031f..b900896 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -6079,6 +6079,10 @@
       "enterprise/connectors/device_trust/signals/signals_service_factory.h",
       "enterprise/connectors/device_trust/signals/signals_service_impl.cc",
       "enterprise/connectors/device_trust/signals/signals_service_impl.h",
+      "enterprise/signals/user_delegate_impl.cc",
+      "enterprise/signals/user_delegate_impl.h",
+      "enterprise/signals/user_permission_service_factory.cc",
+      "enterprise/signals/user_permission_service_factory.h",
 
       # This policy is only supported on non-lacros desktop
       "performance_manager/public/user_tuning/high_efficiency_policy_handler.h",
@@ -6124,6 +6128,11 @@
       "enterprise/connectors/analysis/local_binary_upload_service.h",
       "enterprise/connectors/analysis/local_binary_upload_service_factory.cc",
       "enterprise/connectors/analysis/local_binary_upload_service_factory.h",
+      "enterprise/connectors/device_trust/attestation/browser/attester.h",
+      "enterprise/connectors/device_trust/attestation/browser/device_attester.cc",
+      "enterprise/connectors/device_trust/attestation/browser/device_attester.h",
+      "enterprise/connectors/device_trust/attestation/browser/profile_attester.cc",
+      "enterprise/connectors/device_trust/attestation/browser/profile_attester.h",
       "enterprise/connectors/device_trust/attestation/desktop/crypto_utility.cc",
       "enterprise/connectors/device_trust/attestation/desktop/crypto_utility.h",
       "enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service.cc",
@@ -6148,10 +6157,6 @@
       "enterprise/signals/signals_aggregator_factory.h",
       "enterprise/signals/system_signals_service_host_factory.cc",
       "enterprise/signals/system_signals_service_host_factory.h",
-      "enterprise/signals/user_delegate_impl.cc",
-      "enterprise/signals/user_delegate_impl.h",
-      "enterprise/signals/user_permission_service_factory.cc",
-      "enterprise/signals/user_permission_service_factory.h",
       "policy/cloud/profile_token_policy_web_signin_service.cc",
       "policy/cloud/profile_token_policy_web_signin_service.h",
       "policy/cloud/profile_token_policy_web_signin_service_factory.cc",
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
index 2f5abdd..9ca39893 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
+++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.cc
@@ -116,7 +116,6 @@
                                              jint width,
                                              jint height,
                                              jfloat y_offset,
-                                             jboolean should_readd_background,
                                              jint background_color) {
   gfx::RectF content(0, y_offset, width, height);
   layer()->SetPosition(gfx::PointF(0, y_offset));
@@ -127,20 +126,6 @@
   // Content tree should not be affected by tab strip scene layer visibility.
   if (content_tree_)
     content_tree_->layer()->SetPosition(gfx::PointF(0, -y_offset));
-
-  // Make sure tab strip changes are committed after rotating the device.
-  // See https://crbug.com/503930 for more details.
-  // InsertChild() forces the tree sync, which seems to fix the problem.
-  // Note that this is a workaround.
-  // TODO(changwan): find out why the update is not committed after rotation.
-  if (should_readd_background) {
-    int background_index = 0;
-    if (content_tree_ && content_tree_->layer()) {
-      background_index = 1;
-    }
-    DCHECK(layer()->children()[background_index] == tab_strip_layer_);
-    layer()->InsertChild(tab_strip_layer_, background_index);
-  }
 }
 
 void TabStripSceneLayer::UpdateNewTabButton(
diff --git a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h
index ce79473..ea7275a8 100644
--- a/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h
+++ b/chrome/browser/android/compositor/scene_layer/tab_strip_scene_layer.h
@@ -55,7 +55,6 @@
                            jint width,
                            jint height,
                            jfloat y_offset,
-                           jboolean should_readd_background,
                            jint background_color);
 
   void UpdateNewTabButton(
diff --git a/chrome/browser/autocomplete/autocomplete_scoring_model_service_factory.cc b/chrome/browser/autocomplete/autocomplete_scoring_model_service_factory.cc
index a2249c7..47660316 100644
--- a/chrome/browser/autocomplete/autocomplete_scoring_model_service_factory.cc
+++ b/chrome/browser/autocomplete/autocomplete_scoring_model_service_factory.cc
@@ -30,10 +30,12 @@
 AutocompleteScoringModelServiceFactory::AutocompleteScoringModelServiceFactory()
     : ProfileKeyedServiceFactory(
           "AutocompleteScoringModelService",
+          // This service is available for the regular profile in both the
+          // original and the OTR modes.
           ProfileSelections::Builder()
-              .WithRegular(ProfileSelection::kOriginalOnly)
+              .WithRegular(ProfileSelection::kOwnInstance)
               // TODO(crbug.com/1418376): Check if this service is needed in
-              // Guest mode.
+              // Guest mode (likely not since local history is unavailable).
               .WithGuest(ProfileSelection::kOriginalOnly)
               .Build()) {
   DependsOn(OptimizationGuideKeyedServiceFactory::GetInstance());
diff --git a/chrome/browser/autofill/android/save_update_address_profile_prompt_controller_unittest.cc b/chrome/browser/autofill/android/save_update_address_profile_prompt_controller_unittest.cc
index c997802..555a811 100644
--- a/chrome/browser/autofill/android/save_update_address_profile_prompt_controller_unittest.cc
+++ b/chrome/browser/autofill/android/save_update_address_profile_prompt_controller_unittest.cc
@@ -21,7 +21,6 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/test_personal_data_manager.h"
 #include "components/signin/public/base/consent_level.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
@@ -65,8 +64,6 @@
     original_profile_ = test::GetFullProfile();
     original_profile_.SetInfo(NAME_FULL, u"John Doe", GetLocale());
     original_profile_.SetInfo(PHONE_HOME_WHOLE_NUMBER, u"", GetLocale());
-
-    CountryNames::SetLocaleString(GetLocale());
   }
 
   TestingProfile::TestingFactories GetTestingFactories() const override {
diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.cc b/chrome/browser/bookmarks/android/bookmark_bridge.cc
index 5079d72..d1514b0 100644
--- a/chrome/browser/bookmarks/android/bookmark_bridge.cc
+++ b/chrome/browser/bookmarks/android/bookmark_bridge.cc
@@ -14,6 +14,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/android/callback_android.h"
 #include "base/android/jni_array.h"
 #include "base/android/jni_string.h"
 #include "base/containers/adapters.h"
@@ -44,6 +45,7 @@
 #include "components/bookmarks/common/bookmark_pref_names.h"
 #include "components/bookmarks/managed/managed_bookmark_service.h"
 #include "components/dom_distiller/core/url_utils.h"
+#include "components/page_image_service/image_service.h"
 #include "components/power_bookmarks/core/power_bookmark_utils.h"
 #include "components/power_bookmarks/core/proto/power_bookmark_meta.pb.h"
 #include "components/prefs/pref_service.h"
@@ -104,6 +106,15 @@
   return collator_;
 }
 
+// static
+void HandleImageUrlResponse(
+    base::android::ScopedJavaGlobalRef<jobject> callback,
+    const GURL& image_url) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  RunObjectCallbackAndroid(callback,
+                           url::GURLAndroid::FromNativeGURL(env, image_url));
+}
+
 // The key used to connect the instance of the bookmark bridge to the bookmark
 // model.
 const char kBookmarkBridgeUserDataKey[] = "bookmark_bridge";
@@ -130,7 +141,8 @@
         profile, model, ManagedBookmarkServiceFactory::GetForProfile(profile),
         PartnerBookmarksShim::BuildForBrowserContext(
             chrome::GetBrowserContextRedirectedInIncognito(profile)),
-        ReadingListManagerFactory::GetForBrowserContext(profile));
+        ReadingListManagerFactory::GetForBrowserContext(profile),
+        page_image_service::ImageServiceFactory::GetForBrowserContext(profile));
     model->SetUserData(kBookmarkBridgeUserDataKey,
                        base::WrapUnique(bookmark_bridge));
   }
@@ -143,12 +155,14 @@
     BookmarkModel* model,
     bookmarks::ManagedBookmarkService* managed_bookmark_service,
     PartnerBookmarksShim* partner_bookmarks_shim,
-    ReadingListManager* reading_list_manager)
+    ReadingListManager* reading_list_manager,
+    page_image_service::ImageService* image_service)
     : profile_(profile),
       bookmark_model_(model),
       managed_bookmark_service_(managed_bookmark_service),
       partner_bookmarks_shim_(partner_bookmarks_shim),
       reading_list_manager_(reading_list_manager),
+      image_service_(image_service),
       weak_ptr_factory_(this) {
   profile_observation_.Observe(profile_.get());
   bookmark_model_->AddObserver(this);
@@ -189,6 +203,26 @@
   bookmark_model_->RemoveUserData(kBookmarkBridgeUserDataKey);
 }
 
+void BookmarkBridge::GetImageUrlForBookmark(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj,
+    const JavaParamRef<jobject>& j_url,
+    const JavaParamRef<jobject>& j_callback) {
+  ScopedJavaGlobalRef<jobject> callback(j_callback);
+  if (!image_service_) {
+    RunObjectCallbackAndroid(callback, nullptr);
+    return;
+  }
+
+  page_image_service::mojom::Options options;
+  options.suggest_images = true;
+  options.optimization_guide_images = true;
+  image_service_->FetchImageFor(
+      page_image_service::mojom::ClientId::Bookmarks,
+      *url::GURLAndroid::ToNativeGURL(env, j_url), options,
+      base::BindOnce(&HandleImageUrlResponse, callback));
+}
+
 base::android::ScopedJavaLocalRef<jobject>
 BookmarkBridge::GetBookmarkIdForWebContents(
     JNIEnv* env,
diff --git a/chrome/browser/bookmarks/android/bookmark_bridge.h b/chrome/browser/bookmarks/android/bookmark_bridge.h
index 5c06a98..44388de 100644
--- a/chrome/browser/bookmarks/android/bookmark_bridge.h
+++ b/chrome/browser/bookmarks/android/bookmark_bridge.h
@@ -18,6 +18,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/supports_user_data.h"
 #include "chrome/browser/android/bookmarks/partner_bookmarks_shim.h"
+#include "chrome/browser/image_service/image_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_observer.h"
 #include "chrome/browser/reading_list/android/reading_list_manager.h"
@@ -49,7 +50,8 @@
                  bookmarks::BookmarkModel* model,
                  bookmarks::ManagedBookmarkService* managed_bookmark_service,
                  PartnerBookmarksShim* partner_bookmarks_shim,
-                 ReadingListManager* reading_list_manager);
+                 ReadingListManager* reading_list_manager,
+                 page_image_service::ImageService* image_service);
 
   BookmarkBridge(const BookmarkBridge&) = delete;
   BookmarkBridge& operator=(const BookmarkBridge&) = delete;
@@ -57,6 +59,12 @@
 
   void Destroy(JNIEnv*, const base::android::JavaParamRef<jobject>&);
 
+  void GetImageUrlForBookmark(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj,
+      const base::android::JavaParamRef<jobject>& j_url,
+      const base::android::JavaParamRef<jobject>& j_callback);
+
   base::android::ScopedJavaLocalRef<jobject> GetBookmarkIdForWebContents(
       JNIEnv* env,
       const base::android::JavaParamRef<jobject>& obj,
@@ -367,7 +375,9 @@
   raw_ptr<PartnerBookmarksShim> partner_bookmarks_shim_;
 
   // Holds reading list data. A keyed service owned by the profile.
-  raw_ptr<ReadingListManager> reading_list_manager_;
+  raw_ptr<ReadingListManager> reading_list_manager_;  // weak
+
+  raw_ptr<page_image_service::ImageService> image_service_;  // weak
 
   // Observes the profile destruction and creation.
   base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this};
diff --git a/chrome/browser/device_reauth/android/device_authenticator_android.cc b/chrome/browser/device_reauth/android/device_authenticator_android.cc
index 513db14..ed0cb0a 100644
--- a/chrome/browser/device_reauth/android/device_authenticator_android.cc
+++ b/chrome/browser/device_reauth/android/device_authenticator_android.cc
@@ -73,6 +73,7 @@
     case device_reauth::DeviceAuthRequester::kLocalCardAutofill:
     case device_reauth::DeviceAuthRequester::kDeviceLockPage:
     case device_reauth::DeviceAuthRequester::kPaymentMethodsReauthInSettings:
+    case device_reauth::DeviceAuthRequester::kVirtualCardAutofill:
       return false;
   }
 }
diff --git a/chrome/browser/download/download_frame_policy_browsertest.cc b/chrome/browser/download/download_frame_policy_browsertest.cc
index 9902dee..b61a0ac 100644
--- a/chrome/browser/download/download_frame_policy_browsertest.cc
+++ b/chrome/browser/download/download_frame_policy_browsertest.cc
@@ -224,8 +224,8 @@
             browser()->tab_strip_model(), &web_feature_waiter_);
     content::TestNavigationObserver popup_observer(main_url);
     popup_observer.StartWatchingNewWebContents();
-    EXPECT_TRUE(ExecuteScript(GetSubframeRfh(),
-                              "window.open(\"" + main_url.spec() + "\");"));
+    EXPECT_TRUE(
+        ExecJs(GetSubframeRfh(), "window.open(\"" + main_url.spec() + "\");"));
     popup_observer.Wait();
     ASSERT_EQ(2, browser()->tab_strip_model()->count());
     ASSERT_TRUE(browser()->tab_strip_model()->IsTabSelected(1));
diff --git a/chrome/browser/download/save_page_browsertest.cc b/chrome/browser/download/save_page_browsertest.cc
index ed935e7b..ef31570 100644
--- a/chrome/browser/download/save_page_browsertest.cc
+++ b/chrome/browser/download/save_page_browsertest.cc
@@ -968,7 +968,7 @@
 
   // Refer to the test file from the test page.
   GURL file_url = net::FilePathToFileURL(file_path);
-  ASSERT_TRUE(ExecuteScript(
+  ASSERT_TRUE(ExecJs(
       browser()->tab_strip_model()->GetWebContentsAt(0),
       base::StringPrintf("document.getElementById('resource1').src = '%s';",
                          file_url.spec().data())));
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
index 2ee6a55..99c5a5a 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
@@ -637,6 +637,7 @@
     PrepareRequest(enterprise_connectors::PRINT, request.get());
     request->set_filename(title_);
     request->set_printer_name(data_.printer_name);
+    request->set_printer_type(data_.printer_type);
     if (!page_content_type_.empty()) {
       request->set_content_type(page_content_type_);
     }
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
index 248f147..06bf56e 100644
--- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
+++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
@@ -116,6 +116,12 @@
     // Printer name of the page being sent to, empty for non-print actions.
     std::string printer_name;
 
+    // TODO(b/280457160): Set printer type once scan after printer preview
+    // is done.
+    // Printer type of the page being sent to, empty for non-print actions.
+    enterprise_connectors::ContentMetaData::PrintMetadata::PrinterType
+        printer_type;
+
     // The settings to use for the analysis of the data in this struct.
     enterprise_connectors::AnalysisSettings settings;
   };
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/browser/attester.h b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/attester.h
new file mode 100644
index 0000000..357b60bc
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/attester.h
@@ -0,0 +1,40 @@
+// Copyright 2023 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_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_BROWSER_ATTESTER_H_
+#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_BROWSER_ATTESTER_H_
+
+#include <set>
+#include <string>
+
+#include "base/functional/callback_forward.h"
+#include "chrome/browser/enterprise/connectors/device_trust/attestation/common/proto/device_trust_attestation_ca.pb.h"
+#include "chrome/browser/enterprise/connectors/device_trust/common/common_types.h"
+
+namespace enterprise_connectors {
+
+// Interface for classes to carry out specific actions of building a challenge
+// response based on different policy levels.
+class Attester {
+ public:
+  virtual ~Attester() = default;
+
+  // Decorates the `key_info` object based on the policy `levels`.
+  // `done_closure` is invoked to indicate the completion of this action.
+  virtual void DecorateKeyInfo(const std::set<DTCPolicyLevel>& levels,
+                               KeyInfo& key_info,
+                               base::OnceClosure done_closure) = 0;
+
+  // Based on the policy `levels`, this signs the given `challenge_response` and
+  // populates the `signed_data` with the produced signature. `done_closure` is
+  // invoked to indicate the completion this action.
+  virtual void SignResponse(const std::set<DTCPolicyLevel>& levels,
+                            const std::string& challenge_response,
+                            SignedData& signed_data,
+                            base::OnceClosure done_closure) = 0;
+};
+
+}  // namespace enterprise_connectors
+
+#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_BROWSER_ATTESTER_H_
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester.cc b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester.cc
new file mode 100644
index 0000000..131b089
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester.cc
@@ -0,0 +1,95 @@
+// Copyright 2023 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/enterprise/connectors/device_trust/attestation/browser/device_attester.h"
+
+#include <utility>
+
+#include "base/check.h"
+#include "base/functional/callback.h"
+#include "components/enterprise/browser/controller/browser_dm_token_storage.h"
+#include "components/enterprise/browser/device_trust/device_trust_key_manager.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+
+namespace enterprise_connectors {
+
+DeviceAttester::DeviceAttester(
+    DeviceTrustKeyManager* key_manager,
+    policy::BrowserDMTokenStorage* dm_token_storage,
+    policy::CloudPolicyStore* browser_cloud_policy_store)
+    : key_manager_(key_manager),
+      dm_token_storage_(dm_token_storage),
+      browser_cloud_policy_store_(browser_cloud_policy_store) {
+  CHECK(key_manager_);
+  CHECK(dm_token_storage_);
+}
+
+DeviceAttester::~DeviceAttester() = default;
+
+void DeviceAttester::DecorateKeyInfo(const std::set<DTCPolicyLevel>& levels,
+                                     KeyInfo& key_info,
+                                     base::OnceClosure done_closure) {
+  if (levels.find(DTCPolicyLevel::kBrowser) == levels.end()) {
+    std::move(done_closure).Run();
+    return;
+  }
+
+  auto dm_token = dm_token_storage_->RetrieveDMToken();
+  if (dm_token.is_valid()) {
+    key_info.set_dm_token(dm_token.value());
+  }
+
+  // The device_id is necessary to validate the dm_token.
+  key_info.set_device_id(dm_token_storage_->RetrieveClientId());
+
+  if (browser_cloud_policy_store_ &&
+      browser_cloud_policy_store_->has_policy()) {
+    const auto* policy = browser_cloud_policy_store_->policy();
+    key_info.set_customer_id(policy->obfuscated_customer_id());
+  }
+
+  key_manager_->ExportPublicKeyAsync(base::BindOnce(
+      &DeviceAttester::OnPublicKeyExported, weak_factory_.GetWeakPtr(),
+      std::ref(key_info), std::move(done_closure)));
+}
+
+void DeviceAttester::SignResponse(const std::set<DTCPolicyLevel>& levels,
+                                  const std::string& challenge_response,
+                                  SignedData& signed_data,
+                                  base::OnceClosure done_closure) {
+  if (levels.find(DTCPolicyLevel::kBrowser) == levels.end()) {
+    std::move(done_closure).Run();
+    return;
+  }
+
+  key_manager_->SignStringAsync(
+      challenge_response,
+      base::BindOnce(&DeviceAttester::OnResponseSigned,
+                     weak_factory_.GetWeakPtr(), std::ref(signed_data),
+                     std::move(done_closure)));
+}
+
+void DeviceAttester::OnPublicKeyExported(
+    KeyInfo& key_info,
+    base::OnceClosure done_closure,
+    absl::optional<std::string> exported_key) {
+  if (exported_key) {
+    key_info.set_browser_instance_public_key(exported_key.value());
+  }
+
+  std::move(done_closure).Run();
+}
+
+void DeviceAttester::OnResponseSigned(
+    SignedData& signed_data,
+    base::OnceClosure done_closure,
+    absl::optional<std::vector<uint8_t>> signed_response) {
+  if (signed_response.has_value()) {
+    signed_data.set_signature(signed_response->data(), signed_response->size());
+  }
+
+  std::move(done_closure).Run();
+}
+
+}  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester.h b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester.h
new file mode 100644
index 0000000..2295df5
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester.h
@@ -0,0 +1,68 @@
+// Copyright 2023 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_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_BROWSER_DEVICE_ATTESTER_H_
+#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_BROWSER_DEVICE_ATTESTER_H_
+
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/enterprise/connectors/device_trust/attestation/browser/attester.h"
+
+namespace policy {
+class BrowserDMTokenStorage;
+class CloudPolicyStore;
+}  // namespace policy
+
+namespace enterprise_connectors {
+
+class DeviceTrustKeyManager;
+
+// This class is in charge of populating device specific details during the
+// browser attestation process.
+class DeviceAttester : public Attester {
+ public:
+  DeviceAttester(DeviceTrustKeyManager* key_manager,
+                 policy::BrowserDMTokenStorage* dm_token_storage,
+                 policy::CloudPolicyStore* browser_cloud_policy_store);
+  ~DeviceAttester() override;
+
+  // Attester:
+  void DecorateKeyInfo(const std::set<DTCPolicyLevel>& levels,
+                       KeyInfo& key_info,
+                       base::OnceClosure done_closure) override;
+  void SignResponse(const std::set<DTCPolicyLevel>& levels,
+                    const std::string& challenge_response,
+                    SignedData& signed_data,
+                    base::OnceClosure done_closure) override;
+
+ private:
+  // Adds the `exported_key` details to the `key_info` and invokes the
+  // `done_closure` to indicate completion.
+  void OnPublicKeyExported(KeyInfo& key_info,
+                           base::OnceClosure done_closure,
+                           absl::optional<std::string> exported_key);
+
+  // Adds the `signed_response` to the `signed_data` and invokes the
+  // `done_closure` to indicate completion.
+  void OnResponseSigned(SignedData& signed_data,
+                        base::OnceClosure done_closure,
+                        absl::optional<std::vector<uint8_t>> signed_response);
+
+  // Owned by the CBCMController, which is eventually owned by the browser
+  // process. Since the current service is owned at the profile level, this
+  // respects the browser shutdown sequence.
+  const raw_ptr<DeviceTrustKeyManager> key_manager_;
+
+  // Helper for handling the DM token and device ID.
+  const raw_ptr<policy::BrowserDMTokenStorage> dm_token_storage_;
+
+  // Used for retrieving a managed devices customer ID.
+  const raw_ptr<policy::CloudPolicyStore> browser_cloud_policy_store_;
+
+  base::WeakPtrFactory<DeviceAttester> weak_factory_{this};
+};
+
+}  // namespace enterprise_connectors
+
+#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_BROWSER_DEVICE_ATTESTER_H_
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester_unittest.cc
new file mode 100644
index 0000000..37b26dfc
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/device_attester_unittest.cc
@@ -0,0 +1,262 @@
+// Copyright 2023 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/enterprise/connectors/device_trust/attestation/browser/device_attester.h"
+
+#include "base/run_loop.h"
+#include "base/test/task_environment.h"
+#include "base/test/test_future.h"
+#include "chrome/browser/enterprise/connectors/device_trust/key_management/browser/mock_device_trust_key_manager.h"
+#include "chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence/scoped_key_persistence_delegate_factory.h"
+#include "components/enterprise/browser/controller/fake_browser_dm_token_storage.h"
+#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Invoke;
+
+namespace enterprise_connectors {
+
+namespace {
+
+constexpr char kFakeDeviceId[] = "fake_device_id";
+constexpr char kDmToken[] = "fake-dm-token";
+constexpr char kInvalidDmToken[] = "INVALID_DM_TOKEN";
+constexpr char kFakeCustomerId[] = "fake_obfuscated_customer_id";
+constexpr char kFakeChallengeResponse[] = "fake_challenge_response";
+
+}  // namespace
+
+class DeviceAttesterTest : public testing::Test {
+ protected:
+  DeviceAttesterTest()
+      : device_attester_(&mock_key_manager_,
+                         &fake_dm_token_storage_,
+                         &mock_browser_cloud_policy_store_) {
+    fake_dm_token_storage_.SetDMToken(kDmToken);
+    fake_dm_token_storage_.SetClientId(kFakeDeviceId);
+    test_key_pair_ =
+        persistence_delegate_factory_.CreateKeyPersistenceDelegate()
+            ->LoadKeyPair();
+    levels_.insert(DTCPolicyLevel::kBrowser);
+  }
+
+  void SetFakeBrowserPolicyData() {
+    auto policy_data = std::make_unique<enterprise_management::PolicyData>();
+    policy_data->set_obfuscated_customer_id(kFakeCustomerId);
+    mock_browser_cloud_policy_store_.set_policy_data_for_testing(
+        std::move(policy_data));
+  }
+
+  void SetupPubkeyExport(bool can_export_pubkey = true) {
+    EXPECT_CALL(mock_key_manager_, ExportPublicKeyAsync(_))
+        .WillOnce(
+            Invoke([&, can_export_pubkey](
+                       base::OnceCallback<void(absl::optional<std::string>)>
+                           callback) {
+              if (can_export_pubkey) {
+                auto public_key_info =
+                    test_key_pair_->key()->GetSubjectPublicKeyInfo();
+                std::string public_key(public_key_info.begin(),
+                                       public_key_info.end());
+                public_key_ = public_key;
+                std::move(callback).Run(public_key);
+              } else {
+                std::move(callback).Run(absl::nullopt);
+              }
+            }));
+  }
+
+  void SetupSignature(bool can_sign = true) {
+    EXPECT_CALL(mock_key_manager_, SignStringAsync(_, _))
+        .WillOnce(Invoke(
+            [&, can_sign](const std::string& str,
+                          base::OnceCallback<void(
+                              absl::optional<std::vector<uint8_t>>)> callback) {
+              if (can_sign) {
+                signature = test_key_pair_->key()->SignSlowly(
+                    base::as_bytes(base::make_span(str)));
+                std::move(callback).Run(signature);
+              } else {
+                std::move(callback).Run(absl::nullopt);
+              }
+            }));
+  }
+
+  base::test::SingleThreadTaskEnvironment task_environment_;
+  test::ScopedKeyPersistenceDelegateFactory persistence_delegate_factory_;
+  std::unique_ptr<SigningKeyPair> test_key_pair_;
+  absl::optional<std::vector<uint8_t>> signature;
+  std::string public_key_;
+  testing::StrictMock<test::MockDeviceTrustKeyManager> mock_key_manager_;
+  policy::FakeBrowserDMTokenStorage fake_dm_token_storage_;
+  policy::MockCloudPolicyStore mock_browser_cloud_policy_store_;
+  DeviceAttester device_attester_;
+  base::test::TestFuture<void> future_;
+  KeyInfo key_info_;
+  SignedData signed_data_;
+  base::RunLoop run_loop_;
+  std::set<DTCPolicyLevel> levels_;
+};
+
+// Tests that the correct device details are added when the device attester
+// builds the key info.
+TEST_F(DeviceAttesterTest, DecorateKeyInfo_Success) {
+  SetFakeBrowserPolicyData();
+  SetupPubkeyExport();
+
+  device_attester_.DecorateKeyInfo(levels_, key_info_, run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_EQ(key_info_.dm_token(), kDmToken);
+  EXPECT_EQ(key_info_.device_id(), kFakeDeviceId);
+  EXPECT_EQ(key_info_.customer_id(), kFakeCustomerId);
+  EXPECT_EQ(key_info_.browser_instance_public_key(), public_key_);
+}
+
+// Tests that the correct device details are added when the device attester
+// attempts to build the key info with an invalid DM token.
+TEST_F(DeviceAttesterTest, DecorateKeyInfo_InvalidDmToken) {
+  fake_dm_token_storage_.SetDMToken(kInvalidDmToken);
+  SetFakeBrowserPolicyData();
+  SetupPubkeyExport();
+
+  device_attester_.DecorateKeyInfo(levels_, key_info_, run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(key_info_.has_dm_token());
+  EXPECT_EQ(key_info_.device_id(), kFakeDeviceId);
+  EXPECT_EQ(key_info_.customer_id(), kFakeCustomerId);
+  EXPECT_EQ(key_info_.browser_instance_public_key(), public_key_);
+}
+
+// Tests that the correct device details are added when the device attester
+// attempts to build the key info with an empty DM token.
+TEST_F(DeviceAttesterTest, DecorateKeyInfo_EmptyDmToken) {
+  fake_dm_token_storage_.SetDMToken(std::string());
+  SetFakeBrowserPolicyData();
+  SetupPubkeyExport();
+
+  device_attester_.DecorateKeyInfo(levels_, key_info_, run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(key_info_.has_dm_token());
+  EXPECT_EQ(key_info_.device_id(), kFakeDeviceId);
+  EXPECT_EQ(key_info_.customer_id(), kFakeCustomerId);
+  EXPECT_EQ(key_info_.browser_instance_public_key(), public_key_);
+}
+
+// Tests that the correct device details are added when the device ID details
+// are missing.
+TEST_F(DeviceAttesterTest, DecorateKeyInfo_MissingDeviceID) {
+  fake_dm_token_storage_.SetClientId(std::string());
+  SetFakeBrowserPolicyData();
+  SetupPubkeyExport();
+
+  device_attester_.DecorateKeyInfo(levels_, key_info_, run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(key_info_.has_dm_token());
+  EXPECT_EQ(key_info_.device_id(), "");
+  EXPECT_EQ(key_info_.customer_id(), kFakeCustomerId);
+  EXPECT_EQ(key_info_.browser_instance_public_key(), public_key_);
+}
+
+// Tests that the correct device details are added when the customer ID details
+// are missing.
+TEST_F(DeviceAttesterTest, DecorateKeyInfo_NoBrowserCustomerId) {
+  SetupPubkeyExport();
+
+  device_attester_.DecorateKeyInfo(levels_, key_info_, run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(key_info_.has_customer_id());
+  EXPECT_EQ(key_info_.dm_token(), kDmToken);
+  EXPECT_EQ(key_info_.device_id(), kFakeDeviceId);
+  EXPECT_EQ(key_info_.browser_instance_public_key(), public_key_);
+}
+
+// Tests that the correct device details are added when a failure occurred
+// exporting the public key.
+TEST_F(DeviceAttesterTest, DecorateKeyInfo_FailedPublicKeyExport) {
+  SetFakeBrowserPolicyData();
+  SetupPubkeyExport(/*can_export_pubkey=*/false);
+
+  device_attester_.DecorateKeyInfo(levels_, key_info_, run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(key_info_.has_browser_instance_public_key());
+  EXPECT_EQ(key_info_.dm_token(), kDmToken);
+  EXPECT_EQ(key_info_.device_id(), kFakeDeviceId);
+  EXPECT_EQ(key_info_.customer_id(), kFakeCustomerId);
+}
+
+// Tests that the correct device details are added when a failure occurred
+// exporting the public key.
+TEST_F(DeviceAttesterTest, DecorateKeyInfo_MissingBrowserPolicyLevel) {
+  SetFakeBrowserPolicyData();
+  EXPECT_CALL(mock_key_manager_, ExportPublicKeyAsync(_)).Times(0);
+  auto empty_policy_level = std::set<DTCPolicyLevel>();
+
+  device_attester_.DecorateKeyInfo(empty_policy_level, key_info_,
+                                   run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(key_info_.has_browser_instance_public_key());
+  EXPECT_FALSE(key_info_.has_dm_token());
+  EXPECT_FALSE(key_info_.has_device_id());
+  EXPECT_FALSE(key_info_.has_customer_id());
+}
+
+// Tests that no policy data is added when the browser cloud policy store is
+// null.
+TEST_F(DeviceAttesterTest, DecorateKeyInfo_MissingBrowserCloudPolicyStore) {
+  SetupPubkeyExport();
+
+  DeviceAttester(&mock_key_manager_, &fake_dm_token_storage_, nullptr)
+      .DecorateKeyInfo(levels_, key_info_, run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(key_info_.has_customer_id());
+  EXPECT_EQ(key_info_.dm_token(), kDmToken);
+  EXPECT_EQ(key_info_.device_id(), kFakeDeviceId);
+  EXPECT_EQ(key_info_.browser_instance_public_key(), public_key_);
+}
+
+// Tests when a browser level signature is added to the signed data.
+TEST_F(DeviceAttesterTest, SignResponse_Success) {
+  SetupSignature();
+
+  device_attester_.SignResponse(levels_, kFakeChallengeResponse, signed_data_,
+                                run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_TRUE(signed_data_.has_signature());
+}
+
+// Tests when no browser level signature is added to the signed data.
+TEST_F(DeviceAttesterTest, SignResponse_NoSignature) {
+  SetupSignature(/*can_sign=*/false);
+
+  device_attester_.SignResponse(levels_, kFakeChallengeResponse, signed_data_,
+                                run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(signed_data_.has_signature());
+}
+
+// Tests that no browser level signature is added to the signed data when the
+// browser level is not in the policy level set.
+TEST_F(DeviceAttesterTest, SignResponse_MissingBrowserPolicyLevel) {
+  EXPECT_CALL(mock_key_manager_, SignStringAsync(_, _)).Times(0);
+  auto empty_policy_level = std::set<DTCPolicyLevel>();
+
+  device_attester_.SignResponse(empty_policy_level, kFakeChallengeResponse,
+                                signed_data_, run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(signed_data_.has_signature());
+}
+
+}  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/browser/profile_attester.cc b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/profile_attester.cc
new file mode 100644
index 0000000..36e2585
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/profile_attester.cc
@@ -0,0 +1,42 @@
+// Copyright 2023 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/enterprise/connectors/device_trust/attestation/browser/profile_attester.h"
+
+#include <utility>
+
+#include "base/check.h"
+#include "base/functional/callback.h"
+#include "components/enterprise/browser/identifiers/profile_id_service.h"
+#include "components/policy/core/common/cloud/cloud_policy_store.h"
+
+namespace enterprise_connectors {
+
+ProfileAttester::ProfileAttester(
+    enterprise::ProfileIdService* profile_id_service,
+    policy::CloudPolicyStore* user_cloud_policy_store)
+    : profile_id_service_(profile_id_service),
+      user_cloud_policy_store_(user_cloud_policy_store) {
+  CHECK(profile_id_service_);
+}
+
+ProfileAttester::~ProfileAttester() = default;
+
+void ProfileAttester::DecorateKeyInfo(const std::set<DTCPolicyLevel>& levels,
+                                      KeyInfo& key_info,
+                                      base::OnceClosure done_closure) {
+  // TODO(b:279063303): Populate this with profile specific key info once the DT
+  // proto changes occur.
+  std::move(done_closure).Run();
+}
+
+void ProfileAttester::SignResponse(const std::set<DTCPolicyLevel>& levels,
+                                   const std::string& challenge_response,
+                                   SignedData& signed_data,
+                                   base::OnceClosure done_closure) {
+  // Profile level signatures are currently not supported.
+  std::move(done_closure).Run();
+}
+
+}  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/browser/profile_attester.h b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/profile_attester.h
new file mode 100644
index 0000000..3db6789e
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/profile_attester.h
@@ -0,0 +1,49 @@
+// Copyright 2023 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_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_BROWSER_PROFILE_ATTESTER_H_
+#define CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_BROWSER_PROFILE_ATTESTER_H_
+
+#include "chrome/browser/enterprise/connectors/device_trust/attestation/browser/attester.h"
+
+#include "base/memory/raw_ptr.h"
+
+namespace enterprise {
+class ProfileIdService;
+}  // namespace enterprise
+
+namespace policy {
+class CloudPolicyStore;
+}  // namespace policy
+
+namespace enterprise_connectors {
+
+// This class is in charge of populating profile specific details during the
+// browser attestation process.
+class ProfileAttester : public Attester {
+ public:
+  ProfileAttester(enterprise::ProfileIdService* profile_id_service,
+                  policy::CloudPolicyStore* user_cloud_policy_store);
+  ~ProfileAttester() override;
+
+  // Attester:
+  void DecorateKeyInfo(const std::set<DTCPolicyLevel>& levels,
+                       KeyInfo& key_info,
+                       base::OnceClosure done_closure) override;
+  void SignResponse(const std::set<DTCPolicyLevel>& levels,
+                    const std::string& challenge_response,
+                    SignedData& signed_data,
+                    base::OnceClosure done_closure) override;
+
+ private:
+  // Used for retrieving the profile ID.
+  const raw_ptr<enterprise::ProfileIdService> profile_id_service_;
+
+  // Used for retrieving a managed profiles customer and gaia ID.
+  const raw_ptr<policy::CloudPolicyStore> user_cloud_policy_store_;
+};
+
+}  // namespace enterprise_connectors
+
+#endif  // CHROME_BROWSER_ENTERPRISE_CONNECTORS_DEVICE_TRUST_ATTESTATION_BROWSER_PROFILE_ATTESTER_H_
diff --git a/chrome/browser/enterprise/connectors/device_trust/attestation/browser/profile_attester_unittest.cc b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/profile_attester_unittest.cc
new file mode 100644
index 0000000..dd162eb
--- /dev/null
+++ b/chrome/browser/enterprise/connectors/device_trust/attestation/browser/profile_attester_unittest.cc
@@ -0,0 +1,89 @@
+// Copyright 2023 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/enterprise/connectors/device_trust/attestation/browser/profile_attester.h"
+
+#include "base/run_loop.h"
+#include "chrome/browser/enterprise/identifiers/profile_id_service_factory.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/enterprise/browser/identifiers/profile_id_service.h"
+#include "components/policy/core/common/cloud/mock_cloud_policy_store.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace enterprise_connectors {
+
+namespace {
+
+constexpr char kFakeProfileId[] = "fake-profile-id";
+constexpr char kFakeChallengeResponse[] = "fake_challenge_response";
+
+std::unique_ptr<KeyedService> CreateProfileIDService(
+    content::BrowserContext* context) {
+  return std::make_unique<enterprise::ProfileIdService>(kFakeProfileId);
+}
+
+}  // namespace
+
+class ProfileAttesterTest : public testing::Test {
+ protected:
+  ProfileAttesterTest() : profile_manager_(TestingBrowserProcess::GetGlobal()) {
+    EXPECT_TRUE(profile_manager_.SetUp());
+    profile_ = profile_manager_.CreateTestingProfile("test-user");
+    enterprise::ProfileIdServiceFactory::GetInstance()->SetTestingFactory(
+        profile_, base::BindRepeating(&CreateProfileIDService));
+  }
+
+  void SetUp() override {
+    testing::Test::SetUp();
+
+    profile_attester_ = std::make_unique<ProfileAttester>(
+        enterprise::ProfileIdServiceFactory::GetForProfile(profile_),
+        &mock_profile_cloud_policy_store_);
+
+    levels_.insert(DTCPolicyLevel::kUser);
+  }
+
+  content::BrowserTaskEnvironment task_environment_;
+  base::RunLoop run_loop_;
+  KeyInfo key_info_;
+  SignedData signed_data_;
+  TestingProfileManager profile_manager_;
+  raw_ptr<TestingProfile> profile_;
+  policy::MockCloudPolicyStore mock_profile_cloud_policy_store_;
+  std::unique_ptr<ProfileAttester> profile_attester_;
+  std::set<DTCPolicyLevel> levels_;
+};
+
+// Tests that no details are added when the profile attester decorates the key
+// info.
+TEST_F(ProfileAttesterTest, DecorateKeyInfo_NoKeyDetails) {
+  profile_attester_->DecorateKeyInfo(levels_, key_info_,
+                                     run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(key_info_.has_key_type());
+  EXPECT_FALSE(key_info_.has_domain());
+  EXPECT_FALSE(key_info_.has_device_id());
+  EXPECT_FALSE(key_info_.has_certificate());
+  EXPECT_FALSE(key_info_.has_signed_public_key_and_challenge());
+  EXPECT_FALSE(key_info_.has_customer_id());
+  EXPECT_FALSE(key_info_.has_browser_instance_public_key());
+  EXPECT_FALSE(key_info_.has_signing_scheme());
+  EXPECT_FALSE(key_info_.has_device_trust_signals_json());
+  EXPECT_FALSE(key_info_.has_dm_token());
+}
+
+// Tests that no profile level signature is added to the signature map.
+TEST_F(ProfileAttesterTest, SignResponse_NoSignature) {
+  profile_attester_->SignResponse(levels_, kFakeChallengeResponse, signed_data_,
+                                  run_loop_.QuitClosure());
+  run_loop_.Run();
+
+  EXPECT_FALSE(signed_data_.has_signature());
+}
+
+}  // namespace enterprise_connectors
diff --git a/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win.cc b/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win.cc
index e73e613..cfe13c3 100644
--- a/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win.cc
+++ b/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win.cc
@@ -180,6 +180,51 @@
   return SUCCEEDED(hresult) ? manager : nullptr;
 }
 
+void ParseCookieInfo(const ProofOfPossessionCookieInfo* cookie_info,
+                     const DWORD cookie_info_count,
+                     net::HttpRequestHeaders& auth_headers) {
+  net::cookie_util::ParsedRequestCookies parsed_cookies;
+  if (base::FeatureList::IsEnabled(
+          enterprise_auth::kCloudApAuthAttachAsHeader)) {
+    // If the auth cookie name begins with 'x-ms-', attach the cookie as a
+    // new header. Otherwise, append it to the existing list of cookies.
+    static constexpr base::StringPiece kHeaderPrefix("x-ms-");
+    for (DWORD i = 0; i < cookie_info_count; ++i) {
+      const ProofOfPossessionCookieInfo& cookie = cookie_info[i];
+      auto ascii_cookie_name = base::WideToASCII(cookie.name);
+      if (base::StartsWith(ascii_cookie_name, kHeaderPrefix,
+                           base::CompareCase::INSENSITIVE_ASCII)) {
+        // Removing cookie attributes from the value before setting it as a
+        // header.
+        std::string ascii_cookie_value = base::WideToASCII(cookie.data);
+        std::string::size_type cookie_attributes_position =
+            ascii_cookie_value.find(";");
+        if (cookie_attributes_position != std::string::npos) {
+          ascii_cookie_value =
+              ascii_cookie_value.substr(0, cookie_attributes_position);
+        }
+        auth_headers.SetHeader(std::move(ascii_cookie_name),
+                               std::move(ascii_cookie_value));
+      } else {
+        parsed_cookies.emplace_back(std::move(ascii_cookie_name),
+                                    base::WideToASCII(cookie.data));
+      }
+    }
+  } else {
+    // Append all auth cookies to the existing set of cookies.
+    for (DWORD i = 0; i < cookie_info_count; ++i) {
+      const ProofOfPossessionCookieInfo& cookie = cookie_info[i];
+      parsed_cookies.emplace_back(base::WideToASCII(cookie.name),
+                                  base::WideToASCII(cookie.data));
+    }
+  }
+  if (parsed_cookies.size() > 0) {
+    auth_headers.SetHeader(
+        net::HttpRequestHeaders::kCookie,
+        net::cookie_util::SerializeRequestCookieLine(parsed_cookies));
+  }
+}
+
 // Returns the proof-of-possession cookies and headers for the interactive
 // user to authenticate to the IdP/STS at `url`.
 net::HttpRequestHeaders GetAuthData(const GURL& url) {
@@ -199,38 +244,7 @@
                                      &cookie_info_count, &cookie_info);
     if (SUCCEEDED(hresult)) {
       DCHECK(!cookie_info_count || cookie_info);
-      net::cookie_util::ParsedRequestCookies parsed_cookies;
-      if (base::FeatureList::IsEnabled(
-              enterprise_auth::kCloudApAuthAttachAsHeader)) {
-        // If the auth cookie name begins with 'x-ms-', attach the cookie as a
-        // new header. Otherwise, append it to the existing list of cookies.
-        static constexpr base::StringPiece kHeaderPrefix("x-ms-");
-        for (DWORD i = 0; i < cookie_info_count; ++i) {
-          const ProofOfPossessionCookieInfo& cookie = cookie_info[i];
-          auto ascii_name = base::WideToASCII(cookie.name);
-          if (base::StartsWith(ascii_name, kHeaderPrefix,
-                               base::CompareCase::INSENSITIVE_ASCII)) {
-            auth_headers.SetHeader(std::move(ascii_name),
-                                   base::WideToASCII(cookie.data));
-          } else {
-            parsed_cookies.emplace_back(std::move(ascii_name),
-                                        base::WideToASCII(cookie.data));
-          }
-        }
-      } else {
-        // Append all auth cookies to the existing set of cookies.
-        for (DWORD i = 0; i < cookie_info_count; ++i) {
-          const ProofOfPossessionCookieInfo& cookie = cookie_info[i];
-          parsed_cookies.emplace_back(base::WideToASCII(cookie.name),
-                                      base::WideToASCII(cookie.data));
-        }
-      }
-      if (parsed_cookies.size() > 0) {
-        auth_headers.SetHeader(
-            net::HttpRequestHeaders::kCookie,
-            net::cookie_util::SerializeRequestCookieLine(parsed_cookies));
-      }
-
+      ParseCookieInfo(cookie_info, cookie_info_count, auth_headers);
       if (cookie_info)
         FreeProofOfPossessionCookieInfoArray(cookie_info, cookie_info_count);
     }
@@ -437,4 +451,11 @@
   *support_level_for_testing_ = level.value();
 }
 
+void CloudApProviderWin::ParseCookieInfoForTesting(
+    const ProofOfPossessionCookieInfo* cookie_info,
+    const DWORD cookie_info_count,
+    net::HttpRequestHeaders& auth_headers) {
+  ParseCookieInfo(cookie_info, cookie_info_count, auth_headers);
+}
+
 }  // namespace enterprise_auth
diff --git a/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win.h b/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win.h
index 5a0c005f..d4294ecb 100644
--- a/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win.h
+++ b/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win.h
@@ -11,6 +11,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 class GURL;
+class ProofOfPossessionCookieInfo;
 
 namespace net {
 class HttpRequestHeaders;
@@ -44,6 +45,9 @@
   FRIEND_TEST_ALL_PREFIXES(CloudApProviderWinTest, Unsupported);
   FRIEND_TEST_ALL_PREFIXES(CloudApProviderWinTest, NotJoined);
   FRIEND_TEST_ALL_PREFIXES(CloudApProviderWinTest, Joined);
+  FRIEND_TEST_ALL_PREFIXES(CloudApProviderWinTest, ParseCookieInfo);
+  FRIEND_TEST_ALL_PREFIXES(CloudApProviderWinTest,
+                           ParseCookieInfo_HeaderFeatureEnabled);
 
   // Runs the stored callbacks using the provided auth headers.
   void OnGetDataCallback(net::HttpRequestHeaders auth_headers);
@@ -52,6 +56,11 @@
   // override if not.
   static void SetSupportLevelForTesting(absl::optional<SupportLevel> level);
 
+  // Allows cookie info to be parsed for testing purposes.
+  void ParseCookieInfoForTesting(const ProofOfPossessionCookieInfo* cookie_info,
+                                 const DWORD cookie_info_count,
+                                 net::HttpRequestHeaders& auth_headers);
+
   // List of callbacks to run when auth data is received.
   using GetDataCallbackList =
       base::OnceCallbackList<void(net::HttpRequestHeaders)>;
diff --git a/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win_unittest.cc b/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win_unittest.cc
index 62a33e3..8d5728d 100644
--- a/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win_unittest.cc
+++ b/chrome/browser/enterprise/platform_auth/cloud_ap_provider_win_unittest.cc
@@ -4,14 +4,19 @@
 
 #include "chrome/browser/enterprise/platform_auth/cloud_ap_provider_win.h"
 
+#include <proofofpossessioncookieinfo.h>
+
 #include <memory>
 #include <vector>
 
 #include "base/run_loop.h"
+#include "base/strings/string_util.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/test/test_reg_util_win.h"
 #include "base/win/registry.h"
+#include "chrome/browser/enterprise/platform_auth/platform_auth_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -21,6 +26,19 @@
 
 namespace enterprise_auth {
 
+namespace {
+
+// Helper function for constructing ProofOfPossessionCookieInfo objects.
+ProofOfPossessionCookieInfo GetCookieInfo(const wchar_t* name,
+                                          const wchar_t* data) {
+  ProofOfPossessionCookieInfo cookie_info;
+  cookie_info.name = const_cast<LPWSTR>(name);
+  cookie_info.data = const_cast<LPWSTR>(data);
+  return cookie_info;
+}
+
+}  // namespace
+
 class CloudApProviderWinTest : public ::testing::Test {
  protected:
   ~CloudApProviderWinTest() override {
@@ -146,4 +164,84 @@
   run_loop.Run();
 }
 
+// Tests that cookie info is correctly parsed into the cookie header.
+TEST_F(CloudApProviderWinTest, ParseCookieInfo) {
+  CloudApProviderWin provider;
+  net::HttpRequestHeaders auth_headers;
+  DWORD cookie_info_count = 2;
+
+  const wchar_t* cookie_name_1 = L"name";
+  const wchar_t* cookie_name_2 = L"x-ms-name";
+  const wchar_t* cookie_data = L"data; cookie_attributes; a; b";
+
+  ProofOfPossessionCookieInfo cookie_info_1 =
+      GetCookieInfo(cookie_name_1, cookie_data);
+  ProofOfPossessionCookieInfo cookie_info_2 =
+      GetCookieInfo(cookie_name_2, cookie_data);
+  ProofOfPossessionCookieInfo cookie_info[2] = {cookie_info_1, cookie_info_2};
+  provider.ParseCookieInfoForTesting(cookie_info, cookie_info_count,
+                                     auth_headers);
+
+  // The names and data of all cookies should appear in the cookie header.
+  std::string cookie_value;
+  ASSERT_TRUE(
+      auth_headers.GetHeader(net::HttpRequestHeaders::kCookie, &cookie_value));
+  EXPECT_EQ(
+      cookie_value,
+      base::JoinString({base::JoinString({base::WideToASCII(cookie_name_1),
+                                          base::WideToASCII(cookie_data)},
+                                         "="),
+                        base::JoinString({base::WideToASCII(cookie_name_2),
+                                          base::WideToASCII(cookie_data)},
+                                         "=")},
+                       "; "));
+
+  // None of the cookies should be set as individual headers.
+  EXPECT_FALSE(
+      auth_headers.GetHeader(base::WideToASCII(cookie_name_1), &cookie_value));
+  EXPECT_FALSE(
+      auth_headers.GetHeader(base::WideToASCII(cookie_name_2), &cookie_value));
+}
+
+// Tests that cookie info is correctly parsed into the corresponding headers.
+TEST_F(CloudApProviderWinTest, ParseCookieInfo_HeaderFeatureEnabled) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      enterprise_auth::kCloudApAuthAttachAsHeader);
+
+  CloudApProviderWin provider;
+  net::HttpRequestHeaders auth_headers;
+  DWORD cookie_info_count = 2;
+
+  const wchar_t* cookie_name_1 = L"name";
+  const wchar_t* cookie_name_2 = L"x-ms-name";
+  const wchar_t* cookie_data = L"data; cookie_attributes; a; b";
+
+  ProofOfPossessionCookieInfo cookie_info_1 =
+      GetCookieInfo(cookie_name_1, cookie_data);
+  ProofOfPossessionCookieInfo cookie_info_2 =
+      GetCookieInfo(cookie_name_2, cookie_data);
+  ProofOfPossessionCookieInfo cookie_info[2] = {cookie_info_1, cookie_info_2};
+  provider.ParseCookieInfoForTesting(cookie_info, cookie_info_count,
+                                     auth_headers);
+
+  // The names and data of all cookies whose names don't begin with 'x-ms'
+  // should appear in the cookie header.
+  std::string cookie_value;
+  ASSERT_TRUE(
+      auth_headers.GetHeader(net::HttpRequestHeaders::kCookie, &cookie_value));
+  EXPECT_EQ(cookie_value, base::JoinString({base::WideToASCII(cookie_name_1),
+                                            base::WideToASCII(cookie_data)},
+                                           "="));
+
+  // Only cookies whose name begins with 'x-ms' should be set as individual
+  // headers.
+  EXPECT_FALSE(
+      auth_headers.GetHeader(base::WideToASCII(cookie_name_1), &cookie_value));
+  EXPECT_TRUE(
+      auth_headers.GetHeader(base::WideToASCII(cookie_name_2), &cookie_value));
+  // Cookie attributes are removed from the header value.
+  EXPECT_EQ(cookie_value, base::WideToASCII(L"data"));
+}
+
 }  // namespace enterprise_auth
diff --git a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
index 1eff178..aee03e1 100644
--- a/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
+++ b/chrome/browser/enterprise/reporting/extension_request/extension_request_report_generator.cc
@@ -30,22 +30,20 @@
 // add-request.
 std::unique_ptr<ExtensionsWorkflowEvent> GenerateReport(
     const std::string& extension_id,
-    const base::Value* request_data) {
+    const base::Value::Dict* request_data) {
   auto report = std::make_unique<ExtensionsWorkflowEvent>();
   report->set_id(extension_id);
   if (request_data) {
-    if (request_data->is_dict()) {
-      absl::optional<base::Time> timestamp =
-          ::base::ValueToTime(request_data->GetDict().Find(
-              extension_misc::kExtensionRequestTimestamp));
-      if (timestamp)
-        report->set_request_timestamp_millis(timestamp->ToJavaTime());
+    absl::optional<base::Time> timestamp = ::base::ValueToTime(
+        request_data->Find(extension_misc::kExtensionRequestTimestamp));
+    if (timestamp) {
+      report->set_request_timestamp_millis(timestamp->ToJavaTime());
+    }
 
-      const std::string* justification = request_data->FindStringKey(
-          extension_misc::kExtensionWorkflowJustification);
-      if (justification) {
-        report->set_justification(*justification);
-      }
+    const std::string* justification = request_data->FindString(
+        extension_misc::kExtensionWorkflowJustification);
+    if (justification) {
+      report->set_justification(*justification);
     }
     report->set_removed(false);
   } else {
@@ -100,8 +98,7 @@
   const base::Value::Dict& uploaded_requests =
       profile->GetPrefs()->GetDict(kCloudExtensionRequestUploadedIds);
 
-  for (auto it : pending_requests) {
-    const std::string& extension_id = it.first;
+  for (auto [extension_id, request_data] : pending_requests) {
     if (!ShouldUploadExtensionRequest(extension_id, webstore_update_url,
                                       extension_management)) {
       continue;
@@ -111,13 +108,11 @@
     if (uploaded_requests.contains(extension_id))
       continue;
 
-    reports.push_back(
-        GenerateReport(extension_id, /*request_data=*/&it.second));
+    CHECK(request_data.is_dict());
+    reports.push_back(GenerateReport(extension_id, &request_data.GetDict()));
   }
 
-  for (auto it : uploaded_requests) {
-    const std::string& extension_id = it.first;
-
+  for (auto [extension_id, ignored] : uploaded_requests) {
     // Request is still pending, no need to send remove request.
     if (pending_requests.contains(extension_id))
       continue;
diff --git a/chrome/browser/enterprise/reporting/profile_report_generator_desktop.cc b/chrome/browser/enterprise/reporting/profile_report_generator_desktop.cc
index 93a04f04..d34b641 100644
--- a/chrome/browser/enterprise/reporting/profile_report_generator_desktop.cc
+++ b/chrome/browser/enterprise/reporting/profile_report_generator_desktop.cc
@@ -54,9 +54,9 @@
       extension_urls::GetDefaultWebstoreUpdateUrl().spec();
 
   int number_of_requests = 0;
-  for (auto it : pending_requests) {
+  for (auto [extension_id, request_data] : pending_requests) {
     if (!ExtensionRequestReportGenerator::ShouldUploadExtensionRequest(
-            it.first, webstore_update_url, extension_management)) {
+            extension_id, webstore_update_url, extension_management)) {
       continue;
     }
 
@@ -67,13 +67,15 @@
       break;
 
     auto* request = report->add_extension_requests();
-    request->set_id(it.first);
+    request->set_id(extension_id);
+
+    const auto& request_data_dict = request_data.GetDict();
     absl::optional<base::Time> timestamp = ::base::ValueToTime(
-        it.second.GetDict().Find(extension_misc::kExtensionRequestTimestamp));
+        request_data_dict.Find(extension_misc::kExtensionRequestTimestamp));
     if (timestamp)
       request->set_request_timestamp(timestamp->ToJavaTime());
 
-    const std::string* justification = it.second.FindStringKey(
+    const std::string* justification = request_data_dict.FindString(
         extension_misc::kExtensionWorkflowJustification);
     if (justification) {
       request->set_justification(*justification);
diff --git a/chrome/browser/enterprise/reporting/real_time_report_generator_unittest.cc b/chrome/browser/enterprise/reporting/real_time_report_generator_unittest.cc
index 9cc1927..5275acd 100644
--- a/chrome/browser/enterprise/reporting/real_time_report_generator_unittest.cc
+++ b/chrome/browser/enterprise/reporting/real_time_report_generator_unittest.cc
@@ -38,12 +38,9 @@
   TestingProfile* profile = profile_manager()->CreateTestingProfile("profile");
 
   profile->GetTestingPrefService()->SetManagedPref(
-      prefs::kCloudExtensionRequestEnabled,
-      std::make_unique<base::Value>(true));
+      prefs::kCloudExtensionRequestEnabled, base::Value(true));
 
-  std::unique_ptr<base::Value> requests =
-      std::make_unique<base::Value>(base::Value::Type::DICT);
-  requests->SetKey(extension_id, base::Value());
+  auto requests = base::Value::Dict().Set(extension_id, base::Value::Dict());
   profile->GetTestingPrefService()->SetUserPref(
       prefs::kCloudExtensionRequestIds, std::move(requests));
 
diff --git a/chrome/browser/extensions/api/management/management_api_unittest.cc b/chrome/browser/extensions/api/management/management_api_unittest.cc
index 9096df8f..2bc06aa 100644
--- a/chrome/browser/extensions/api/management/management_api_unittest.cc
+++ b/chrome/browser/extensions/api/management/management_api_unittest.cc
@@ -1031,9 +1031,6 @@
       ExtensionInstalledBlockedByParentDialogAction blocked_action,
       base::OnceClosure done_callback) {
     show_block_dialog_count_++;
-    SupervisedUserExtensionsMetricsRecorder::RecordEnablementUmaMetrics(
-        SupervisedUserExtensionsMetricsRecorder::EnablementState::
-            kFailedToEnable);
     std::move(done_callback).Run();
   }
 
@@ -1135,16 +1132,10 @@
     EXPECT_EQ(supervised_user_delegate_->show_block_dialog_count(), 1);
   }
 
-  histogram_tester.ExpectUniqueSample(
-      SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName,
-      SupervisedUserExtensionsMetricsRecorder::EnablementState::kFailedToEnable,
-      1);
-  histogram_tester.ExpectTotalCount(
-      SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, 1);
-  EXPECT_EQ(
-      1,
-      user_action_tester.GetActionCount(
-          SupervisedUserExtensionsMetricsRecorder::kFailedToEnableActionName));
+  // Metrics reporting cannot be tested here, because the current implementation
+  // of `TestSupervisedUserExtensionsDelegate` overrides the
+  // `ShowInstallBlockedByParentDialogForExtension` method that records the
+  // metric in the production code.
 }
 
 // Tests enabling an extension via management API after it was disabled due to
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 0fa11b7..426b7c2 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -1060,6 +1060,9 @@
   (*s_allowlist)
       [performance_manager::user_tuning::prefs::kHighEfficiencyModeState] =
           settings_api::PrefType::PREF_TYPE_NUMBER;
+  (*s_allowlist)[performance_manager::user_tuning::prefs::
+                     kHighEfficiencyModeTimeBeforeDiscardInMinutes] =
+      settings_api::PrefType::PREF_TYPE_NUMBER;
   (*s_allowlist)
       [performance_manager::user_tuning::prefs::kBatterySaverModeState] =
           settings_api::PrefType::PREF_TYPE_NUMBER;
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 8653876..801fa74e 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -430,7 +430,7 @@
     "Enable mandatory re-auth for payments autofill";
 const char kAutofillEnablePaymentsMandatoryReauthDescription[] =
     "When enabled, in use-cases where we would not have triggered any "
-    "user-visible authentication to autofill payment methods, we will trigger "
+    "interactive authentication to autofill payment methods, we will trigger "
     "a device authentication.";
 
 const char kAutofillEnableRankingFormulaAddressProfilesName[] =
diff --git a/chrome/browser/lifetime/application_lifetime_browsertest.cc b/chrome/browser/lifetime/application_lifetime_browsertest.cc
index 5624f9f..808400e 100644
--- a/chrome/browser/lifetime/application_lifetime_browsertest.cc
+++ b/chrome/browser/lifetime/application_lifetime_browsertest.cc
@@ -108,10 +108,10 @@
       ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
   content::WebContents* tab =
       browser()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_TRUE(content::ExecuteScript(
-      tab,
-      "window.addEventListener('beforeunload',"
-      "function(event) { event.returnValue = 'Foo'; });"));
+  ASSERT_TRUE(
+      content::ExecJs(tab,
+                      "window.addEventListener('beforeunload',"
+                      "function(event) { event.returnValue = 'Foo'; });"));
   content::PrepContentsForBeforeUnloadTest(tab);
   content::GetUIThreadTaskRunner({})->PostTask(
       FROM_HERE, base::BindOnce(&chrome::RelaunchIgnoreUnloadHandlers));
diff --git a/chrome/browser/lifetime/browser_close_manager_browsertest.cc b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
index c31dca5..51fc930 100644
--- a/chrome/browser/lifetime/browser_close_manager_browsertest.cc
+++ b/chrome/browser/lifetime/browser_close_manager_browsertest.cc
@@ -790,10 +790,10 @@
   EXPECT_EQ(2u, BrowserList::GetInstance()->size());
   // Add beforeunload handler for the 2nd (title2.html) tab which haven't had it
   // yet.
-  ASSERT_TRUE(content::ExecuteScript(
-      browser2->tab_strip_model()->GetWebContentsAt(1),
-      "window.addEventListener('beforeunload', "
-      "function(event) { event.returnValue = 'Foo'; });"));
+  ASSERT_TRUE(
+      content::ExecJs(browser2->tab_strip_model()->GetWebContentsAt(1),
+                      "window.addEventListener('beforeunload', "
+                      "function(event) { event.returnValue = 'Foo'; });"));
   EXPECT_TRUE(browser2->tab_strip_model()
                   ->GetWebContentsAt(1)
                   ->NeedToFireBeforeUnloadOrUnloadEvents());
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc b/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
index a470c255..93c8c2a 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
@@ -223,9 +223,9 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(incognito, url));
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_TRUE(content::ExecuteScript(
-      incognito->tab_strip_model()->GetActiveWebContents(),
-      "document.getElementById('google').click();"));
+  EXPECT_TRUE(
+      content::ExecJs(incognito->tab_strip_model()->GetActiveWebContents(),
+                      "document.getElementById('google').click();"));
   base::RunLoop().RunUntilIdle();
 
   auto entries = test_ukm_recorder->GetMergedEntriesByName(
@@ -249,9 +249,9 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_TRUE(content::ExecuteScript(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      "document.getElementById('google').click();"));
+  EXPECT_TRUE(
+      content::ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
+                      "document.getElementById('google').click();"));
   base::RunLoop().RunUntilIdle();
 
   auto entries = test_ukm_recorder->GetMergedEntriesByName(
@@ -421,9 +421,9 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
   WaitLinkEnteredViewport(1);
 
-  EXPECT_TRUE(content::ExecuteScript(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      "document.getElementById('google').click();"));
+  EXPECT_TRUE(
+      content::ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
+                      "document.getElementById('google').click();"));
   base::RunLoop().RunUntilIdle();
 
   // Navigate to another page.
@@ -469,9 +469,9 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(incognito, url));
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_TRUE(content::ExecuteScript(
-      incognito->tab_strip_model()->GetActiveWebContents(),
-      "document.getElementById('google').click();"));
+  EXPECT_TRUE(
+      content::ExecJs(incognito->tab_strip_model()->GetActiveWebContents(),
+                      "document.getElementById('google').click();"));
   content::WaitForLoadStop(
       incognito->tab_strip_model()->GetActiveWebContents());
 
diff --git a/chrome/browser/new_tab_page/modules/drive/drive_service.cc b/chrome/browser/new_tab_page/modules/drive/drive_service.cc
index a8d9fee..1f8fa4b 100644
--- a/chrome/browser/new_tab_page/modules/drive/drive_service.cc
+++ b/chrome/browser/new_tab_page/modules/drive/drive_service.cc
@@ -280,6 +280,8 @@
   url_loader_.reset();
 
   if (net_error != net::OK || !response_body) {
+    base::UmaHistogramEnumeration("NewTabPage.Drive.ItemSuggestRequestResult",
+                                  ItemSuggestRequestResult::kNetworkError);
     for (auto& callback : callbacks_) {
       std::move(callback).Run(std::vector<drive::mojom::FilePtr>());
     }
@@ -297,6 +299,8 @@
 void DriveService::OnJsonParsed(
     data_decoder::DataDecoder::ValueOrError result) {
   if (!result.has_value()) {
+    base::UmaHistogramEnumeration("NewTabPage.Drive.ItemSuggestRequestResult",
+                                  ItemSuggestRequestResult::kJsonParseError);
     for (auto& callback : callbacks_) {
       std::move(callback).Run(std::vector<drive::mojom::FilePtr>());
     }
@@ -305,12 +309,15 @@
   }
   auto* items = result->GetDict().FindList("item");
   if (!items) {
+    base::UmaHistogramEnumeration("NewTabPage.Drive.ItemSuggestRequestResult",
+                                  ItemSuggestRequestResult::kContentError);
     for (auto& callback : callbacks_) {
       std::move(callback).Run(std::vector<drive::mojom::FilePtr>());
     }
     callbacks_.clear();
     return;
   }
+  ItemSuggestRequestResult request_result = ItemSuggestRequestResult::kSuccess;
   std::vector<drive::mojom::FilePtr> document_list;
   for (const auto& item : *items) {
     const auto& item_dict = item.GetDict();
@@ -320,12 +327,14 @@
         "justification.unstructuredJustificationDescription.textSegment");
     if (!justification_text_segments ||
         justification_text_segments->size() == 0) {
+      request_result = ItemSuggestRequestResult::kContentError;
       continue;
     }
     std::string justification_text;
     for (auto& text_segment : *justification_text_segments) {
       auto* justification_text_path = text_segment.GetDict().FindString("text");
       if (!justification_text_path) {
+        request_result = ItemSuggestRequestResult::kContentError;
         continue;
       }
       justification_text += *justification_text_path;
@@ -334,6 +343,7 @@
     auto* item_url = item_dict.FindString("url");
     if (!title || !mime_type || justification_text.empty() || !id ||
         !item_url || !GURL(*item_url).is_valid()) {
+      request_result = ItemSuggestRequestResult::kContentError;
       continue;
     }
     auto mojo_drive_doc = drive::mojom::File::New();
@@ -344,6 +354,8 @@
     mojo_drive_doc->item_url = GURL(*item_url);
     document_list.push_back(std::move(mojo_drive_doc));
   }
+  base::UmaHistogramEnumeration("NewTabPage.Drive.ItemSuggestRequestResult",
+                                request_result);
   for (auto& callback : callbacks_) {
     std::move(callback).Run(mojo::Clone(document_list));
   }
diff --git a/chrome/browser/new_tab_page/modules/drive/drive_service.h b/chrome/browser/new_tab_page/modules/drive/drive_service.h
index d395310..f8ae486 100644
--- a/chrome/browser/new_tab_page/modules/drive/drive_service.h
+++ b/chrome/browser/new_tab_page/modules/drive/drive_service.h
@@ -27,6 +27,16 @@
 class PrimaryAccountAccessTokenFetcher;
 }  // namespace signin
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class ItemSuggestRequestResult {
+  kSuccess = 0,
+  kNetworkError = 1,
+  kJsonParseError = 2,
+  kContentError = 3,
+  kMaxValue = kContentError,
+};
+
 // Handles requests for user Google Drive data.
 class DriveService : public KeyedService {
  public:
diff --git a/chrome/browser/new_tab_page/modules/drive/drive_service_unittest.cc b/chrome/browser/new_tab_page/modules/drive/drive_service_unittest.cc
index 031f8aa9b..a30d239 100644
--- a/chrome/browser/new_tab_page/modules/drive/drive_service_unittest.cc
+++ b/chrome/browser/new_tab_page/modules/drive/drive_service_unittest.cc
@@ -147,6 +147,11 @@
   ASSERT_EQ(1,
             histogram_tester_.GetBucketCount("NewTabPage.Modules.DataRequest",
                                              base::PersistentHash("drive")));
+  // The third item is malformed. So, even though we can display the first two
+  // items, we report a content error.
+  ASSERT_EQ(1, histogram_tester_.GetBucketCount(
+                   "NewTabPage.Drive.ItemSuggestRequestResult",
+                   ItemSuggestRequestResult::kContentError));
 }
 
 TEST_F(DriveServiceTest, PassesDataToMultipleRequestsToDriveService) {
@@ -246,6 +251,9 @@
   ASSERT_EQ(1,
             histogram_tester_.GetBucketCount("NewTabPage.Modules.DataRequest",
                                              base::PersistentHash("drive")));
+  ASSERT_EQ(1, histogram_tester_.GetBucketCount(
+                   "NewTabPage.Drive.ItemSuggestRequestResult",
+                   ItemSuggestRequestResult::kSuccess));
 }
 
 TEST_F(DriveServiceTest, PassesCachedDataIfRequested) {
@@ -453,6 +461,9 @@
   ASSERT_EQ(1,
             histogram_tester_.GetBucketCount("NewTabPage.Modules.DataRequest",
                                              base::PersistentHash("drive")));
+  ASSERT_EQ(1, histogram_tester_.GetBucketCount(
+                   "NewTabPage.Drive.ItemSuggestRequestResult",
+                   ItemSuggestRequestResult::kNetworkError));
 }
 
 TEST_F(DriveServiceTest, PassesNoDataOnEmptyResponse) {
@@ -480,6 +491,9 @@
   ASSERT_EQ(1,
             histogram_tester_.GetBucketCount("NewTabPage.Modules.DataRequest",
                                              base::PersistentHash("drive")));
+  ASSERT_EQ(1, histogram_tester_.GetBucketCount(
+                   "NewTabPage.Drive.ItemSuggestRequestResult",
+                   ItemSuggestRequestResult::kJsonParseError));
 }
 
 TEST_F(DriveServiceTest, PassesNoDataOnMissingItemKey) {
@@ -510,6 +524,9 @@
   ASSERT_EQ(1,
             histogram_tester_.GetBucketCount("NewTabPage.Modules.DataRequest",
                                              base::PersistentHash("drive")));
+  ASSERT_EQ(1, histogram_tester_.GetBucketCount(
+                   "NewTabPage.Drive.ItemSuggestRequestResult",
+                   ItemSuggestRequestResult::kContentError));
 }
 
 TEST_F(DriveServiceTest, DismissModule) {
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom
index 8c7c0113..3af591c 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters.mojom
@@ -20,8 +20,9 @@
   // Open or make visible the Journeys UI on the Side Panel.
   ShowJourneysSidePanel(string query);
 
-  // Open the URLs as a new tab group and clobber the existing NTP.
-  OpenUrlsInTabGroup(array<url.mojom.Url> urls);
+  // Open the URLs as a new tab group and clobber the existing NTP. The new tab
+  // group has an optional name `tab_group_name`.
+  OpenUrlsInTabGroup(array<url.mojom.Url> urls, string? tab_group_name);
 
   // Dismisses a cluster by marking its associated visits as not relevant.
   DismissCluster(array<history_clusters.mojom.URLVisit> visits);
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
index 5e0c7f82..1b2ac69 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.cc
@@ -23,6 +23,8 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/side_panel/history_clusters/history_clusters_tab_helper.h"
+#include "chrome/browser/ui/tabs/tab_group.h"
+#include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "components/history/core/browser/url_row.h"
 #include "components/history_clusters/core/history_cluster_type_utils.h"
 #include "components/history_clusters/core/history_clusters_service.h"
@@ -217,7 +219,12 @@
 }
 
 void HistoryClustersPageHandler::OpenUrlsInTabGroup(
-    const std::vector<GURL>& urls) {
+    const std::vector<GURL>& urls,
+    const absl::optional<std::string>& tab_group_name) {
+  // This method is different from HistoryClustersHandler::OpenUrlsInTabGroup:
+  //  - It takes over the current tab instead of opening new background tabs.
+  //  - It inserts the new tabs into the location of the current tab.
+
   if (urls.empty()) {
     return;
   }
@@ -250,7 +257,17 @@
 
   tab_indices.insert(tab_indices.begin(), model->GetIndexOfWebContents(
                                               model->GetActiveWebContents()));
-  model->AddToNewGroup(tab_indices);
+
+  auto new_group_id = model->AddToNewGroup(tab_indices);
+  if (!new_group_id.is_empty() && tab_group_name) {
+    if (auto* group_model = model->group_model()) {
+      auto* tab_group = group_model->GetTabGroup(new_group_id);
+      // Copy and modify the existing visual data with a new title.
+      tab_groups::TabGroupVisualData visual_data = *tab_group->visual_data();
+      visual_data.SetTitle(base::UTF8ToUTF16(*tab_group_name));
+      tab_group->SetVisualData(visual_data);
+    }
+  }
 
   if (tab_indices.size() > 1) {
     model->ActivateTabAt(tab_indices.at(1));
diff --git a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.h b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.h
index e19c0d7e..36a1cd7c 100644
--- a/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.h
+++ b/chrome/browser/new_tab_page/modules/history_clusters/history_clusters_page_handler.h
@@ -44,7 +44,9 @@
   void GetCartForCluster(history_clusters::mojom::ClusterPtr cluster,
                          GetCartForClusterCallback callback) override;
   void ShowJourneysSidePanel(const std::string& query) override;
-  void OpenUrlsInTabGroup(const std::vector<GURL>&) override;
+  void OpenUrlsInTabGroup(const std::vector<GURL>& urls,
+                          const absl::optional<std::string>& tab_group_name =
+                              absl::nullopt) override;
   void DismissCluster(
       const std::vector<history_clusters::mojom::URLVisitPtr> visits) override;
 
diff --git a/chrome/browser/paint_preview/android/BUILD.gn b/chrome/browser/paint_preview/android/BUILD.gn
index f03e05b..d10ad4cb 100644
--- a/chrome/browser/paint_preview/android/BUILD.gn
+++ b/chrome/browser/paint_preview/android/BUILD.gn
@@ -83,10 +83,12 @@
     "//content/public/android:content_java",
     "//content/public/test/android:content_java_test_support",
     "//net/android:net_java_test_support",
+    "//third_party/android_deps:espresso_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/androidx:androidx_test_monitor_java",
     "//third_party/androidx:androidx_test_runner_java",
     "//third_party/androidx:androidx_test_uiautomator_uiautomator_java",
+    "//third_party/hamcrest:hamcrest_library_java",
     "//third_party/junit",
     "//third_party/mockito:mockito_java",
     "//ui/android:ui_java_test_support",
diff --git a/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewTest.java b/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewTest.java
index 5fb4156d..1fb6c0b 100644
--- a/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewTest.java
+++ b/chrome/browser/paint_preview/android/javatests/src/org/chromium/chrome/browser/paint_preview/StartupPaintPreviewTest.java
@@ -4,17 +4,19 @@
 
 package org.chromium.chrome.browser.paint_preview;
 
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.longClick;
+
 import static org.chromium.base.test.util.Batch.PER_CLASS;
 import static org.chromium.chrome.browser.paint_preview.TabbedPaintPreviewTest.assertAttachedAndShown;
 
-import android.os.SystemClock;
-import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 
 import androidx.test.filters.MediumTest;
 
+import org.hamcrest.Matchers;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.Before;
@@ -28,7 +30,9 @@
 
 import org.chromium.base.test.util.Batch;
 import org.chromium.base.test.util.CallbackHelper;
+import org.chromium.base.test.util.Criteria;
 import org.chromium.base.test.util.CriteriaHelper;
+import org.chromium.base.test.util.DisabledTest;
 import org.chromium.chrome.browser.paint_preview.services.PaintPreviewTabService;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
@@ -96,6 +100,7 @@
 
     @Test
     @MediumTest
+    @DisabledTest(message = "https://crbug.com/1444246")
     public void testSnackbarShow() throws ExecutionException, InterruptedException {
         Tab tab = sActivityTestRule.getActivity().getActivityTab();
         StartupPaintPreview startupPaintPreview = TestThreadUtils.runOnUiThreadBlocking(
@@ -111,20 +116,20 @@
         View view = tabbedPaintPreview.getViewForTesting();
 
         // First tap.
-        simulateTap(view, view.getWidth() / 2f, view.getHeight() / 2f);
+        onView(Matchers.is(view)).perform(click());
         assertSnackbarVisibility(snackbarManager, false);
         // Second tap.
-        simulateTap(view, view.getWidth() / 2f, view.getHeight() / 2f);
+        onView(Matchers.is(view)).perform(click());
         assertSnackbarVisibility(snackbarManager, false);
         // Third tap.
-        simulateTap(view, view.getWidth() / 2f, view.getHeight() / 2f);
+        onView(Matchers.is(view)).perform(click());
         assertSnackbarVisibility(snackbarManager, true);
 
         TestThreadUtils.runOnUiThreadBlocking(snackbarManager::dismissAllSnackbars);
         assertSnackbarVisibility(snackbarManager, false);
 
         // Simulate long press.
-        simulateLongPress(view, view.getWidth() / 2f, view.getHeight() / 2f);
+        onView(Matchers.is(view)).perform(longClick());
         assertSnackbarVisibility(snackbarManager, true);
     }
 
@@ -193,10 +198,11 @@
         showAndWaitForInflation(startupPaintPreview, tabbedPaintPreview, dismissCallback);
         SnackbarManager snackbarManager = sActivityTestRule.getActivity().getSnackbarManager();
         View view = tabbedPaintPreview.getViewForTesting();
-        simulateLongPress(view, view.getWidth() / 2f, view.getHeight() / 2f);
+        onView(Matchers.is(view)).perform(longClick());
         assertSnackbarVisibility(snackbarManager, true);
-        TestThreadUtils.runOnUiThreadBlocking(()->
-                snackbarManager.getCurrentSnackbarForTesting().getController().onAction(null));
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            snackbarManager.getCurrentSnackbarForTesting().getController().onAction(null);
+        });
         assertAttachedAndShown(tabbedPaintPreview, false, false);
         Assert.assertEquals(
                 "Dismiss callback should have been called.", 1, dismissCallback.getCallCount());
@@ -227,42 +233,24 @@
     private void assertSnackbarVisibility(SnackbarManager snackbarManager, boolean visible) {
         String message =
                 visible ? "Snackbar should be visible." : "Snackbar should not be visible.";
-        CriteriaHelper.pollUiThread(() -> snackbarManager.isShowing() == visible, message);
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat(message, snackbarManager.isShowing(), Matchers.is(visible));
+        });
     }
 
     private void showAndWaitForInflation(StartupPaintPreview startupPaintPreview,
             TabbedPaintPreview tabbedPaintPreview, CallbackHelper dismissCallback) {
-        TestThreadUtils.runOnUiThreadBlocking(
-                ()
-                        -> startupPaintPreview.show(
-                                dismissCallback == null ? null : dismissCallback::notifyCalled));
+        TestThreadUtils.runOnUiThreadBlocking(() -> {
+            startupPaintPreview.show(
+                    dismissCallback == null ? null : dismissCallback::notifyCalled);
+        });
         assertAttachedAndShown(tabbedPaintPreview, true, true);
-        CriteriaHelper.pollUiThread(()
-                                            -> tabbedPaintPreview.getViewForTesting() != null
-                        && ((ViewGroup) tabbedPaintPreview.getViewForTesting()).getChildCount() > 0,
-                "TabbedPaintPreview either doesn't have a view or a view with 0 children.");
-    }
-
-    private void simulateTap(View view, float x, float y)
-            throws ExecutionException, InterruptedException {
-        simulateTapWithDuration(view, x, y, 50);
-    }
-
-    private void simulateLongPress(View view, float x, float y)
-            throws ExecutionException, InterruptedException {
-        Thread.sleep(100);
-        simulateTapWithDuration(view, x, y, ViewConfiguration.getLongPressTimeout() + 200);
-    }
-
-    private void simulateTapWithDuration(View view, float x, float y, long holdDurationMs)
-            throws ExecutionException, InterruptedException {
-        long downTime = SystemClock.uptimeMillis();
-        MotionEvent down = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0);
-        TestThreadUtils.runOnUiThreadBlocking(() -> view.dispatchTouchEvent(down));
-
-        Thread.sleep(holdDurationMs);
-        MotionEvent up = MotionEvent.obtain(
-                downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0);
-        TestThreadUtils.runOnUiThreadBlocking(() -> view.dispatchTouchEvent(up));
+        CriteriaHelper.pollUiThread(() -> {
+            Criteria.checkThat("TabbedPaintPreview has no view",
+                    tabbedPaintPreview.getViewForTesting(), Matchers.notNullValue());
+            Criteria.checkThat("TabbedPaintPreview has 0 children",
+                    ((ViewGroup) tabbedPaintPreview.getViewForTesting()).getChildCount(),
+                    Matchers.not(0));
+        });
     }
 }
diff --git a/chrome/browser/payments/payment_handler_change_payment_method_browsertest.cc b/chrome/browser/payments/payment_handler_change_payment_method_browsertest.cc
index a087c88..061a58d 100644
--- a/chrome/browser/payments/payment_handler_change_payment_method_browsertest.cc
+++ b/chrome/browser/payments/payment_handler_change_payment_method_browsertest.cc
@@ -33,8 +33,8 @@
   std::string method_name;
   InstallPaymentApp("a.com", "/change_payment_method_app.js", &method_name);
 
-  ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
-                                     GetParam().init_test_code));
+  ASSERT_TRUE(
+      content::ExecJs(GetActiveWebContents(), GetParam().init_test_code));
 
   std::string actual_output =
       content::EvalJs(GetActiveWebContents(),
diff --git a/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc
index 3468a0be..9e198ac 100644
--- a/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/document_picture_in_picture_window_controller_browsertest.cc
@@ -71,7 +71,10 @@
 #include "ui/views/widget/widget_observer.h"
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chromeos/ui/base/chromeos_ui_constants.h"
+#include "ui/aura/window.h"
 #include "ui/base/hit_test.h"
+#include "ui/events/test/event_generator.h"
 #endif
 
 using content::EvalJs;
@@ -490,3 +493,39 @@
 
   EXPECT_EQ(false, pip_frame_view->frame()->IsMenuRunnerRunningForTesting());
 }
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+// Verify that it is possible to resize a document picture in picture window
+// using the resize outside bound in ChromeOS ASH.
+IN_PROC_BROWSER_TEST_F(DocumentPictureInPictureWindowControllerBrowserTest,
+                       CanResizeUsingOutsideBounds) {
+  // Attempt to create a Document PiP window with the minimum window size.
+  LoadTabAndEnterPictureInPicture(
+      browser(), PictureInPictureWindowManager::GetMinimumInnerWindowSize());
+  auto* pip_web_contents = window_controller()->GetChildWebContents();
+  ASSERT_NE(nullptr, pip_web_contents);
+  WaitForPageLoad(pip_web_contents);
+
+  // Get a point within the resize outside bounds.
+  auto* browser_view = static_cast<BrowserView*>(
+      BrowserWindow::FindBrowserWindowWithWebContents(pip_web_contents));
+  const auto left_center_point = browser_view->GetBounds().left_center();
+  const auto resize_outside_bound_point =
+      gfx::Point(left_center_point.x() - chromeos::kResizeInsideBoundsSize -
+                     chromeos::kResizeOutsideBoundsSize / 2,
+                 left_center_point.y());
+
+  // Perform a click on the left resize outside bound, followed by a drag to the
+  // left.
+  aura::Window* window = browser_view->GetNativeWindow();
+  const auto initial_window_size = window->GetBoundsInScreen().size();
+  ui::test::EventGenerator event_generator(window->GetRootWindow());
+  event_generator.set_current_screen_location(resize_outside_bound_point);
+  const int drag_distance = 10;
+  event_generator.DragMouseBy(-drag_distance, 0);
+
+  // Verify that the rezise took place.
+  const auto expected_size = initial_window_size + gfx::Size(drag_distance, 0);
+  ASSERT_EQ(expected_size, window->GetBoundsInScreen().size());
+}
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/chrome/browser/printing/print_job_worker_oop.cc b/chrome/browser/printing/print_job_worker_oop.cc
index 632a59a..43ff03c5 100644
--- a/chrome/browser/printing/print_job_worker_oop.cc
+++ b/chrome/browser/printing/print_job_worker_oop.cc
@@ -145,9 +145,7 @@
     PRINTER_LOG(ERROR) << "Unable to get page " << page_index
                        << " via service for document "
                        << document_oop_->cookie();
-    task_runner()->PostTask(FROM_HERE,
-                            base::BindOnce(&PrintJobWorkerOop::OnFailure,
-                                           worker_weak_factory_.GetWeakPtr()));
+    NotifyFailure(mojom::ResultCode::kFailed);
     return;
   }
   VLOG(1) << "Rendered printed page via service for document "
@@ -204,7 +202,8 @@
   document_oop_ = nullptr;
 }
 
-void PrintJobWorkerOop::OnDidCancel(scoped_refptr<PrintJob> job) {
+void PrintJobWorkerOop::OnDidCancel(scoped_refptr<PrintJob> job,
+                                    mojom::ResultCode cancel_reason) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DVLOG(1) << "Cancel completed for printing via service for document "
            << document_oop_->cookie();
@@ -212,6 +211,17 @@
   UnregisterServiceManagerClient();
   printing_context_id_.reset();
 
+  if (cancel_reason == mojom::ResultCode::kCanceled) {
+    print_job()->PostTask(FROM_HERE,
+                          base::BindOnce(&PrintJob::Cancel, std::move(job)));
+  } else {
+    PostTask(FROM_HERE, base::BindOnce(
+                            [](base::WeakPtr<PrintJobWorkerOop> self,
+                               scoped_refptr<PrintJob> job) {
+                              self->PrintJobWorker::OnFailure();
+                            },
+                            worker_weak_factory_.GetWeakPtr(), std::move(job)));
+  }
   // Done with private document reference.
   document_oop_ = nullptr;
 }
@@ -287,23 +297,17 @@
 }
 
 void PrintJobWorkerOop::OnCancel() {
-  // Retain a reference to the PrintJob to ensure it doesn't get deleted before
-  // the `OnDidCancel()` callback occurs.
   content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&PrintJobWorkerOop::SendCancel,
+      FROM_HERE, base::BindOnce(&PrintJobWorkerOop::NotifyFailure,
                                 ui_weak_factory_.GetWeakPtr(),
-                                base::WrapRefCounted(print_job())));
-  PrintJobWorker::OnCancel();
+                                mojom::ResultCode::kCanceled));
 }
 
 void PrintJobWorkerOop::OnFailure() {
-  // Retain a reference to the PrintJob to ensure it doesn't get deleted before
-  // the `OnDidCancel()` callback occurs.
   content::GetUIThreadTaskRunner({})->PostTask(
-      FROM_HERE, base::BindOnce(&PrintJobWorkerOop::SendCancel,
+      FROM_HERE, base::BindOnce(&PrintJobWorkerOop::NotifyFailure,
                                 ui_weak_factory_.GetWeakPtr(),
-                                base::WrapRefCounted(print_job())));
-  PrintJobWorker::OnFailure();
+                                mojom::ResultCode::kFailed));
 }
 
 void PrintJobWorkerOop::UnregisterServiceManagerClient() {
@@ -349,6 +353,17 @@
 void PrintJobWorkerOop::NotifyFailure(mojom::ResultCode result) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
+  // If an error has occurred during rendering in middle of a multi-page job,
+  // it could be possible for the `OnDidRenderPrintedPage()` callback of latter
+  // pages to still go through error processing.  In such a case the document
+  // might already have been canceled, so we should ensure to only send a
+  // cancel request to the service if we haven't already done so.
+  if (print_cancel_requested_) {
+    return;
+  }
+
+  print_cancel_requested_ = true;
+
   PrintOopResult uma_result = PrintOopResult::kFailed;
   if (result == mojom::ResultCode::kAccessDenied) {
     // An attempt to restart could be undesirable if some pages were able to
@@ -370,15 +385,9 @@
   base::UmaHistogramEnumeration(kPrintOopPrintResultHistogramName, uma_result);
 
   // Initiate rest of regular failure handling.
-  if (result == mojom::ResultCode::kCanceled) {
-    task_runner()->PostTask(FROM_HERE,
-                            base::BindOnce(&PrintJobWorkerOop::OnCancel,
-                                           worker_weak_factory_.GetWeakPtr()));
-  } else {
-    task_runner()->PostTask(FROM_HERE,
-                            base::BindOnce(&PrintJobWorkerOop::OnFailure,
-                                           worker_weak_factory_.GetWeakPtr()));
-  }
+  SendCancel(base::BindOnce(&PrintJobWorkerOop::OnDidCancel,
+                            ui_weak_factory_.GetWeakPtr(),
+                            base::WrapRefCounted(print_job()), result));
 }
 
 void PrintJobWorkerOop::SendEstablishPrintingContext() {
@@ -500,18 +509,8 @@
                                           printing_context()->job_id()));
 }
 
-void PrintJobWorkerOop::SendCancel(scoped_refptr<PrintJob> job) {
+void PrintJobWorkerOop::SendCancel(base::OnceClosure on_did_cancel_callback) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  // If an error has occurred during rendering in middle of a multi-page job,
-  // it could be possible for the `OnDidRenderPrintedPage()` callback of latter
-  // pages to still go through error processing.  In such a case the document
-  // might already have been canceled, so we should ensure to only send a
-  // cancel request to the service if we haven't already done so.
-  if (print_cancel_requested_)
-    return;
-
-  print_cancel_requested_ = true;
   VLOG(1) << "Sending cancel for document " << document_oop_->cookie();
 
   PrintBackendServiceManager& service_mgr =
@@ -519,10 +518,9 @@
 
   // Retain a reference to the PrintJob to ensure it doesn't get deleted before
   // the `OnDidCancel()` callback occurs.
-  service_mgr.Cancel(
-      *service_manager_client_id_, device_name_, document_oop_->cookie(),
-      base::BindOnce(&PrintJobWorkerOop::OnDidCancel,
-                     ui_weak_factory_.GetWeakPtr(), std::move(job)));
+  service_mgr.Cancel(*service_manager_client_id_, device_name_,
+                     document_oop_->cookie(),
+                     std::move(on_did_cancel_callback));
 }
 
 }  // namespace printing
diff --git a/chrome/browser/printing/print_job_worker_oop.h b/chrome/browser/printing/print_job_worker_oop.h
index e51d45f..95038015 100644
--- a/chrome/browser/printing/print_job_worker_oop.h
+++ b/chrome/browser/printing/print_job_worker_oop.h
@@ -71,7 +71,8 @@
 #endif
   virtual void OnDidRenderPrintedDocument(mojom::ResultCode result);
   virtual void OnDidDocumentDone(int job_id, mojom::ResultCode result);
-  virtual void OnDidCancel(scoped_refptr<PrintJob> job);
+  virtual void OnDidCancel(scoped_refptr<PrintJob> job,
+                           mojom::ResultCode cancel_reason);
 
   // `PrintJobWorker` overrides.
 #if BUILDFLAG(IS_WIN)
@@ -107,7 +108,7 @@
       mojom::MetafileDataType data_type,
       base::ReadOnlySharedMemoryRegion serialized_data);
   void SendDocumentDone();
-  void SendCancel(scoped_refptr<PrintJob> job);
+  void SendCancel(base::OnceClosure on_did_cancel_callback);
 
   // Used to test spooling memory error handling.
   const bool simulate_spooling_memory_errors_;
diff --git a/chrome/browser/printing/system_access_process_print_browsertest.cc b/chrome/browser/printing/system_access_process_print_browsertest.cc
index 36abb6e..a0fabb0 100644
--- a/chrome/browser/printing/system_access_process_print_browsertest.cc
+++ b/chrome/browser/printing/system_access_process_print_browsertest.cc
@@ -208,12 +208,13 @@
     callbacks_->did_document_done_callback.Run(result);
   }
 
-  void OnDidCancel(scoped_refptr<PrintJob> job) override {
+  void OnDidCancel(scoped_refptr<PrintJob> job,
+                   mojom::ResultCode result) override {
     DVLOG(1) << "Observed: cancel";
     // Must not use `std::move(job)`, as that could potentially cause the `job`
     // (and consequentially `this`) to be destroyed before
     // `did_cancel_callback` is run.
-    PrintJobWorkerOop::OnDidCancel(job);
+    PrintJobWorkerOop::OnDidCancel(job, result);
     callbacks_->did_cancel_callback.Run();
   }
 
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index e609fb2..c62c3cc 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -1782,6 +1782,9 @@
   }
 
   menu_model_.AddItem(GetSearchForImageIdc(), menu_string);
+  if (companion::IsSearchImageInCompanionSidePanelSupported(GetBrowser())) {
+    menu_model_.SetIsNewFeatureAt(menu_model_.GetItemCount() - 1, true);
+  }
 
   if (base::FeatureList::IsEnabled(lens::features::kLensStandalone) &&
       base::FeatureList::IsEnabled(lens::features::kEnableImageTranslate) &&
@@ -2064,6 +2067,9 @@
           l10n_util::GetStringFUTF16(IDS_CONTENT_CONTEXT_SEARCHWEBFOR,
                                      default_provider->short_name(),
                                      printable_selection_text));
+      if (companion::IsSearchWebInCompanionSidePanelSupported(GetBrowser())) {
+        menu_model_.SetIsNewFeatureAt(menu_model_.GetItemCount() - 1, true);
+      }
     }
   } else {
     if ((selection_navigation_url_ != params_.link_url) &&
@@ -2316,6 +2322,9 @@
     menu_model_.AddItem(GetRegionSearchIdc(),
                         l10n_util::GetStringFUTF16(
                             resource_id, GetImageSearchProviderName(provider)));
+    if (companion::IsSearchImageInCompanionSidePanelSupported(GetBrowser())) {
+      menu_model_.SetIsNewFeatureAt(menu_model_.GetItemCount() - 1, true);
+    }
   }
 }
 
diff --git a/chrome/browser/resources/connectors_internals/device_trust_connector.html b/chrome/browser/resources/connectors_internals/device_trust_connector.html
index 9bb2f5a..e01bbc9 100644
--- a/chrome/browser/resources/connectors_internals/device_trust_connector.html
+++ b/chrome/browser/resources/connectors_internals/device_trust_connector.html
@@ -40,6 +40,17 @@
     </div>
   </div>
 </div>
+<div id="consent-details">
+  <div>
+    Consent Was Received: <span id="consent-received" class="bold"></span>
+  </div>
+  <div>
+    Can Collect Signals: <span id="can-collect" class="bold"></span>
+  </div>
+</div>
+<div id="no-consent-details" class="red hidden">
+  Details about user consent could not be retrieved.
+</div>
 <div>
   Signals:
   <pre id="signals"></pre>
diff --git a/chrome/browser/resources/connectors_internals/device_trust_connector.ts b/chrome/browser/resources/connectors_internals/device_trust_connector.ts
index accad09f..4bdabc5 100644
--- a/chrome/browser/resources/connectors_internals/device_trust_connector.ts
+++ b/chrome/browser/resources/connectors_internals/device_trust_connector.ts
@@ -4,7 +4,7 @@
 
 import {CustomElement} from 'chrome://resources/js/custom_element.js';
 
-import {DeviceTrustState, Int32Value, KeyInfo, KeyManagerInitializedValue, KeyManagerPermanentFailure, KeyTrustLevel, KeyType, PageHandler, PageHandlerInterface} from './connectors_internals.mojom-webui.js';
+import {ConsentMetadata, DeviceTrustState, Int32Value, KeyInfo, KeyManagerInitializedValue, KeyManagerPermanentFailure, KeyTrustLevel, KeyType, PageHandler, PageHandlerInterface} from './connectors_internals.mojom-webui.js';
 import {getTemplate} from './device_trust_connector.html.js';
 
 const TrustLevelStringMap = {
@@ -42,13 +42,26 @@
     return getTemplate();
   }
 
-  public set enabledString(str: string) {
-    const strEl = (this.$('#enabled-string') as HTMLElement);
-    if (strEl) {
-      strEl.innerText = str;
-    } else {
-      console.error('Could not find #enabled-string element.');
+  public set enabledString(isEnabledString: string) {
+    this.setValueToElement('#enabled-string', isEnabledString);
+  }
+
+  public set consentMetadata(consentMetadata: ConsentMetadata|undefined) {
+    const consentDetailsEl = (this.$('#consent-details') as HTMLElement);
+    const noConsentDetailsEl = (this.$('#no-consent') as HTMLElement);
+    if (!consentMetadata) {
+      this.showElement(noConsentDetailsEl);
+      this.hideElement(consentDetailsEl);
+      return;
     }
+
+    this.showElement(consentDetailsEl);
+    this.hideElement(noConsentDetailsEl);
+
+    this.setValueToElement(
+        '#consent-received', `${consentMetadata.consentReceived}`);
+    this.setValueToElement(
+        '#can-collect', `${consentMetadata.canCollectSignals}`);
   }
 
   public set keyInfo(keyInfo: KeyInfo) {
@@ -145,9 +158,8 @@
     }
 
     this.enabledString = `${state.isEnabled}`;
-
+    this.consentMetadata = state.consentMetadata;
     this.keyInfo = state.keyInfo;
-
     this.signalsString = state.signalsJson;
   }
 
@@ -174,6 +186,15 @@
     element?.classList.add('hidden');
   }
 
+  private setValueToElement(elementId: string, stringValue: string) {
+    const htmlElement = (this.$(elementId) as HTMLElement);
+    if (htmlElement) {
+      htmlElement.innerText = stringValue;
+    } else {
+      console.error(`Could not find ${elementId} element.`);
+    }
+  }
+
   private trustLevelToString(trustLevel: KeyTrustLevel): string {
     return TrustLevelStringMap[trustLevel] || 'invalid';
   }
diff --git a/chrome/browser/resources/new_tab_page/icons/lens_icon.svg b/chrome/browser/resources/new_tab_page/icons/lens_icon.svg
index 02d6691..eaf5abd8 100644
--- a/chrome/browser/resources/new_tab_page/icons/lens_icon.svg
+++ b/chrome/browser/resources/new_tab_page/icons/lens_icon.svg
@@ -1,8 +1,7 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 192">
-  <path fill="none" d="M0 0h192v192H0z"/>
-  <circle cx="96" cy="104.15" r="28" fill="#4285f4"/>
-  <path fill="#ea4335" d="M160 72v64c0 1.69-.34 3.29-.82 4.82-1.57 4.92-5.43 8.78-10.35 10.35-1.53.49-3.13.82-4.82.82H66l16 16h62c4.42 0 8.63-.9 12.46-2.51 3.83-1.62 7.28-3.96 10.17-6.86 1.45-1.45 2.76-3.03 3.91-4.74 2.3-3.4 3.96-7.28 4.81-11.44.43-2.08.65-4.24.65-6.45V84l-6-19-10.82 2.18c.48 1.53.82 3.13.82 4.82z"/>
-  <path fill="#4285f4" d="M32 72c0-1.69.34-3.29.82-4.82 1.57-4.92 5.43-8.78 10.35-10.35 1.54-.49 3.14-.83 4.83-.83h96c1.69 0 3.29.34 4.82.82L149 45l-17-5-16-16H76L60 40H48c-17.67 0-32 14.33-32 32v32l16 16V72z"/>
-  <path fill="#34a853" d="M144 40h-12l16.83 16.83c1.23.39 2.39.93 3.47 1.59 2.16 1.32 3.97 3.13 5.29 5.29.66 1.08 1.2 2.24 1.59 3.47L176 84V72c0-17.67-14.33-32-32-32z"/>
-  <path fill="#fbbc05" d="M48 168h39.89l-16-16H48c-8.82 0-16-7.18-16-16v-23.89l-16-16V136c0 17.67 14.33 32 32 32z"/>
-</svg>
\ No newline at end of file
+<svg xml:space="preserve" width="192" height="192" xmlns="http://www.w3.org/2000/svg">
+  <circle cx="144.07" cy="144" r="16" fill="#34A853"/>
+  <circle cx="96.07" cy="104" r="24" fill="#4285F4"/>
+  <path fill="#EA4335" d="M24 135.2c0 18.11 14.69 32.8 32.8 32.8H96v-16l-40.1-.1c-8.8 0-15.9-8.19-15.9-17.9v-18H24v19.2z"/>
+  <path fill="#FBBC04" d="M168 72.8c0-18.11-14.69-32.8-32.8-32.8H116l20 16c8.8 0 16 8.29 16 18v30h16V72.8z"/>
+  <path fill="#4285F4" d="M112 24H80L68 40H56.8C38.69 40 24 54.69 24 72.8V92h16V74c0-9.71 7.2-18 16-18h80l-24-32z"/>
+</svg>
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
index 483894b..ea133db 100644
--- a/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/history_clusters/module.ts
@@ -227,7 +227,8 @@
   private onOpenAllInTabGroupClick_() {
     const urls = [this.searchResultPage, ...this.cluster.visits].map(
         visit => visit.normalizedUrl);
-    HistoryClustersProxyImpl.getInstance().handler.openUrlsInTabGroup(urls);
+    HistoryClustersProxyImpl.getInstance().handler.openUrlsInTabGroup(
+        urls, this.cluster.tabGroupName ?? null);
   }
 
   private shouldShowCartTile_(cart: Object): boolean {
diff --git a/chrome/browser/resources/new_tab_page/modules/history_clusters_v2/module.ts b/chrome/browser/resources/new_tab_page/modules/history_clusters_v2/module.ts
index 87cb2bb4c..f4af30c 100644
--- a/chrome/browser/resources/new_tab_page/modules/history_clusters_v2/module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/history_clusters_v2/module.ts
@@ -91,7 +91,8 @@
   private onOpenAllInTabGroupClick_() {
     const urls = [this.searchResultsPage_, ...this.cluster.visits].map(
         visit => visit.normalizedUrl);
-    HistoryClustersProxyImpl.getInstance().handler.openUrlsInTabGroup(urls);
+    HistoryClustersProxyImpl.getInstance().handler.openUrlsInTabGroup(
+        urls, this.cluster.tabGroupName ?? null);
   }
 }
 
diff --git a/chrome/browser/resources/settings/BUILD.gn b/chrome/browser/resources/settings/BUILD.gn
index 8e7ef83..9ba4918 100644
--- a/chrome/browser/resources/settings/BUILD.gn
+++ b/chrome/browser/resources/settings/BUILD.gn
@@ -288,6 +288,7 @@
     "on_startup_page/on_startup_browser_proxy.ts",
     "on_startup_page/startup_urls_page_browser_proxy.ts",
     "page_visibility.ts",
+    "performance_page/discard_timer_options.ts",
     "performance_page/performance_browser_proxy.ts",
     "performance_page/performance_metrics_proxy.ts",
     "performance_page/tab_discard_exception_validation_mixin.ts",
diff --git a/chrome/browser/resources/settings/autofill_page/payments_manager_proxy.ts b/chrome/browser/resources/settings/autofill_page/payments_manager_proxy.ts
index 444e657c..1dac5662 100644
--- a/chrome/browser/resources/settings/autofill_page/payments_manager_proxy.ts
+++ b/chrome/browser/resources/settings/autofill_page/payments_manager_proxy.ts
@@ -87,6 +87,12 @@
    * A null response means that there is no platform authenticator.
    */
   isUserVerifyingPlatformAuthenticatorAvailable(): Promise<boolean|null>;
+
+  /**
+   * Authenticate the user via device authentication and flip the mandatory auth
+   * toggle is successful.
+   */
+  authenticateUserAndFlipMandatoryAuthToggle(): void;
 }
 
 /**
@@ -166,6 +172,10 @@
         .isUserVerifyingPlatformAuthenticatorAvailable();
   }
 
+  authenticateUserAndFlipMandatoryAuthToggle() {
+    chrome.autofillPrivate.authenticateUserAndFlipMandatoryAuthToggle();
+  }
+
   static getInstance(): PaymentsManagerProxy {
     return instance || (instance = new PaymentsManagerImpl());
   }
diff --git a/chrome/browser/resources/settings/autofill_page/payments_section.html b/chrome/browser/resources/settings/autofill_page/payments_section.html
index f0cf4b2..105cad54 100644
--- a/chrome/browser/resources/settings/autofill_page/payments_section.html
+++ b/chrome/browser/resources/settings/autofill_page/payments_section.html
@@ -36,7 +36,8 @@
     <settings-toggle-button id="mandatoryAuthToggle"
         no-extension-indicator label="$i18n{enableMandatoryAuthToggleLabel}"
         sub-label="$i18n{enableMandatoryAuthToggleSublabel}"
-        pref="{{prefs.autofill.payment_methods_mandatory_reauth}}">
+        pref="{{prefs.autofill.payment_methods_mandatory_reauth}}"
+        on-settings-boolean-control-change="onMandatoryAuthToggleChange_">
     </settings-toggle-button>
   </template>
 </if>
diff --git a/chrome/browser/resources/settings/autofill_page/payments_section.ts b/chrome/browser/resources/settings/autofill_page/payments_section.ts
index d3d448e5..704f886 100644
--- a/chrome/browser/resources/settings/autofill_page/payments_section.ts
+++ b/chrome/browser/resources/settings/autofill_page/payments_section.ts
@@ -620,6 +620,18 @@
       focusWithoutInk(element);
     }
   }
+
+  /**
+   * Checks for user auth before flipping the mandatory auth toggle.
+   */
+  private onMandatoryAuthToggleChange_(e: Event) {
+    const mandatoryAuthToggle = e.target as SettingsToggleButtonElement;
+    assert(mandatoryAuthToggle);
+    // The toggle is reset to the value when it was clicked.
+    // It will be flipped afterwards if the user auth is successful.
+    mandatoryAuthToggle.checked = !mandatoryAuthToggle.checked;
+    this.paymentsManager_.authenticateUserAndFlipMandatoryAuthToggle();
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/settings/performance_page/discard_timer_options.ts b/chrome/browser/resources/settings/performance_page/discard_timer_options.ts
new file mode 100644
index 0000000..d9abd304
--- /dev/null
+++ b/chrome/browser/resources/settings/performance_page/discard_timer_options.ts
@@ -0,0 +1,47 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {DropdownMenuOptionList} from '/shared/settings/controls/settings_dropdown_menu.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+
+export function getDiscardTimerOptions(): DropdownMenuOptionList {
+  return [
+    {
+      value: 5,
+      name: loadTimeData.getString('tabDiscardTimerFiveMinutes'),
+    },
+    {
+      value: 15,
+      name: loadTimeData.getString('tabDiscardTimerFifteenMinutes'),
+    },
+    {
+      value: 30,
+      name: loadTimeData.getString('tabDiscardTimerThirtyMinutes'),
+    },
+    {
+      value: 60,
+      name: loadTimeData.getString('tabDiscardTimerOneHour'),
+    },
+    {
+      value: 2 * 60,
+      name: loadTimeData.getString('tabDiscardTimerTwoHours'),
+    },
+    {
+      value: 4 * 60,
+      name: loadTimeData.getString('tabDiscardTimerFourHours'),
+    },
+    {
+      value: 8 * 60,
+      name: loadTimeData.getString('tabDiscardTimerEightHours'),
+    },
+    {
+      value: 16 * 60,
+      name: loadTimeData.getString('tabDiscardTimerSixteenHours'),
+    },
+    {
+      value: 24 * 60,
+      name: loadTimeData.getString('tabDiscardTimerTwentyFourHours'),
+    },
+  ];
+}
diff --git a/chrome/browser/resources/settings/performance_page/performance_page.html b/chrome/browser/resources/settings/performance_page/performance_page.html
index aefe30f..70e9dd6 100644
--- a/chrome/browser/resources/settings/performance_page/performance_page.html
+++ b/chrome/browser/resources/settings/performance_page/performance_page.html
@@ -1,10 +1,55 @@
+<style include="cr-shared-style settings-shared">
+  .high-efficiency-radio-group {
+    display: flex;
+    flex-direction: column;
+    padding: 0 var(--cr-section-padding);
+  }
+
+  #enabledOnTimerButton::part(labelWrapper) {
+    align-items: center;
+    display: flex;
+    justify-content: space-between;
+  }
+</style>
 <settings-toggle-button id="toggleButton" on-change="onChange_"
     pref="{{prefs.performance_tuning.high_efficiency_mode.state}}"
     label="$i18n{highEfficiencyModeLabel}"
     sub-label="$i18n{highEfficiencyModeDescription}"
     learn-more-url="$i18n{highEfficiencyLearnMoreUrl}"
     numeric-unchecked-value="[[highEfficiencyModeStateEnum_.DISABLED]]"
-    numeric-checked-value="[[highEfficiencyModeStateEnum_.ENABLED_ON_TIMER]]">
+    numeric-checked-value="[[toggleButtonCheckedValue_(
+        isHighEfficiencyMultistateModeEnabled_)]]">
 </settings-toggle-button>
+<template is="dom-if" if="[[isHighEfficiencyMultistateModeEnabled_]]">
+  <iron-collapse id="radioGroupCollapse"
+      opened="[[isHighEfficiencyModeEnabled_(
+          prefs.performance_tuning.high_efficiency_mode.state.value)]]">
+    <div class="high-efficiency-radio-group">
+      <settings-radio-group id="radioGroup" on-change="onChange_"
+          pref="{{prefs.performance_tuning.high_efficiency_mode.state}}"
+          group-aria-label="$i18n{highEfficiencyModeRadioGroupAriaLabel}">
+        <controlled-radio-button
+            label="$i18n{highEfficiencyModeHeuristicsLabel}"
+            name="[[highEfficiencyModeStateEnum_.ENABLED]]"
+            pref="[[prefs.performance_tuning.high_efficiency_mode.state]]">
+        </controlled-radio-button>
+        <controlled-radio-button id="enabledOnTimerButton"
+            label="$i18n{highEfficiencyModeOnTimerLabel}"
+            name="[[highEfficiencyModeStateEnum_.ENABLED_ON_TIMER]]"
+            pref="[[prefs.performance_tuning.high_efficiency_mode.state]]"
+            exportparts="labelWrapper">
+          <settings-dropdown-menu id="discardTimeDropdown"
+              label="$i18n{highEfficiencyModeOnTimerLabel}"
+              disabled="[[!isHighEfficiencyModeEnabledOnTimer_(
+                  prefs.performance_tuning.high_efficiency_mode.state.value)]]"
+              pref="{{prefs.performance_tuning.high_efficiency_mode.time_before_discard_in_minutes}}"
+              menu-options="[[discardTimerOptions_]]"
+              on-click="onDropdownClick_">
+          </settings-dropdown-menu>
+        </controlled-radio-button>
+      </settings-radio-group>
+    </div>
+  </iron-collapse>
+</template>
 <tab-discard-exception-list id="tabDiscardExceptionsList" prefs="{{prefs}}">
 </tab-discard-exception-list>
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/performance_page/performance_page.ts b/chrome/browser/resources/settings/performance_page/performance_page.ts
index 8cb437e7..cc022e3 100644
--- a/chrome/browser/resources/settings/performance_page/performance_page.ts
+++ b/chrome/browser/resources/settings/performance_page/performance_page.ts
@@ -2,13 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import '/shared/settings/controls/controlled_radio_button.js';
+import '/shared/settings/controls/settings_dropdown_menu.js';
+import '/shared/settings/controls/settings_radio_group.js';
 import '/shared/settings/controls/settings_toggle_button.js';
+import 'chrome://resources/cr_elements/cr_shared_style.css.js';
+import 'chrome://resources/polymer/v3_0/iron-collapse/iron-collapse.js';
+import '../settings_shared.css.js';
 import './tab_discard_exception_list.js';
 
+import {DropdownMenuOptionList} from '/shared/settings/controls/settings_dropdown_menu.js';
 import {SettingsToggleButtonElement} from '/shared/settings/controls/settings_toggle_button.js';
 import {PrefsMixin} from 'chrome://resources/cr_components/settings_prefs/prefs_mixin.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {getDiscardTimerOptions} from './discard_timer_options.js';
 import {HighEfficiencyModeState, PerformanceMetricsProxy, PerformanceMetricsProxyImpl} from './performance_metrics_proxy.js';
 import {getTemplate} from './performance_page.html.js';
 import {TabDiscardExceptionListElement} from './tab_discard_exception_list.js';
@@ -37,6 +46,24 @@
 
   static get properties() {
     return {
+      /**
+       * List of options for the discard timer drop-down menu.
+       */
+      discardTimerOptions_: {
+        readOnly: true,
+        type: Array,
+        value: getDiscardTimerOptions,
+      },
+
+      isHighEfficiencyMultistateModeEnabled_: {
+        readOnly: true,
+        type: Boolean,
+        value() {
+          return loadTimeData.getBoolean(
+              'isHighEfficiencyMultistateModeEnabled');
+        },
+      },
+
       highEfficiencyModeStateEnum_: {
         readOnly: true,
         type: Object,
@@ -48,10 +75,31 @@
   private metricsProxy_: PerformanceMetricsProxy =
       PerformanceMetricsProxyImpl.getInstance();
 
+  private discardTimerOptions_: DropdownMenuOptionList;
+  private isHighEfficiencyMultistateModeEnabled_: boolean;
+
   private onChange_() {
     this.metricsProxy_.recordHighEfficiencyModeChanged(
         this.getPref<number>(HIGH_EFFICIENCY_MODE_PREF).value);
   }
+
+  private toggleButtonCheckedValue_() {
+    return this.isHighEfficiencyMultistateModeEnabled_ ?
+        HighEfficiencyModeState.ENABLED :
+        HighEfficiencyModeState.ENABLED_ON_TIMER;
+  }
+
+  private isHighEfficiencyModeEnabled_(value: number): boolean {
+    return value !== HighEfficiencyModeState.DISABLED;
+  }
+
+  private isHighEfficiencyModeEnabledOnTimer_(value: number): boolean {
+    return value === HighEfficiencyModeState.ENABLED_ON_TIMER;
+  }
+
+  private onDropdownClick_(e: Event) {
+    e.stopPropagation();
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/settings_shared/controls/controlled_radio_button.html b/chrome/browser/resources/settings_shared/controls/controlled_radio_button.html
index b2c2c28..6536fec 100644
--- a/chrome/browser/resources/settings_shared/controls/controlled_radio_button.html
+++ b/chrome/browser/resources/settings_shared/controls/controlled_radio_button.html
@@ -38,7 +38,7 @@
   <div class="disc"></div>
 </div>
 
-<div id="labelWrapper">
+<div id="labelWrapper" part="labelWrapper">
   <span id="label" hidden$="[[!label]]">[[label]]</span>
   <span id="slotted-content">
     <slot></slot>
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index bade9826c..02f832d 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -155,19 +155,14 @@
       const selection = shadowRoot.getSelection();
       assert(selection);
       const {anchorNode, anchorOffset, focusNode, focusOffset} = selection;
-      if (focusNode && anchorNode) {
-        const anchorNodeId = this.domNodeToAxNodeIdMap_.get(anchorNode);
-        const focusNodeId = this.domNodeToAxNodeIdMap_.get(focusNode);
-        assert(anchorNodeId && focusNodeId);
-        chrome.readAnything.onSelectionChange(
-            anchorNodeId, anchorOffset, focusNodeId, focusOffset);
-      } else if (!focusNode && !anchorNode) {
-        // If a non-zero selection exists and then you click inside that
-        // selection area, we get null nodes. We still want to forward this to
-        // readAnything because this causes the selection to clear and we need
-        // to clear the selection on the main page.
-        chrome.readAnything.clearSelection();
+      if (!anchorNode || !focusNode) {
+        return;
       }
+      const anchorNodeId = this.domNodeToAxNodeIdMap_.get(anchorNode);
+      const focusNodeId = this.domNodeToAxNodeIdMap_.get(focusNode);
+      assert(anchorNodeId && focusNodeId);
+      chrome.readAnything.onSelectionChange(
+          anchorNodeId, anchorOffset, focusNodeId, focusOffset);
     };
   }
 
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
index 0eea120..d4faa26c 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything.d.ts
@@ -72,9 +72,6 @@
     // determine which empty state to display.
     function isSelectable(): boolean;
 
-    // Indicates that the Reading mode panel selection was cleared.
-    function clearSelection(): void;
-
     // Called when a user makes a selection change. AnchorNodeID and
     // focusAXNodeID are AXNodeIDs which identify the anchor and focus AXNodes
     // in the main pane. The selection can either be forward or backwards.
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
index a0e5629..341bf7e 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -213,6 +213,14 @@
       ->set_printer_name(printer_name);
 }
 
+void BinaryUploadService::Request::set_printer_type(
+    enterprise_connectors::ContentMetaData::PrintMetadata::PrinterType
+        printer_type) {
+  content_analysis_request_.mutable_request_data()
+      ->mutable_print_metadata()
+      ->set_printer_type(printer_type);
+}
+
 std::string BinaryUploadService::Request::SetRandomRequestToken() {
   DCHECK(request_token().empty());
 
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
index 7c3ccb911..c459f9bc 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
@@ -169,6 +169,9 @@
     void set_user_action_requests_count(uint64_t user_action_requests_count);
     void set_tab_url(const GURL& tab_url);
     void set_printer_name(const std::string& printer_name);
+    void set_printer_type(
+        enterprise_connectors::ContentMetaData::PrintMetadata::PrinterType
+            printer_type);
 
     std::string SetRandomRequestToken();
 
diff --git a/chrome/browser/search_engines/template_url_scraper_browsertest.cc b/chrome/browser/search_engines/template_url_scraper_browsertest.cc
index d73ecb36..fa1abcb 100644
--- a/chrome/browser/search_engines/template_url_scraper_browsertest.cc
+++ b/chrome/browser/search_engines/template_url_scraper_browsertest.cc
@@ -113,7 +113,7 @@
   content::WebContents* web_contents =
       browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver observer(web_contents);
-  EXPECT_TRUE(content::ExecuteScript(web_contents, "submit_form()"));
+  EXPECT_TRUE(content::ExecJs(web_contents, "submit_form()"));
   observer.Wait();
 
   all_urls = template_urls->GetTemplateURLs();
diff --git a/chrome/browser/ssl/https_only_mode_browsertest.cc b/chrome/browser/ssl/https_only_mode_browsertest.cc
index 41c89df..01d92c2a 100644
--- a/chrome/browser/ssl/https_only_mode_browsertest.cc
+++ b/chrome/browser/ssl/https_only_mode_browsertest.cc
@@ -118,7 +118,7 @@
   void ProceedThroughInterstitial(content::WebContents* tab) {
     content::TestNavigationObserver nav_observer(tab, 1);
     std::string javascript = "window.certificateErrorPageController.proceed();";
-    ASSERT_TRUE(content::ExecuteScript(tab, javascript));
+    ASSERT_TRUE(content::ExecJs(tab, javascript));
     nav_observer.Wait();
   }
 
@@ -126,7 +126,7 @@
     content::TestNavigationObserver nav_observer(tab, 1);
     std::string javascript =
         "window.certificateErrorPageController.dontProceed();";
-    ASSERT_TRUE(content::ExecuteScript(tab, javascript));
+    ASSERT_TRUE(content::ExecJs(tab, javascript));
     nav_observer.Wait();
   }
 
@@ -381,8 +381,8 @@
 
   // Submit the form and wait for the navigation to complete.
   content::TestNavigationObserver nav_observer(contents, 1);
-  ASSERT_TRUE(content::ExecuteScript(
-      contents, "document.getElementById('submit').click();"));
+  ASSERT_TRUE(
+      content::ExecJs(contents, "document.getElementById('submit').click();"));
   nav_observer.Wait();
 
   // Check that the navigation has ended up on the HTTP target.
@@ -656,7 +656,7 @@
       contents));
 
   // Simulate clicking the learn more link (CMD_OPEN_HELP_CENTER).
-  ASSERT_TRUE(content::ExecuteScript(
+  ASSERT_TRUE(content::ExecJs(
       contents, "window.certificateErrorPageController.openHelpCenter();"));
 
   // New tab should include the p-link "first_mode".
diff --git a/chrome/browser/ssl/https_upgrades_browsertest.cc b/chrome/browser/ssl/https_upgrades_browsertest.cc
index 0b7ff47..7cd8255 100644
--- a/chrome/browser/ssl/https_upgrades_browsertest.cc
+++ b/chrome/browser/ssl/https_upgrades_browsertest.cc
@@ -222,7 +222,7 @@
   void ProceedThroughInterstitial(content::WebContents* tab) {
     content::TestNavigationObserver nav_observer(tab, 1);
     std::string javascript = "window.certificateErrorPageController.proceed();";
-    ASSERT_TRUE(content::ExecuteScript(tab, javascript));
+    ASSERT_TRUE(content::ExecJs(tab, javascript));
     nav_observer.Wait();
   }
 
@@ -230,7 +230,7 @@
     content::TestNavigationObserver nav_observer(tab, 1);
     std::string javascript =
         "window.certificateErrorPageController.dontProceed();";
-    ASSERT_TRUE(content::ExecuteScript(tab, javascript));
+    ASSERT_TRUE(content::ExecJs(tab, javascript));
     nav_observer.Wait();
   }
 
@@ -1022,8 +1022,8 @@
 
   // Submit the form and wait for the navigation to complete.
   content::TestNavigationObserver nav_observer(contents, 1);
-  ASSERT_TRUE(content::ExecuteScript(
-      contents, "document.getElementById('submit').click();"));
+  ASSERT_TRUE(
+      content::ExecJs(contents, "document.getElementById('submit').click();"));
   nav_observer.Wait();
 
   // Check that the navigation has ended up on the HTTP target.
@@ -1289,7 +1289,7 @@
       contents));
 
   // Simulate clicking the learn more link (CMD_OPEN_HELP_CENTER).
-  ASSERT_TRUE(content::ExecuteScript(
+  ASSERT_TRUE(content::ExecJs(
       contents, "window.certificateErrorPageController.openHelpCenter();"));
 
   // New tab should include the p-link "first_mode".
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index 3e1a4e26..9a95e2a 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -1398,9 +1398,9 @@
       browser(), https_server_.GetURL(replacement_path)));
   content::TestNavigationObserver navigation_observer(
       browser()->tab_strip_model()->GetActiveWebContents());
-  ASSERT_TRUE(content::ExecuteScript(
-      browser()->tab_strip_model()->GetActiveWebContents(),
-      "document.getElementById('submit').click();"));
+  ASSERT_TRUE(
+      content::ExecJs(browser()->tab_strip_model()->GetActiveWebContents(),
+                      "document.getElementById('submit').click();"));
   navigation_observer.Wait();
   // Check that the histogram count logs the security level of the page
   // containing the form, not of the form target page.
@@ -1521,7 +1521,7 @@
     content::TitleWatcher title_watcher(contents, expected_title);
     // Execute the JavaScript code to trigger the followup navigation from the
     // current page.
-    EXPECT_TRUE(content::ExecuteScript(
+    EXPECT_TRUE(content::ExecJs(
         contents,
         base::StringPrintf("location.href = '%s';", sxg_url.spec().c_str())));
     // The inner content of test.example.org_test.sxg has
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 72ffc67b..6ea2ad3 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -572,7 +572,7 @@
         NOTREACHED();
       }
     }
-    ASSERT_TRUE(content::ExecuteScript(tab, javascript));
+    ASSERT_TRUE(content::ExecJs(tab, javascript));
     return;
   }
 
@@ -1223,10 +1223,10 @@
       browser(), https_server_.GetURL("/ssl/google.html")));
   ssl_test_util::CheckAuthenticatedState(tab, AuthState::NONE);
   ssl_test_util::SecurityStateWebContentsObserver observer(tab);
-  ASSERT_TRUE(content::ExecuteScript(tab,
-                                     "var i = document.createElement('img');"
-                                     "i.src = 'http://example.test';"
-                                     "document.body.appendChild(i);"));
+  ASSERT_TRUE(content::ExecJs(tab,
+                              "var i = document.createElement('img');"
+                              "i.src = 'http://example.test';"
+                              "document.body.appendChild(i);"));
   observer.WaitForDidChangeVisibleSecurityState();
   ssl_test_util::CheckSecurityState(tab, CertError::NONE,
                                     security_state::WARNING,
@@ -1254,10 +1254,10 @@
       browser(), https_server_.GetURL("/ssl/google.html")));
   ssl_test_util::CheckAuthenticatedState(tab, AuthState::NONE);
   ssl_test_util::SecurityStateWebContentsObserver security_state_observer(tab);
-  ASSERT_TRUE(content::ExecuteScript(tab,
-                                     "var i = document.createElement('img');"
-                                     "i.src = 'http://example.test';"
-                                     "document.body.appendChild(i);"));
+  ASSERT_TRUE(content::ExecJs(tab,
+                              "var i = document.createElement('img');"
+                              "i.src = 'http://example.test';"
+                              "document.body.appendChild(i);"));
   security_state_observer.WaitForDidChangeVisibleSecurityState();
   ssl_test_util::CheckSecurityState(tab, CertError::NONE,
                                     security_state::WARNING,
@@ -2999,7 +2999,7 @@
   set_expected_notification("\"mixed-image-loaded\"");
   observe(tab);
   set_run_loop(&run_loop);
-  ASSERT_TRUE(content::ExecuteScript(
+  ASSERT_TRUE(content::ExecJs(
       tab,
       "var loaded = function () {"
       "  window.domAutomationController.send('mixed-image-loaded');"
@@ -3347,8 +3347,8 @@
         expected_show_dangerous ? AuthState::RAN_INSECURE_CONTENT
                                 : AuthState::NONE);
     // Clears title.
-    ASSERT_TRUE(content::ExecuteScript(tab->GetPrimaryMainFrame(),
-                                       "document.title = \"\";"));
+    ASSERT_TRUE(
+        content::ExecJs(tab->GetPrimaryMainFrame(), "document.title = \"\";"));
 
     {
       // SetAllowRunningInsecureContent will reload the page.
@@ -3962,7 +3962,7 @@
       content::Source<content::NavigationController>(&tab->GetController()));
   const std::string javascript =
       "window.certificateErrorPageController.proceed();";
-  EXPECT_TRUE(content::ExecuteScript(tab, javascript));
+  EXPECT_TRUE(content::ExecJs(tab, javascript));
   observer.Wait();
   ssl_test_util::CheckAuthenticationBrokenState(
       tab, net::CERT_STATUS_DATE_INVALID, AuthState::NONE);
@@ -3987,7 +3987,7 @@
       content::Source<content::NavigationController>(&tab->GetController()));
   const std::string javascript =
       "window.certificateErrorPageController.dontProceed();";
-  EXPECT_TRUE(content::ExecuteScript(tab, javascript));
+  EXPECT_TRUE(content::ExecJs(tab, javascript));
   observer.Wait();
   EXPECT_EQ("about:blank", tab->GetVisibleURL().spec());
 }
@@ -4045,7 +4045,7 @@
       "i.src = '%s';"
       "document.body.appendChild(i);",
       https_server_expired_.GetURL("/ssl/google.html").spec().c_str());
-  EXPECT_TRUE(content::ExecuteScript(tab, insert_frame));
+  EXPECT_TRUE(content::ExecJs(tab, insert_frame));
   observer.Wait();
 
   content::RenderFrameHost* child =
@@ -5218,7 +5218,7 @@
   WebContents* tab = browser->tab_strip_model()->GetActiveWebContents();
 
   content::TestNavigationObserver observer(tab);
-  EXPECT_TRUE(ExecuteScript(tab, "history.pushState({}, '', '');"));
+  EXPECT_TRUE(ExecJs(tab, "history.pushState({}, '', '');"));
   observer.Wait();
 
   ui_test_utils::NavigateToURLWithDisposition(
@@ -5262,8 +5262,8 @@
   ssl_test_util::CheckAuthenticatedState(tab, AuthState::NONE);
 
   content::TestNavigationObserver observer(tab);
-  ASSERT_TRUE(content::ExecuteScript(
-      tab, "location.replace(window.location.href + '#1')"));
+  ASSERT_TRUE(
+      content::ExecJs(tab, "location.replace(window.location.href + '#1')"));
   observer.Wait();
   EXPECT_TRUE(content::WaitForLoadStop(tab));
   ssl_test_util::CheckAuthenticatedState(tab, AuthState::NONE);
@@ -5396,7 +5396,7 @@
     content::TestNavigationObserver observer(tab);
     std::string script = "history.replaceState({}, '', '/server-redirect?" +
                          redirect_dest_url.spec() + "')";
-    EXPECT_TRUE(ExecuteScript(tab, script));
+    EXPECT_TRUE(ExecJs(tab, script));
     observer.Wait();
   }
 
@@ -5524,7 +5524,7 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(), https_server_.GetURL("/ssl/google.html")));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_TRUE(content::ExecuteScript(tab, "location.hash = Math.random()"));
+  ASSERT_TRUE(content::ExecJs(tab, "location.hash = Math.random()"));
   ssl_test_util::CheckAuthenticatedState(tab, AuthState::NONE);
 }
 
@@ -5539,7 +5539,7 @@
   ssl_test_util::CheckAuthenticatedState(tab, AuthState::NONE);
 
   content::TestNavigationObserver observer(tab);
-  EXPECT_TRUE(ExecuteScript(tab, "history.pushState({}, '', '');"));
+  EXPECT_TRUE(ExecJs(tab, "history.pushState({}, '', '');"));
   observer.Wait();
   ssl_test_util::CheckAuthenticatedState(tab, AuthState::NONE);
 
@@ -5796,7 +5796,7 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(
       browser(), embedded_test_server()->GetURL("/ssl/google.html")));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
-  ASSERT_TRUE(content::ExecuteScript(tab, "window.open()"));
+  ASSERT_TRUE(content::ExecJs(tab, "window.open()"));
 }
 
 class SSLUICaptivePortalListEnabledTest : public SSLUITest {
@@ -6038,7 +6038,7 @@
       "contains a form that targets an insecure endpoint "
       "'http://does-not-exist.test/ssl/google_files/logo.gif'. This endpoint "
       "should be made available over a secure connection.");
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
 
   // We shouldn't be displaying an interstitial.
@@ -6066,7 +6066,7 @@
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
   nav_observer.StartWatchingNewWebContents();
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   tab = browser()->tab_strip_model()->GetActiveWebContents();
   security_interstitials::SecurityInterstitialTabHelper* helper =
@@ -6094,7 +6094,7 @@
       browser(), https_server_.GetURL(replacement_path)));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6134,7 +6134,7 @@
       browser(), https_server_.GetURL(replacement_path)));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6175,7 +6175,7 @@
       browser(), https_server_.GetURL(replacement_path)));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6222,7 +6222,7 @@
       browser(), https_server_.GetURL(replacement_path)));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6251,7 +6251,7 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), form_site_url));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6283,7 +6283,7 @@
       browser(), https_server_.GetURL(replacement_path)));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6319,7 +6319,7 @@
       browser(), https_server_.GetURL(replacement_path)));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6357,7 +6357,7 @@
       browser(), https_server_.GetURL(replacement_path)));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6402,7 +6402,7 @@
 
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
 
   security_interstitials::SecurityInterstitialTabHelper* helper =
@@ -6445,7 +6445,7 @@
       browser(), https_server_.GetURL(replacement_path)));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6515,7 +6515,7 @@
       browser(), https_server_.GetURL(replacement_path)));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
   security_interstitials::SecurityInterstitialTabHelper* helper =
       security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
@@ -6566,7 +6566,7 @@
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), form_site_url));
   WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(tab, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(tab, "submitForm();"));
   nav_observer.Wait();
 
   // No interstitial should be shown, and we should be in the form action URL.
@@ -7959,8 +7959,8 @@
   // Proceed through the interstitial and observe that the histogram is
   // recorded correctly.
   content::TestNavigationObserver nav_observer(tab, 1);
-  ASSERT_TRUE(content::ExecuteScript(
-      tab, "window.certificateErrorPageController.proceed();"));
+  ASSERT_TRUE(
+      content::ExecJs(tab, "window.certificateErrorPageController.proceed();"));
   nav_observer.Wait();
 }
 
@@ -8346,7 +8346,7 @@
       "contains a form that targets an insecure endpoint "
       "'http://does-not-exist.test/ssl/google_files/logo.gif'. This endpoint "
       "should be made available over a secure connection.");
-  ASSERT_TRUE(content::ExecuteScript(fenced_frame, "submitForm();"));
+  ASSERT_TRUE(content::ExecJs(fenced_frame, "submitForm();"));
   observer.Wait();
 
   security_interstitials::SecurityInterstitialTabHelper* helper =
diff --git a/chrome/browser/ssl/ssl_fenced_frame_browsertest.cc b/chrome/browser/ssl/ssl_fenced_frame_browsertest.cc
index 791fc04..226e02ef 100644
--- a/chrome/browser/ssl/ssl_fenced_frame_browsertest.cc
+++ b/chrome/browser/ssl/ssl_fenced_frame_browsertest.cc
@@ -106,7 +106,7 @@
   EXPECT_TRUE(IsShowingSSLInterstitial(web_contents()));
   const std::string javascript =
       "window.certificateErrorPageController.proceed();";
-  ASSERT_TRUE(ExecuteScript(web_contents(), javascript));
+  ASSERT_TRUE(ExecJs(web_contents(), javascript));
 
   Browser* app_browser = InstallAndOpenTestWebApp(
       embedded_test_server()->GetURL("/fenced_frames/basic.html"));
diff --git a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
index c441bae..dd14fc6 100644
--- a/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
+++ b/chrome/browser/translate/translate_manager_render_view_host_unittest.cc
@@ -508,8 +508,8 @@
 // display names in English locale. To save space, Chrome's copy of ICU
 // does not have the display name for a language unless it's in the
 // Accept-Language list.
-static const char* kServerLanguageList[] = {
-    "ach", "ak", "af", "en-CA", "zh", "yi", "fr-FR", "tl", "iw", "in", "xx"};
+static const char* kServerLanguageList[] = {"ak",    "af", "en-CA", "zh", "yi",
+                                            "fr-FR", "tl", "iw",    "in", "xx"};
 
 // Test the fetching of languages from the translate server
 TEST_F(TranslateManagerRenderViewHostTest, FetchLanguagesFromTranslateServer) {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index afa4529d..e1b3322d3 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -58,6 +58,7 @@
     "autofill/payments/autofill_progress_dialog_controller.h",
     "autofill/payments/autofill_progress_dialog_controller_impl.cc",
     "autofill/payments/autofill_progress_dialog_controller_impl.h",
+    "autofill/payments/autofill_progress_dialog_view.cc",
     "autofill/payments/autofill_progress_dialog_view.h",
     "autofill/payments/card_unmask_authentication_selection_dialog.h",
     "autofill/payments/card_unmask_authentication_selection_dialog_controller.h",
@@ -714,6 +715,7 @@
       "//chrome/browser/enterprise/connectors/device_trust:features",
       "//chrome/browser/enterprise/connectors/device_trust/attestation/common:types",
       "//chrome/browser/ui/webui/connectors_internals:mojo_bindings",
+      "//components/device_signals/core/browser",
     ]
   }
   if (is_chromeos) {
@@ -3289,6 +3291,7 @@
       "//ash/webui/personalization_app/search:mojo_bindings",
       "//ash/webui/print_management",
       "//ash/webui/projector_app",
+      "//ash/webui/projector_app/public/mojom:projector_mojo_bindings",
       "//ash/webui/scanning",
       "//ash/webui/shimless_rma",
       "//ash/webui/shortcut_customization_ui",
@@ -4295,7 +4298,6 @@
       "views/device_signals_consent/device_signals_consent_dialog_coordinator.cc",
       "views/device_signals_consent/device_signals_consent_dialog_coordinator.h",
     ]
-    deps += [ "//components/device_signals/core/browser" ]
   }
 
   if (enable_webui_certificate_viewer) {
diff --git a/chrome/browser/ui/android/autofill/autofill_progress_dialog_view_android.cc b/chrome/browser/ui/android/autofill/autofill_progress_dialog_view_android.cc
index 68f9932..1a58ce0 100644
--- a/chrome/browser/ui/android/autofill/autofill_progress_dialog_view_android.cc
+++ b/chrome/browser/ui/android/autofill/autofill_progress_dialog_view_android.cc
@@ -65,6 +65,10 @@
   }
 }
 
+void AutofillProgressDialogViewAndroid::InvalidateControllerForCallbacks() {
+  controller_ = nullptr;
+}
+
 void AutofillProgressDialogViewAndroid::OnDismissed(JNIEnv* env) {
   if (controller_) {
     controller_->OnDismissed(/*is_canceled_by_user=*/true);
diff --git a/chrome/browser/ui/android/autofill/autofill_progress_dialog_view_android.h b/chrome/browser/ui/android/autofill/autofill_progress_dialog_view_android.h
index 2a416d7..afb88f8e 100644
--- a/chrome/browser/ui/android/autofill/autofill_progress_dialog_view_android.h
+++ b/chrome/browser/ui/android/autofill/autofill_progress_dialog_view_android.h
@@ -27,6 +27,7 @@
   // AutofillProgressDialogView.
   void Dismiss(bool show_confirmation_before_closing,
                bool is_canceled_by_user) override;
+  void InvalidateControllerForCallbacks() override;
 
   // Called by the Java code when the progress dialog is dismissed.
   void OnDismissed(JNIEnv* env);
diff --git a/chrome/browser/ui/android/fast_checkout/ui_view_android_utils_unittest.cc b/chrome/browser/ui/android/fast_checkout/ui_view_android_utils_unittest.cc
index badcb848..ca0b181 100644
--- a/chrome/browser/ui/android/fast_checkout/ui_view_android_utils_unittest.cc
+++ b/chrome/browser/ui/android/fast_checkout/ui_view_android_utils_unittest.cc
@@ -8,12 +8,10 @@
 #include "components/autofill/core/browser/autofill_data_util.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 TEST(FastCheckoutUIViewAndroidUtils, CreateFastCheckoutAutofillProfile) {
-  autofill::CountryNames::SetLocaleString("en-US");
   JNIEnv* env = base::android::AttachCurrentThread();
   autofill::AutofillProfile profile = autofill::test::GetFullProfile();
 
@@ -55,7 +53,6 @@
 }
 
 TEST(FastCheckoutUIViewAndroidUtils, CreateFastCheckoutCreditCard) {
-  autofill::CountryNames::SetLocaleString("en-US");
   JNIEnv* env = base::android::AttachCurrentThread();
   const autofill::CreditCard credit_cards[] = {
       autofill::test::GetCreditCard(), autofill::test::GetFullServerCard(),
diff --git a/chrome/browser/ui/ash/global_media_controls/media_notification_provider_impl.cc b/chrome/browser/ui/ash/global_media_controls/media_notification_provider_impl.cc
index 572e51913..098a91a 100644
--- a/chrome/browser/ui/ash/global_media_controls/media_notification_provider_impl.cc
+++ b/chrome/browser/ui/ash/global_media_controls/media_notification_provider_impl.cc
@@ -134,11 +134,6 @@
   return notification_list_view;
 }
 
-std::unique_ptr<views::View>
-MediaNotificationProviderImpl::GetActiveMediaNotificationView() {
-  return std::make_unique<views::View>();
-}
-
 void MediaNotificationProviderImpl::OnBubbleClosing() {
   item_manager_->SetDialogDelegate(nullptr);
 }
diff --git a/chrome/browser/ui/ash/global_media_controls/media_notification_provider_impl.h b/chrome/browser/ui/ash/global_media_controls/media_notification_provider_impl.h
index 085a9448..6447fbb 100644
--- a/chrome/browser/ui/ash/global_media_controls/media_notification_provider_impl.h
+++ b/chrome/browser/ui/ash/global_media_controls/media_notification_provider_impl.h
@@ -53,7 +53,6 @@
       int separator_thickness,
       bool should_clip_height,
       const std::string& item_id) override;
-  std::unique_ptr<views::View> GetActiveMediaNotificationView() override;
   void OnBubbleClosing() override;
   void SetColorTheme(
       const media_message_center::NotificationTheme& color_theme) override;
diff --git a/chrome/browser/ui/ash/projector/pending_screencast_manager.cc b/chrome/browser/ui/ash/projector/pending_screencast_manager.cc
index 975648f..5fc76209 100644
--- a/chrome/browser/ui/ash/projector/pending_screencast_manager.cc
+++ b/chrome/browser/ui/ash/projector/pending_screencast_manager.cc
@@ -606,14 +606,14 @@
 
   xhr_sender_->Send(
       GURL(base::StrCat({ash::kDriveV3BaseUrl, file_id})),
-      ash::kRequestMethodPatch, request_body,
+      ash::projector::mojom::RequestType::kPatch, request_body,
       /*use_credentials=*/false,
       /*use_api_key=*/false,
-      base::BindOnce([](bool success, const std::string& response_body,
-                        const std::string& error) {
-        if (!success) {
+      base::BindOnce([](const std::string& response_body,
+                        ash::projector::mojom::XhrResponseCode response_code) {
+        if (response_code != ash::projector::mojom::XhrResponseCode::kSuccess) {
           LOG(ERROR) << "Failed to send Drive patch request for file."
-                     << " Error: " << error;
+                     << " Error: " << response_code;
         }
       }));
 }
diff --git a/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc b/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
index bb5110b8..8cf864b 100644
--- a/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
+++ b/chrome/browser/ui/ash/projector/pending_screencast_manager_browsertest.cc
@@ -785,18 +785,19 @@
   network::TestURLLoaderFactory test_url_loader_factory;
   pending_screencast_manager()->SetProjectorXhrSenderForTest(
       std::make_unique<MockXhrSender>(
-          base::BindLambdaForTesting([&](const GURL& url,
-                                         const std::string& method,
-                                         const std::string& request_body) {
-            EXPECT_EQ(
-                "{\"contentHints\":{\"indexableText\":\" metadata file. "
-                "another sentence.\"}}",
-                request_body);
-            EXPECT_EQ("PATCH", method);
-            EXPECT_EQ(GURL("https://www.googleapis.com/drive/v3/files/fileId"),
-                      url);
-            run_loop.Quit();
-          }),
+          base::BindLambdaForTesting(
+              [&](const GURL& url, projector::mojom::RequestType method,
+                  const absl::optional<std::string>& request_body) {
+                EXPECT_EQ(
+                    "{\"contentHints\":{\"indexableText\":\" metadata file. "
+                    "another sentence.\"}}",
+                    *request_body);
+                EXPECT_EQ(projector::mojom::RequestType::kPatch, method);
+                EXPECT_EQ(
+                    GURL("https://www.googleapis.com/drive/v3/files/fileId"),
+                    url);
+                run_loop.Quit();
+              }),
           &test_url_loader_factory));
 
   // Mocks a metadata file finishes upload:
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 3251275d..90256abc 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -992,10 +992,12 @@
 }
 
 void ChromeAutofillClient::CloseAutofillProgressDialog(
-    bool show_confirmation_before_closing) {
+    bool show_confirmation_before_closing,
+    base::OnceClosure no_interactive_authentication_callback) {
   DCHECK(autofill_progress_dialog_controller_);
   autofill_progress_dialog_controller_->DismissDialog(
-      show_confirmation_before_closing);
+      show_confirmation_before_closing,
+      std::move(no_interactive_authentication_callback));
 }
 
 bool ChromeAutofillClient::IsAutocompleteEnabled() const {
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index 1fba2a3..2116b48 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -234,7 +234,8 @@
       AutofillProgressDialogType autofill_progress_dialog_type,
       base::OnceClosure cancel_callback) override;
   void CloseAutofillProgressDialog(
-      bool show_confirmation_before_closing) override;
+      bool show_confirmation_before_closing,
+      base::OnceClosure no_interactive_authentication_callback) override;
   bool IsAutocompleteEnabled() const override;
   bool IsPasswordManagerEnabled() override;
   void PropagateAutofillPredictions(
diff --git a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.cc b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.cc
index 21ef43a1..2522a412 100644
--- a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.cc
+++ b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.cc
@@ -16,10 +16,13 @@
     : web_contents_(web_contents) {}
 
 AutofillProgressDialogControllerImpl::~AutofillProgressDialogControllerImpl() {
+  // This if-statement is entered in the case where the tab is closed. When the
+  // tab is closed, the controller is destroyed before the view is destroyed, so
+  // we need to invalidate the pointer to `this` in
+  // `autofill_progress_dialog_view_` and trigger `OnDismissed()` manually.
   if (autofill_progress_dialog_view_) {
-    autofill_progress_dialog_view_->Dismiss(
-        /*show_confirmation_before_closing=*/false,
-        /*is_canceled_by_user=*/true);
+    autofill_progress_dialog_view_->InvalidateControllerForCallbacks();
+    OnDismissed(/*is_canceled_by_user=*/true);
     autofill_progress_dialog_view_ = nullptr;
   }
 }
@@ -39,10 +42,14 @@
 }
 
 void AutofillProgressDialogControllerImpl::DismissDialog(
-    bool show_confirmation_before_closing) {
+    bool show_confirmation_before_closing,
+    base::OnceClosure no_interactive_authentication_callback) {
   if (!autofill_progress_dialog_view_)
     return;
 
+  no_interactive_authentication_callback_ =
+      std::move(no_interactive_authentication_callback);
+
   autofill_progress_dialog_view_->Dismiss(show_confirmation_before_closing,
                                           /*is_canceled_by_user=*/false);
   autofill_progress_dialog_view_ = nullptr;
@@ -52,8 +59,14 @@
     bool is_canceled_by_user) {
   // Dialog is being dismissed so set the pointer to nullptr.
   autofill_progress_dialog_view_ = nullptr;
-  if (is_canceled_by_user)
+  if (is_canceled_by_user) {
     std::move(cancel_callback_).Run();
+  } else {
+    if (no_interactive_authentication_callback_) {
+      std::move(no_interactive_authentication_callback_).Run();
+    }
+  }
+
   AutofillMetrics::LogProgressDialogResultMetric(
       is_canceled_by_user, autofill_progress_dialog_type_);
   autofill_progress_dialog_type_ = AutofillProgressDialogType::kUnspecified;
diff --git a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.h b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.h
index 6562ae90..10853273 100644
--- a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.h
+++ b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/functional/callback_forward.h"
 #include "base/memory/raw_ptr.h"
 #include "chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller.h"
 #include "chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.h"
@@ -33,13 +34,27 @@
 
   ~AutofillProgressDialogControllerImpl() override;
 
-  // Show a progress dialog for underlying autofill processes. The
+  // Show a progress dialog for underlying authorization processes. The
   // `autofill_progress_dialog_type` determines the type of the progress dialog
   // and `cancel_callback` is the function to invoke when the cancel button is
   // clicked.
   void ShowDialog(AutofillProgressDialogType autofill_progress_dialog_type,
                   base::OnceClosure cancel_callback);
-  void DismissDialog(bool show_confirmation_before_closing);
+
+  // Dismisses the progress dialog after the underlying authorization processes
+  // have completed. If `show_confirmation_before_closing` is true, the UI
+  // dismissal gets delayed and we show a confirmation screen to inform them
+  // user that the authentication succeeded. The confirmation is automatically
+  // dismissed after a short period of time and the progress dialog closes.
+  //
+  // It maybe be possible to authorize the filling without user interaction
+  // (purely based on risk signals, the user did not had to type a password,
+  // CVC, use biometrics, ...). If the authorization succeeded without user
+  // interaction, DismissDialog calls `no_interactive_authentication_callback`
+  // after closing the dialog.
+  void DismissDialog(bool show_confirmation_before_closing,
+                     base::OnceClosure no_interactive_authentication_callback =
+                         base::OnceClosure());
 
   // AutofillProgressDialogController.
   void OnDismissed(bool is_canceled_by_user) override;
@@ -66,6 +81,8 @@
   // The type of the progress dialog that is being displayed.
   AutofillProgressDialogType autofill_progress_dialog_type_ =
       AutofillProgressDialogType::kUnspecified;
+
+  base::OnceClosure no_interactive_authentication_callback_;
 };
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.cc b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.cc
new file mode 100644
index 0000000..c564d1c1
--- /dev/null
+++ b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.cc
@@ -0,0 +1,13 @@
+// Copyright 2023 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/autofill/payments/autofill_progress_dialog_view.h"
+
+namespace autofill {
+
+AutofillProgressDialogView::AutofillProgressDialogView() = default;
+
+AutofillProgressDialogView::~AutofillProgressDialogView() = default;
+
+}  // namespace autofill
diff --git a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.h b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.h
index 3a0fa3f..29fe7ff 100644
--- a/chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.h
+++ b/chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_BROWSER_UI_AUTOFILL_PAYMENTS_AUTOFILL_PROGRESS_DIALOG_VIEW_H_
 #define CHROME_BROWSER_UI_AUTOFILL_PAYMENTS_AUTOFILL_PROGRESS_DIALOG_VIEW_H_
 
+#include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
 namespace autofill {
 
 class AutofillProgressDialogController;
@@ -13,15 +17,22 @@
 // for autofill flows.
 class AutofillProgressDialogView {
  public:
-  virtual ~AutofillProgressDialogView() = default;
+  AutofillProgressDialogView();
+  virtual ~AutofillProgressDialogView();
 
-  // Called by the controller to dismiss the dialog.
+  // Called by the controller to dismiss the dialog. If
+  // `show_confirmation_before_closing` is true, we will show a confirmation
+  // screen before dismissing the progress dialog. This confirms that we were
+  // able to successfully authenticate the user using risk-based authentication,
+  // which has no interactive authentication.
   virtual void Dismiss(bool show_confirmation_before_closing,
                        bool is_canceled_by_user) = 0;
 
   // Factory function for creating and showing the view.
   static AutofillProgressDialogView* CreateAndShow(
       AutofillProgressDialogController* controller);
+
+  virtual void InvalidateControllerForCallbacks() = 0;
 };
 
 }  // namespace autofill
diff --git a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
index 44bf5af..ff59203 100644
--- a/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
+++ b/chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.cc
@@ -6,7 +6,11 @@
 
 #include <memory>
 
+#include "base/functional/bind.h"
+#include "base/location.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "base/notreached.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
@@ -124,6 +128,9 @@
       browser_for_activation->ActivateContents(
           browser_for_activation->tab_strip_model()->GetWebContentsAt(
               first_tab.value()));
+
+      base::RecordAction(
+          base::UserMetricsAction("TabGroups_SavedTabGroups_Focused"));
       return;
     }
   }
@@ -192,6 +199,9 @@
 
   listener_.ConnectToLocalTabGroup(*model_.Get(saved_group_guid),
                                    local_and_saved_tab_mapping);
+
+  base::RecordAction(
+      base::UserMetricsAction("TabGroups_SavedTabGroups_Opened"));
 }
 
 void SavedTabGroupKeyedService::SaveGroup(
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index cce31ce..66ca3419 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -1294,9 +1294,10 @@
 
     case CommandDuplicate: {
       std::vector<int> indices = GetIndicesForCommand(context_index);
-      for (size_t i = 0; i < indices.size(); ++i) {
-        if (delegate()->CanDuplicateContentsAt(indices[i]))
+      for (int index : indices) {
+        if (delegate()->CanDuplicateContentsAt(index)) {
           return true;
+        }
       }
       return false;
     }
@@ -1408,10 +1409,11 @@
       // Copy the WebContents off as the indices will change as tabs are
       // duplicated.
       std::vector<WebContents*> tabs;
-      for (size_t i = 0; i < indices.size(); ++i)
-        tabs.push_back(GetWebContentsAt(indices[i]));
-      for (size_t i = 0; i < tabs.size(); ++i) {
-        int index = GetIndexOfWebContents(tabs[i]);
+      for (int index : indices) {
+        tabs.push_back(GetWebContentsAt(index));
+      }
+      for (const WebContents* const tab : tabs) {
+        int index = GetIndexOfWebContents(tab);
         if (index != -1 && delegate()->CanDuplicateContentsAt(index))
           delegate()->DuplicateContentsAt(index);
       }
diff --git a/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views.cc b/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views.cc
index 20c37dc..a0b318a3 100644
--- a/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views.h"
 
+#include "base/functional/bind.h"
 #include "base/task/single_thread_task_runner.h"
 #include "chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller.h"
 #include "chrome/browser/ui/autofill/payments/payments_ui_constants.h"
@@ -25,6 +26,9 @@
     : controller_(controller) {
   SetButtons(ui::DIALOG_BUTTON_CANCEL);
   SetButtonLabel(ui::DIALOG_BUTTON_CANCEL, controller_->GetCancelButtonLabel());
+  SetCancelCallback(
+      base::BindOnce(&AutofillProgressDialogViews::OnDialogCanceled,
+                     weak_ptr_factory_.GetWeakPtr()));
   SetModalType(ui::MODAL_TYPE_CHILD);
   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
       views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));
@@ -49,10 +53,10 @@
 }
 
 AutofillProgressDialogViews::~AutofillProgressDialogViews() {
-  // This if-statement is only entered when the user closes the dialog. In other
-  // cases, |controller_| will already be set to nullptr.
+  // This if-statement is always entered, unless the tab is closed. In this
+  // scenario `controller_` will already be invalidated.
   if (controller_) {
-    controller_->OnDismissed(/*is_canceled_by_user=*/true);
+    controller_->OnDismissed(is_canceled_by_user_);
     controller_ = nullptr;
   }
 }
@@ -69,8 +73,10 @@
 
 void AutofillProgressDialogViews::Dismiss(bool show_confirmation_before_closing,
                                           bool is_canceled_by_user) {
-  // If |show_confirmation_before_closing| is true, show the confirmation and
-  // close the widget with a delay. |show_confirmation_before_closing| being
+  is_canceled_by_user_ = is_canceled_by_user;
+
+  // If `show_confirmation_before_closing` is true, show the confirmation and
+  // close the widget with a delay. `show_confirmation_before_closing` being
   // true implies that the user did not cancel the dialog, as it is only set to
   // true once this step in the current flow is completed without any user
   // interaction.
@@ -81,13 +87,13 @@
     base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
         FROM_HERE,
         base::BindOnce(&AutofillProgressDialogViews::CloseWidget,
-                       weak_ptr_factory_.GetWeakPtr(), is_canceled_by_user),
+                       weak_ptr_factory_.GetWeakPtr()),
         kDelayBeforeDismissingProgressDialog);
     return;
   }
 
   // Otherwise close the widget directly.
-  CloseWidget(is_canceled_by_user);
+  CloseWidget();
 }
 
 void AutofillProgressDialogViews::AddedToWidget() {
@@ -99,16 +105,20 @@
           GetWindowTitle(), TitleWithIconAndSeparatorView::Icon::GOOGLE_PAY));
 }
 
+void AutofillProgressDialogViews::InvalidateControllerForCallbacks() {
+  controller_ = nullptr;
+}
+
 std::u16string AutofillProgressDialogViews::GetWindowTitle() const {
   return controller_->GetTitle();
 }
 
-void AutofillProgressDialogViews::CloseWidget(bool is_canceled_by_user) {
-  if (controller_) {
-    controller_->OnDismissed(is_canceled_by_user);
-    controller_ = nullptr;
-  }
+void AutofillProgressDialogViews::CloseWidget() {
   GetWidget()->CloseWithReason(views::Widget::ClosedReason::kUnspecified);
 }
 
+void AutofillProgressDialogViews::OnDialogCanceled() {
+  is_canceled_by_user_ = true;
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views.h b/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views.h
index b09b7f5..76ff8ef 100644
--- a/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views.h
+++ b/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views.h
@@ -30,16 +30,22 @@
   // AutofillProgressDialogView:
   void Dismiss(bool show_confirmation_before_closing,
                bool is_canceled_by_user) override;
+  void InvalidateControllerForCallbacks() override;
 
   // DialogDelegate:
   void AddedToWidget() override;
   std::u16string GetWindowTitle() const override;
 
  private:
-  // Close the widget of this view, and notify controller. |is_canceled_by_user|
-  // is a boolean that is true if the user cancels the progress dialog, false if
-  // the progress dialog closes automatically after a confirmation message.
-  void CloseWidget(bool is_canceled_by_user);
+  // Close the widget of this view, and notify controller.
+  void CloseWidget();
+
+  // Callback that is triggered when the dialog is canceled.
+  void OnDialogCanceled();
+
+  // Boolean that denotes whether the user took an action that cancelled the
+  // dialog. This will be set when `Dismiss()` is called.
+  bool is_canceled_by_user_ = false;
 
   raw_ptr<AutofillProgressDialogController> controller_ = nullptr;
   raw_ptr<views::Label> label_ = nullptr;
diff --git a/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views_browsertest.cc b/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views_browsertest.cc
index eb2f255e..dd4383b 100644
--- a/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/payments/autofill_progress_dialog_views_browsertest.cc
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/mock_callback.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
 #include "chrome/browser/ui/autofill/payments/autofill_progress_dialog_controller_impl.h"
 #include "chrome/browser/ui/autofill/payments/autofill_progress_dialog_view.h"
@@ -117,10 +119,16 @@
   EXPECT_TRUE(dialog_views);
   views::test::WidgetDestroyedWaiter destroyed_waiter(
       dialog_views->GetWidget());
-  dialog_views->Dismiss(/*show_confirmation_before_closing=*/true,
-                        /*is_canceled_by_user=*/false);
+  base::MockOnceClosure no_interactive_authentication_callback;
+  EXPECT_CALL(no_interactive_authentication_callback, Run).Times(1);
+  controller()->DismissDialog(
+      /*show_confirmation_before_closing=*/true,
+      /*no_interactive_authentication_callback=*/
+      no_interactive_authentication_callback.Get());
   destroyed_waiter.Wait();
   EXPECT_FALSE(GetDialogViews());
+  testing::Mock::VerifyAndClearExpectations(
+      &no_interactive_authentication_callback);
   histogram_tester.ExpectUniqueSample(
       "Autofill.ProgressDialog.CardUnmask.Shown", true, 1);
   histogram_tester.ExpectUniqueSample(
diff --git a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
index f778e25..11037b59 100644
--- a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/ui/views/overlay/overlay_window_image_button.h"
 #include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 #include "chrome/grit/generated_resources.h"
+#include "chromeos/ui/frame/frame_utils.h"
 #include "components/omnibox/browser/location_bar_model_impl.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/browser/web_contents.h"
@@ -51,6 +52,7 @@
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 #include "ash/wm/window_util.h"
+#include "chromeos/ui/base/chromeos_ui_constants.h"
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
@@ -70,7 +72,9 @@
 constexpr int kFrameBorderThickness = 4;
 #endif
 
+#if !BUILDFLAG(IS_CHROMEOS_ASH)
 constexpr int kResizeBorder = 10;
+#endif
 constexpr int kResizeAreaCornerSize = 16;
 
 // The time duration that the top bar animation will take in total.
@@ -359,6 +363,8 @@
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+  ash::window_util::SetChildrenUseExtendedHitRegionForWindow(
+      frame->GetNativeWindow()->parent());
   ash::window_util::InstallResizeHandleWindowTargeterForWindow(
       frame->GetNativeWindow());
 #endif
@@ -420,19 +426,17 @@
 
 int PictureInPictureBrowserFrameView::NonClientHitTest(
     const gfx::Point& point) {
-  // Do nothing if the click is outside the window.
-  if (!GetLocalBounds().Contains(point))
-    return HTNOWHERE;
-
   // Allow interacting with the buttons.
   if (GetLocationIconViewBounds().Contains(point) ||
       GetBackToTabControlsBounds().Contains(point) ||
-      GetCloseControlsBounds().Contains(point))
+      GetCloseControlsBounds().Contains(point)) {
     return HTCLIENT;
+  }
 
   for (size_t i = 0; i < content_setting_views_.size(); i++) {
-    if (GetContentSettingViewBounds(i).Contains(point))
+    if (GetContentSettingViewBounds(i).Contains(point)) {
       return HTCLIENT;
+    }
   }
 
   // Allow dragging and resizing the window.
@@ -879,6 +883,8 @@
 gfx::Insets PictureInPictureBrowserFrameView::ResizeBorderInsets() const {
 #if BUILDFLAG(IS_LINUX)
   return FrameBorderInsets();
+#elif BUILDFLAG(IS_CHROMEOS_ASH)
+  return gfx::Insets(chromeos::kResizeInsideBoundsSize);
 #else
   return gfx::Insets(kResizeBorder);
 #endif
diff --git a/chrome/browser/ui/views/performance_controls/high_efficiency_resource_view.cc b/chrome/browser/ui/views/performance_controls/high_efficiency_resource_view.cc
index 4c6229c8..25f4e19 100644
--- a/chrome/browser/ui/views/performance_controls/high_efficiency_resource_view.cc
+++ b/chrome/browser/ui/views/performance_controls/high_efficiency_resource_view.cc
@@ -28,7 +28,7 @@
 namespace {
 
 constexpr int kMemoryLabelSizeDelta = 12;
-constexpr int kGaugeRadius = 80;
+constexpr int kGaugeRadius = 70;
 constexpr int kStrokeWidth = 8;
 constexpr int kTickStrokeWidth = 2;
 constexpr int kBucketCount = 4;
@@ -79,7 +79,7 @@
     }
 
     DrawArc(canvas, center, memory_angle,
-            GetColorProvider()->GetColor(ui::kColorAccent));
+            GetColorProvider()->GetColor(ui::kColorButtonBackgroundProminent));
 
     const SkColor tick_color =
         GetColorProvider()->GetColor(ui::kColorDialogBackground);
diff --git a/chrome/browser/ui/webui/connectors_internals/connectors_internals.mojom b/chrome/browser/ui/webui/connectors_internals/connectors_internals.mojom
index e4f180226..2e2150e 100644
--- a/chrome/browser/ui/webui/connectors_internals/connectors_internals.mojom
+++ b/chrome/browser/ui/webui/connectors_internals/connectors_internals.mojom
@@ -69,6 +69,15 @@
   KeyManagerPermanentFailure permanent_failure;
 };
 
+struct ConsentMetadata {
+  // Whether signals can be collected based on the current user
+  // and management context.
+  bool can_collect_signals;
+
+  // Whether the user has given explicit consent.
+  bool consent_received;
+};
+
 struct DeviceTrustState {
   // Whether the connector is enabled or not.
   bool is_enabled;
@@ -78,6 +87,10 @@
 
   // Json string of the device signals.
   string signals_json;
+
+  // Metadata around whether user consent is required for the given management
+  // context, or if it was already given.
+  ConsentMetadata? consent_metadata;
 };
 
 // Browser interface for the page. Consists of calls for data and hooks for
diff --git a/chrome/browser/ui/webui/connectors_internals/connectors_internals_page_handler.cc b/chrome/browser/ui/webui/connectors_internals/connectors_internals_page_handler.cc
index 6901576..7c50bdce 100644
--- a/chrome/browser/ui/webui/connectors_internals/connectors_internals_page_handler.cc
+++ b/chrome/browser/ui/webui/connectors_internals/connectors_internals_page_handler.cc
@@ -10,9 +10,11 @@
 #include "build/build_config.h"
 #include "chrome/browser/enterprise/connectors/device_trust/device_trust_service.h"
 #include "chrome/browser/enterprise/connectors/device_trust/device_trust_service_factory.h"
+#include "chrome/browser/enterprise/signals/user_permission_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/connectors_internals/connectors_internals.mojom.h"
 #include "chrome/browser/ui/webui/connectors_internals/device_trust_utils.h"
+#include "components/device_signals/core/browser/user_permission_service.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 
@@ -43,7 +45,7 @@
             nullptr,
             connectors_internals::mojom::KeyManagerPermanentFailure::
                 UNSPECIFIED),
-        std::string());
+        std::string(), nullptr);
     std::move(callback).Run(std::move(state));
     return;
   }
@@ -63,8 +65,20 @@
   std::string signals_json;
   base::JSONWriter::WriteWithOptions(
       signals, base::JSONWriter::OPTIONS_PRETTY_PRINT, &signals_json);
+
+  const auto* user_permission_service =
+      enterprise_signals::UserPermissionServiceFactory::GetForProfile(profile_);
+  connectors_internals::mojom::ConsentMetadataPtr consent_metadata = nullptr;
+  if (user_permission_service) {
+    consent_metadata = connectors_internals::mojom::ConsentMetadata::New(
+        user_permission_service->CanCollectSignals() ==
+            device_signals::UserPermission::kGranted,
+        user_permission_service->HasUserConsented());
+  }
+
   auto state = connectors_internals::mojom::DeviceTrustState::New(
-      is_device_trust_enabled, utils::GetKeyInfo(), signals_json);
+      is_device_trust_enabled, utils::GetKeyInfo(), signals_json,
+      std::move(consent_metadata));
   std::move(callback).Run(std::move(state));
 }
 
diff --git a/chrome/browser/ui/webui/constrained_web_dialog_ui_browsertest.cc b/chrome/browser/ui/webui/constrained_web_dialog_ui_browsertest.cc
index b6ef56d5..bf0d296 100644
--- a/chrome/browser/ui/webui/constrained_web_dialog_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/constrained_web_dialog_ui_browsertest.cc
@@ -206,20 +206,20 @@
       &IsEqualSizes, gfx::Size(initial_size, initial_size), dialog_delegate)));
 
   // Resize to dimensions within expected bounds.
-  EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
-      GetChangeDimensionsScript(175)));
+  EXPECT_TRUE(ExecJs(dialog_delegate->GetWebContents(),
+                     GetChangeDimensionsScript(175)));
   ASSERT_TRUE(RunLoopUntil(base::BindRepeating(
       &IsEqualSizes, gfx::Size(new_size, new_size), dialog_delegate)));
 
   // Resize to dimensions smaller than the minimum bounds.
-  EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
-      GetChangeDimensionsScript(50)));
+  EXPECT_TRUE(
+      ExecJs(dialog_delegate->GetWebContents(), GetChangeDimensionsScript(50)));
   ASSERT_TRUE(RunLoopUntil(
       base::BindRepeating(&IsEqualSizes, min_size, dialog_delegate)));
 
   // Resize to dimensions greater than the maximum bounds.
-  EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
-      GetChangeDimensionsScript(250)));
+  EXPECT_TRUE(ExecJs(dialog_delegate->GetWebContents(),
+                     GetChangeDimensionsScript(250)));
   ASSERT_TRUE(RunLoopUntil(
       base::BindRepeating(&IsEqualSizes, max_size, dialog_delegate)));
 }
@@ -251,14 +251,14 @@
             dialog_delegate->GetConstrainedWebDialogPreferredSize());
 
   // Resize <body> to dimension smaller than dialog.
-  EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
-      GetChangeDimensionsScript(100)));
+  EXPECT_TRUE(ExecJs(dialog_delegate->GetWebContents(),
+                     GetChangeDimensionsScript(100)));
   ASSERT_TRUE(RunLoopUntil(base::BindRepeating(
       &IsEqualSizes, initial_dialog_size, dialog_delegate)));
 
   // Resize <body> to dimension larger than dialog.
-  EXPECT_TRUE(ExecuteScript(dialog_delegate->GetWebContents(),
-      GetChangeDimensionsScript(500)));
+  EXPECT_TRUE(ExecJs(dialog_delegate->GetWebContents(),
+                     GetChangeDimensionsScript(500)));
   ASSERT_TRUE(RunLoopUntil(base::BindRepeating(
       &IsEqualSizes, initial_dialog_size, dialog_delegate)));
 }
diff --git a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
index 4abe37f..69b51db8 100644
--- a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
+++ b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.cc
@@ -28,6 +28,9 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/tabs/tab_group.h"
+#include "chrome/browser/ui/tabs/tab_group_model.h"
+#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "components/history/core/browser/history_service.h"
 #include "components/history/core/browser/history_types.h"
 #include "components/history/core/common/pref_names.h"
@@ -338,7 +341,8 @@
 }
 
 void HistoryClustersHandler::OpenVisitUrlsInTabGroup(
-    std::vector<mojom::URLVisitPtr> visits) {
+    std::vector<mojom::URLVisitPtr> visits,
+    const absl::optional<std::string>& tab_group_name) {
   auto* browser = chrome::FindTabbedBrowser(profile_, false);
   if (!browser) {
     return;
@@ -372,7 +376,16 @@
   if (tab_indices.empty()) {
     return;
   }
-  model->AddToNewGroup(tab_indices);
+  auto new_group_id = model->AddToNewGroup(tab_indices);
+  if (!new_group_id.is_empty() && tab_group_name) {
+    if (auto* group_model = model->group_model()) {
+      auto* tab_group = group_model->GetTabGroup(new_group_id);
+      // Copy and modify the existing visual data with a new title.
+      tab_groups::TabGroupVisualData visual_data = *tab_group->visual_data();
+      visual_data.SetTitle(base::UTF8ToUTF16(*tab_group_name));
+      tab_group->SetVisualData(visual_data);
+    }
+  }
 }
 
 void HistoryClustersHandler::OnDebugMessage(const std::string& message) {
diff --git a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
index bb2d2b8..4d60b3f 100644
--- a/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
+++ b/chrome/browser/ui/webui/history_clusters/history_clusters_handler.h
@@ -79,7 +79,9 @@
                     RemoveVisitsCallback callback) override;
   void HideVisits(std::vector<mojom::URLVisitPtr> visits,
                   HideVisitsCallback callback) override;
-  void OpenVisitUrlsInTabGroup(std::vector<mojom::URLVisitPtr> visits) override;
+  void OpenVisitUrlsInTabGroup(std::vector<mojom::URLVisitPtr> visits,
+                               const absl::optional<std::string>&
+                                   tab_group_name = absl::nullopt) override;
   void RecordVisitAction(mojom::VisitAction visit_action,
                          uint32_t visit_index,
                          mojom::VisitType visit_type) override;
diff --git a/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc b/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
index a3c57da5..cae43b6 100644
--- a/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy/policy_ui_browsertest.cc
@@ -394,7 +394,7 @@
   // Click on 'save policies' button.
   const std::string javascript =
       "document.getElementById('export-policies').click()";
-  EXPECT_TRUE(content::ExecuteScript(web_contents(), javascript));
+  EXPECT_TRUE(content::ExecJs(web_contents(), javascript));
 
   base::ThreadPoolInstance::Get()->FlushForTesting();
   // Open the created file.
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 dea483f..d782ba0 100644
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -103,6 +103,7 @@
 #include "ui/accessibility/accessibility_switches.h"
 #include "ui/base/accelerators/accelerator.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/time_format.h"
 #include "ui/base/webui/web_ui_util.h"
 #include "ui/strings/grit/ui_strings.h"
 
@@ -645,6 +646,12 @@
        IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_SETTING},
       {"highEfficiencyModeDescription",
        IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_SETTING_DESCRIPTION},
+      {"highEfficiencyModeHeuristicsLabel",
+       IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_HEURISTICS_LABEL},
+      {"highEfficiencyModeOnTimerLabel",
+       IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_ON_TIMER_LABEL},
+      {"highEfficiencyModeRadioGroupAriaLabel",
+       IDS_SETTINGS_PERFORMANCE_HIGH_EFFICIENCY_MODE_RADIO_GROUP_ARIA_LABEL},
       {"batteryPageTitle", IDS_SETTINGS_BATTERY_PAGE_TITLE},
       {"batterySaverModeLabel",
        IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_SETTING},
@@ -666,6 +673,43 @@
   html_source->AddLocalizedStrings(kLocalizedStrings);
 
   html_source->AddString(
+      "tabDiscardTimerFiveMinutes",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_LONG, base::Minutes(5)));
+  html_source->AddString(
+      "tabDiscardTimerFifteenMinutes",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_LONG, base::Minutes(15)));
+  html_source->AddString(
+      "tabDiscardTimerThirtyMinutes",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_LONG, base::Minutes(30)));
+  html_source->AddString(
+      "tabDiscardTimerOneHour",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_LONG, base::Hours(1)));
+  html_source->AddString(
+      "tabDiscardTimerTwoHours",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_LONG, base::Hours(2)));
+  html_source->AddString(
+      "tabDiscardTimerFourHours",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_LONG, base::Hours(4)));
+  html_source->AddString(
+      "tabDiscardTimerEightHours",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_LONG, base::Hours(8)));
+  html_source->AddString(
+      "tabDiscardTimerSixteenHours",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_LONG, base::Hours(16)));
+  html_source->AddString(
+      "tabDiscardTimerTwentyFourHours",
+      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
+                             ui::TimeFormat::LENGTH_LONG, base::Hours(24)));
+
+  html_source->AddString(
       "batterySaverModeEnabledBelowThresholdLabel",
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_PERFORMANCE_BATTERY_SAVER_MODE_BELOW_THRESHOLD_LABEL,
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index 41f81fc..96ca784 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -500,6 +500,10 @@
 
   // Performance
   AddSettingsPageUIHandler(std::make_unique<PerformanceHandler>());
+  html_source->AddBoolean(
+      "isHighEfficiencyMultistateModeEnabled",
+      base::FeatureList::IsEnabled(
+          performance_manager::features::kHighEfficiencyMultistateMode));
 
   TryShowHatsSurveyWithTimeout();
 }
diff --git a/chrome/browser/ui/webui/settings/site_settings_permissions_handler.cc b/chrome/browser/ui/webui/settings/site_settings_permissions_handler.cc
index b43272e..134787e51 100644
--- a/chrome/browser/ui/webui/settings/site_settings_permissions_handler.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_permissions_handler.cc
@@ -6,11 +6,7 @@
 
 #include "base/check.h"
 #include "base/json/values_util.h"
-#include "base/metrics/histogram_functions.h"
 #include "base/threading/thread_checker.h"
-#include "base/time/clock.h"
-#include "base/time/default_clock.h"
-#include "base/time/time.h"
 #include "base/values.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/permissions/unused_site_permissions_service_factory.h"
@@ -24,23 +20,51 @@
 #include "url/gurl.h"
 
 namespace {
-// Reflects the maximum number of days between a permissions being revoked and
-// the time when the user regrants the permission through the unused site
-// permission module of Safete Check. The maximum number of days is determined
-// by `content_settings::features::
-// kSafetyCheckUnusedSitePermissionsRevocationCleanUpThreshold`.
-size_t kAllowAgainMetricsExclusiveMaxCount = 31;
-// Using a single bucket per day, following the value of
-// |kAllowAgainMetricsExclusiveMaxCount|.
-size_t kAllowAgainMetricsBuckets = 31;
 // Key of the expiration time in the |UnusedSitePermissions| object. Indicates
 // the time after which the associated origin and permissions are no longer
 // shown in the UI.
 constexpr char kExpirationKey[] = "expiration";
+
+std::tuple<url::Origin,
+           std::set<ContentSettingsType>,
+           content_settings::ContentSettingConstraints>
+GetUnusedSitePermissionsFromDict(
+    const base::Value::Dict& unused_site_permissions) {
+  const std::string* origin_str =
+      unused_site_permissions.FindString(site_settings::kOrigin);
+  CHECK(origin_str);
+  const auto url = GURL(*origin_str);
+  CHECK(url.is_valid());
+  const url::Origin origin = url::Origin::Create(url);
+
+  const base::Value::List* permissions =
+      unused_site_permissions.FindList(site_settings::kPermissions);
+  CHECK(permissions);
+  std::set<ContentSettingsType> permission_types;
+  for (const auto& permission : *permissions) {
+    CHECK(permission.is_string());
+    const std::string& type_string = permission.GetString();
+    ContentSettingsType type =
+        site_settings::ContentSettingsTypeFromGroupName(type_string);
+    CHECK(type != ContentSettingsType::DEFAULT)
+        << type_string << " is not expected to have a UI representation.";
+    permission_types.insert(type);
+  }
+
+  const base::Value* js_expiration =
+      unused_site_permissions.Find(kExpirationKey);
+  CHECK(js_expiration);
+  auto expiration = base::ValueToTime(js_expiration);
+
+  const content_settings::ContentSettingConstraints constraints{
+      .expiration = *expiration};
+
+  return std::make_tuple(origin, permission_types, constraints);
+}
 }  // namespace
 
 SiteSettingsPermissionsHandler::SiteSettingsPermissionsHandler(Profile* profile)
-    : profile_(profile), clock_(base::DefaultClock::GetInstance()) {}
+    : profile_(profile) {}
 SiteSettingsPermissionsHandler::~SiteSettingsPermissionsHandler() = default;
 
 void SiteSettingsPermissionsHandler::HandleGetRevokedUnusedSitePermissionsList(
@@ -66,21 +90,6 @@
 
   url::Origin origin = url::Origin::Create(GURL(origin_str));
 
-  HostContentSettingsMap* hcsm =
-      HostContentSettingsMapFactory::GetForProfile(profile_);
-  content_settings::SettingInfo info;
-  base::Value stored_value(hcsm->GetWebsiteSetting(
-      origin.GetURL(), origin.GetURL(),
-      ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, &info));
-  base::Time revoked_time =
-      info.metadata.expiration -
-      content_settings::features::
-          kSafetyCheckUnusedSitePermissionsRevocationCleanUpThreshold.Get();
-  base::UmaHistogramCustomCounts(
-      "Settings.SafetyCheck.UnusedSitePermissionsAllowAgainDays",
-      (clock_->Now() - revoked_time).InDays(), 0,
-      kAllowAgainMetricsExclusiveMaxCount, kAllowAgainMetricsBuckets);
-
   service->RegrantPermissionsForOrigin(origin);
   SendUnusedSitePermissionsReviewList();
 }
@@ -97,7 +106,6 @@
       UnusedSitePermissionsServiceFactory::GetForProfile(profile_);
 
   service->UndoRegrantPermissionsForOrigin(permissions, constraints, origin);
-
   SendUnusedSitePermissionsReviewList();
 }
 
@@ -106,8 +114,8 @@
         const base::Value::List& args) {
   permissions::UnusedSitePermissionsService* service =
       UnusedSitePermissionsServiceFactory::GetForProfile(profile_);
-  service->ClearRevokedPermissionsList();
 
+  service->ClearRevokedPermissionsList();
   SendUnusedSitePermissionsReviewList();
 }
 
@@ -180,43 +188,6 @@
   return result;
 }
 
-std::tuple<url::Origin,
-           std::set<ContentSettingsType>,
-           content_settings::ContentSettingConstraints>
-SiteSettingsPermissionsHandler::GetUnusedSitePermissionsFromDict(
-    const base::Value::Dict& unused_site_permissions) {
-  const std::string* origin_str =
-      unused_site_permissions.FindString(site_settings::kOrigin);
-  CHECK(origin_str);
-  const auto url = GURL(*origin_str);
-  CHECK(url.is_valid());
-  const url::Origin origin = url::Origin::Create(url);
-
-  const base::Value::List* permissions =
-      unused_site_permissions.FindList(site_settings::kPermissions);
-  CHECK(permissions);
-  std::set<ContentSettingsType> permission_types;
-  for (const auto& permission : *permissions) {
-    CHECK(permission.is_string());
-    const std::string& type_string = permission.GetString();
-    ContentSettingsType type =
-        site_settings::ContentSettingsTypeFromGroupName(type_string);
-    CHECK(type != ContentSettingsType::DEFAULT)
-        << type_string << " is not expected to have a UI representation.";
-    permission_types.insert(type);
-  }
-
-  const base::Value* js_expiration =
-      unused_site_permissions.Find(kExpirationKey);
-  CHECK(js_expiration);
-  auto expiration = base::ValueToTime(js_expiration);
-
-  const content_settings::ContentSettingConstraints constraints{
-      .expiration = *expiration};
-
-  return std::make_tuple(origin, permission_types, constraints);
-}
-
 void SiteSettingsPermissionsHandler::RegisterMessages() {
   // Usage of base::Unretained(this) is safe, because web_ui() owns `this` and
   // won't release ownership until destruction.
@@ -257,10 +228,6 @@
                     PopulateUnusedSitePermissionsData());
 }
 
-void SiteSettingsPermissionsHandler::SetClockForTesting(base::Clock* clock) {
-  clock_ = clock;
-}
-
 void SiteSettingsPermissionsHandler::OnJavascriptAllowed() {}
 
 void SiteSettingsPermissionsHandler::OnJavascriptDisallowed() {}
diff --git a/chrome/browser/ui/webui/settings/site_settings_permissions_handler.h b/chrome/browser/ui/webui/settings/site_settings_permissions_handler.h
index 414f949..f1524c55 100644
--- a/chrome/browser/ui/webui/settings/site_settings_permissions_handler.h
+++ b/chrome/browser/ui/webui/settings/site_settings_permissions_handler.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_UI_WEBUI_SETTINGS_SITE_SETTINGS_PERMISSIONS_HANDLER_H_
 
 #include "base/memory/raw_ptr.h"
-#include "base/time/clock.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h"
 #include "components/content_settings/core/common/content_settings_constraints.h"
@@ -72,19 +71,7 @@
   // Sends the list of unused site permissions to review to the WebUI.
   void SendUnusedSitePermissionsReviewList();
 
-  // Get values from |UnusedSitePermission| object in
-  // site_settings_permissions_browser_proxy.ts.
-  std::tuple<url::Origin,
-             std::set<ContentSettingsType>,
-             content_settings::ContentSettingConstraints>
-  GetUnusedSitePermissionsFromDict(
-      const base::Value::Dict& unused_site_permissions);
-
   const raw_ptr<Profile> profile_;
-
-  raw_ptr<base::Clock> clock_;
-
-  void SetClockForTesting(base::Clock* clock);
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_SETTINGS_SITE_SETTINGS_PERMISSIONS_HANDLER_H_
diff --git a/chrome/browser/ui/webui/settings/site_settings_permissions_handler_unittest.cc b/chrome/browser/ui/webui/settings/site_settings_permissions_handler_unittest.cc
index 186208b6..b134e56 100644
--- a/chrome/browser/ui/webui/settings/site_settings_permissions_handler_unittest.cc
+++ b/chrome/browser/ui/webui/settings/site_settings_permissions_handler_unittest.cc
@@ -6,10 +6,8 @@
 #include <memory>
 
 #include "base/memory/scoped_refptr.h"
-#include "base/test/metrics/histogram_tester.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/clock.h"
-#include "base/time/time.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/history/history_service_factory.h"
 #include "chrome/browser/ui/webui/settings/site_settings_helper.h"
@@ -67,17 +65,11 @@
         static_cast<int32_t>(ContentSettingsType::GEOLOCATION));
     dict.Set(permissions::kRevokedKey,
              base::Value::List(std::move(permission_type_list)));
-    const content_settings::ContentSettingConstraints constraint{
-        .expiration =
-            clock()->Now() +
-            content_settings::features::
-                kSafetyCheckUnusedSitePermissionsRevocationCleanUpThreshold
-                    .Get()};
 
     hcsm()->SetWebsiteSettingDefaultScope(
         GURL(kUnusedTestSite), GURL(kUnusedTestSite),
         ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
-        base::Value(dict.Clone()), constraint);
+        base::Value(dict.Clone()));
 
     // There should be only an unused URL in the revoked permissions list.
     const auto& revoked_permissions =
@@ -86,7 +78,6 @@
     EXPECT_EQ(GURL(kUnusedTestSite),
               GURL(*revoked_permissions[0].GetDict().FindString(
                   site_settings::kOrigin)));
-    handler()->SetClockForTesting(&clock_);
   }
 
   void TearDown() override {
@@ -129,10 +120,6 @@
 TEST_F(SiteSettingsPermissionsHandlerTest, PopulateUnusedSitePermissionsData) {
   // Add GEOLOCATION setting for url but do not add to revoked list.
   const content_settings::ContentSettingConstraints constraint{
-      .expiration =
-          clock()->Now() +
-          content_settings::features::
-              kSafetyCheckUnusedSitePermissionsRevocationCleanUpThreshold.Get(),
       .track_last_visit_for_autoexpiration = true};
   hcsm()->SetContentSettingDefaultScope(
       GURL(kUsedTestSite), GURL(kUsedTestSite),
@@ -150,9 +137,6 @@
 
 TEST_F(SiteSettingsPermissionsHandlerTest,
        HandleAllowPermissionsAgainForUnusedSite) {
-  // Advance 14 days; this will be the expected histogram sample.
-  clock()->Advance(base::Days(14));
-  base::HistogramTester histogram_tester;
   base::Value::List initial_unused_site_permissions =
       handler()->PopulateUnusedSitePermissionsData();
   ExpectRevokedPermission();
@@ -162,14 +146,6 @@
   args.Append(base::Value(kUnusedTestSite));
   handler()->HandleAllowPermissionsAgainForUnusedSite(args);
 
-  // Only a single entry should be recorded in the histogram.
-  const std::vector<base::Bucket> buckets = histogram_tester.GetAllSamples(
-      "Settings.SafetyCheck.UnusedSitePermissionsAllowAgainDays");
-  EXPECT_EQ(1U, buckets.size());
-  // The recorded metric should be the elapsed days since the revocation.
-  histogram_tester.ExpectUniqueSample(
-      "Settings.SafetyCheck.UnusedSitePermissionsAllowAgainDays", 14, 1);
-
   // Check there is no origin in revoked permissions list.
   ContentSettingsForOneType revoked_permissions_list;
   hcsm()->GetSettingsForOneType(
diff --git a/chrome/browser/ui/webui/webui_webview_browsertest.cc b/chrome/browser/ui/webui/webui_webview_browsertest.cc
index c64309b..fbb29f5 100644
--- a/chrome/browser/ui/webui/webui_webview_browsertest.cc
+++ b/chrome/browser/ui/webui/webui_webview_browsertest.cc
@@ -367,8 +367,8 @@
   // RenderWidgetHosts in order to work with OOPIFs. See crbug.com/647249.
 
   {
-    EXPECT_TRUE(content::ExecuteScript(embedder_web_contents,
-                                       "console.log('step1: Drag Enter')"));
+    EXPECT_TRUE(content::ExecJs(embedder_web_contents,
+                                "console.log('step1: Drag Enter')"));
 
     WebUIMessageListener listener(embedder_web_contents->GetWebUI(),
                                   "Step1: destNode gets dragenter");
@@ -380,8 +380,8 @@
   }
 
   {
-    EXPECT_TRUE(content::ExecuteScript(embedder_web_contents,
-                                       "console.log('step2: Drag Over')"));
+    EXPECT_TRUE(content::ExecJs(embedder_web_contents,
+                                "console.log('step2: Drag Over')"));
 
     WebUIMessageListener listener(embedder_web_contents->GetWebUI(),
                                   "Step2: destNode gets dragover");
@@ -392,8 +392,8 @@
   }
 
   {
-    EXPECT_TRUE(content::ExecuteScript(embedder_web_contents,
-                                       "console.log('step3: Drop')"));
+    EXPECT_TRUE(
+        content::ExecJs(embedder_web_contents, "console.log('step3: Drop')"));
 
     DNDToInputNavigationObserver observer(embedder_web_contents);
     WebUIMessageListener listener(embedder_web_contents->GetWebUI(),
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 4df39930..f5a1fc44 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1683734405-148978d5ad2b145e716104e0f8875becbd83382d.profdata
+chrome-mac-arm-main-1683748689-a9b990cc64ea8d9f432add8e12961effdfa5a550.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 52eb1c3d..6305b72 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1683730120-d2b57f29577502d057c3d42f853de23dc94de8b0.profdata
+chrome-win64-main-1683741590-61d1dfa3bcd447768811a4de6be67d9f71d644c9.profdata
diff --git a/chrome/common/extensions/api/developer_private.idl b/chrome/common/extensions/api/developer_private.idl
index ad587a71..d720773 100644
--- a/chrome/common/extensions/api/developer_private.idl
+++ b/chrome/common/extensions/api/developer_private.idl
@@ -838,10 +838,6 @@
         ExtensionSiteAccessUpdate[] updates,
         optional VoidCallback callback);
 
-    [nocompile, deprecated="Use management.setEnabled"]
-        static void enable(DOMString id,
-                           boolean enabled,
-                           optional VoidCallback callback);
     [nocompile, deprecated="Use updateExtensionConfiguration"]
         static void allowIncognito(DOMString extensionId,
                                    boolean allow,
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.cc b/chrome/renderer/accessibility/read_anything_app_controller.cc
index b304008..b31588a 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller.cc
@@ -559,7 +559,6 @@
       .SetMethod("onConnected", &ReadAnythingAppController::OnConnected)
       .SetMethod("onLinkClicked", &ReadAnythingAppController::OnLinkClicked)
       .SetMethod("isSelectable", &ReadAnythingAppController::isSelectable)
-      .SetMethod("clearSelection", &ReadAnythingAppController::ClearSelection)
       .SetMethod("onSelectionChange",
                  &ReadAnythingAppController::OnSelectionChange)
       .SetMethod("setContentForTesting",
@@ -736,17 +735,6 @@
   page_handler_->OnLinkClicked(model_.active_tree_id(), ax_node_id);
 }
 
-void ReadAnythingAppController::ClearSelection() const {
-  if (model_.active_tree_id() == ui::AXTreeIDUnknown() ||
-      !model_.ContainsTree(model_.active_tree_id())) {
-    return;
-  }
-  ui::AXSerializableTree* tree =
-      model_.GetTreeFromId(model_.active_tree_id()).get();
-  page_handler_->OnSelectionChange(model_.active_tree_id(), tree->root()->id(),
-                                   0, tree->root()->id(), 0);
-}
-
 void ReadAnythingAppController::OnSelectionChange(ui::AXNodeID anchor_node_id,
                                                   int anchor_offset,
                                                   ui::AXNodeID focus_node_id,
@@ -762,7 +750,6 @@
 
   // Ignore the selection if it's collapsed, which is created by a simple click.
   if ((anchor_offset == focus_offset) && (anchor_node_id == focus_node_id)) {
-    ClearSelection();
     return;
   }
 
diff --git a/chrome/renderer/accessibility/read_anything_app_controller.h b/chrome/renderer/accessibility/read_anything_app_controller.h
index 20f6d82..c5e6f2e 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller.h
+++ b/chrome/renderer/accessibility/read_anything_app_controller.h
@@ -118,7 +118,6 @@
   bool IsOverline(ui::AXNodeID ax_node_id) const;
   void OnConnected();
   void OnLinkClicked(ui::AXNodeID ax_node_id) const;
-  void ClearSelection() const;
   void OnSelectionChange(ui::AXNodeID anchor_node_id,
                          int anchor_offset,
                          ui::AXNodeID focus_node_id,
diff --git a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
index 9bdcc07..0c7f1e3 100644
--- a/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
+++ b/chrome/renderer/accessibility/read_anything_app_controller_browsertest.cc
@@ -93,7 +93,6 @@
     snapshot.nodes[2].id = 3;
     snapshot.nodes[3].id = 4;
     SetUpdateTreeID(&snapshot);
-    root_id_ = snapshot.root_id;
 
     // Send the snapshot to the controller and set its tree ID to be the active
     // tree ID. When the accessibility event is received and unserialized, the
@@ -230,8 +229,6 @@
     controller_->OnLinkClicked(ax_node_id);
   }
 
-  void ClearSelection() { controller_->ClearSelection(); }
-
   void OnSelectionChange(ui::AXNodeID anchor_node_id,
                          int anchor_offset,
                          ui::AXNodeID focus_node_id,
@@ -251,7 +248,6 @@
   ui::AXTreeID ActiveTreeId() { return controller_->model_.active_tree_id(); }
 
   ui::AXTreeID tree_id_;
-  ui::AXNodeID root_id_;
   MockAXTreeDistiller* distiller_ = nullptr;
   testing::StrictMock<MockReadAnythingPageHandler> page_handler_;
 
@@ -1179,15 +1175,8 @@
   OnSelectionChange(anchor_node_id, anchor_offset, focus_node_id, focus_offset);
 }
 
-TEST_F(ReadAnythingAppControllerTest, ClearSelection) {
-  EXPECT_CALL(page_handler_,
-              OnSelectionChange(tree_id_, root_id_, 0, root_id_, 0))
-      .Times(1);
-  ClearSelection();
-  page_handler_.FlushForTesting();
-}
-
-TEST_F(ReadAnythingAppControllerTest, OnSelectionChange_ClickClearsSelection) {
+TEST_F(ReadAnythingAppControllerTest,
+       OnSelectionChange_ClickDoesNotUpdateSelection) {
   ui::AXTreeUpdate update;
   SetUpdateTreeID(&update);
   update.nodes.resize(3);
@@ -1197,58 +1186,15 @@
   update.nodes[0].role = ax::mojom::Role::kStaticText;
   update.nodes[1].role = ax::mojom::Role::kStaticText;
   update.nodes[2].role = ax::mojom::Role::kStaticText;
-  AccessibilityEventReceived({update});
   ui::AXNodeID anchor_node_id = 2;
   int anchor_offset = 15;
   ui::AXNodeID focus_node_id = 2;
   int focus_offset = 15;
   EXPECT_CALL(page_handler_,
-              OnSelectionChange(tree_id_, root_id_, 0, root_id_, 0))
-      .Times(1);
-  OnSelectionChange(anchor_node_id, anchor_offset, focus_node_id, focus_offset);
-  page_handler_.FlushForTesting();
-}
-
-TEST_F(ReadAnythingAppControllerTest,
-       OnSelectionChange_ClickAfterSelectionClearsSelection) {
-  ui::AXTreeUpdate selection_update;
-  SetUpdateTreeID(&selection_update);
-  selection_update.nodes.resize(3);
-  selection_update.nodes[0].id = 2;
-  selection_update.nodes[1].id = 3;
-  selection_update.nodes[2].id = 4;
-  selection_update.nodes[0].role = ax::mojom::Role::kStaticText;
-  selection_update.nodes[1].role = ax::mojom::Role::kStaticText;
-  selection_update.nodes[2].role = ax::mojom::Role::kStaticText;
-  AccessibilityEventReceived({selection_update});
-  ui::AXNodeID anchor_node_id = 2;
-  int anchor_offset = 0;
-  ui::AXNodeID focus_node_id = 3;
-  int focus_offset = 1;
-  EXPECT_CALL(page_handler_,
               OnSelectionChange(tree_id_, anchor_node_id, anchor_offset,
                                 focus_node_id, focus_offset))
-      .Times(1);
+      .Times(0);
   OnSelectionChange(anchor_node_id, anchor_offset, focus_node_id, focus_offset);
-
-  ui::AXTreeUpdate click_update;
-  SetUpdateTreeID(&click_update);
-  click_update.nodes.resize(3);
-  click_update.nodes[0].id = 2;
-  click_update.nodes[1].id = 3;
-  click_update.nodes[2].id = 4;
-  click_update.nodes[0].role = ax::mojom::Role::kStaticText;
-  click_update.nodes[1].role = ax::mojom::Role::kStaticText;
-  click_update.nodes[2].role = ax::mojom::Role::kStaticText;
-  AccessibilityEventReceived({click_update});
-  anchor_node_id = 2;
-  anchor_offset = 15;
-  focus_node_id = 2;
-  focus_offset = 15;
-  EXPECT_CALL(page_handler_,
-              OnSelectionChange(tree_id_, root_id_, 0, root_id_, 0))
-      .Times(1);
-  ClearSelection();
   page_handler_.FlushForTesting();
 }
 
diff --git a/chrome/renderer/accessibility/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything_app_model.cc
index 54987aa..fa6919f 100644
--- a/chrome/renderer/accessibility/read_anything_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything_app_model.cc
@@ -320,10 +320,10 @@
       // ones.
       UnserializePendingUpdates(tree_id);
     }
-  UnserializeUpdates(std::move(updates), tree_id);
-  ProcessNonGeneratedEvents(events);
+    UnserializeUpdates(std::move(updates), tree_id);
+    ProcessNonGeneratedEvents(events);
   } else {
-  UnserializeUpdates(std::move(updates), tree_id);
+    UnserializeUpdates(std::move(updates), tree_id);
   }
 }
 
diff --git a/chrome/renderer/resources/extensions/developer_private_custom_bindings.js b/chrome/renderer/resources/extensions/developer_private_custom_bindings.js
index 79c862c6..e55ae70 100644
--- a/chrome/renderer/resources/extensions/developer_private_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/developer_private_custom_bindings.js
@@ -20,10 +20,6 @@
 
   bindFileSystemFunction('loadDirectory');
 
-  // developerPrivate.enable is the same as chrome.management.setEnabled.
-  // TODO(devlin): Migrate callers off developerPrivate.enable.
-  bindingsAPI.compiledApi.enable = chrome.management.setEnabled;
-
   apiFunctions.setHandleRequest('allowFileAccess',
                                 function(id, allow, callback) {
     chrome.developerPrivate.updateExtensionConfiguration(
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3eb929d..01c0ea5 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -8787,6 +8787,8 @@
       "../browser/browser_switcher/ieem_sitelist_parser_unittest.cc",
       "../browser/browser_switcher/mock_alternative_browser_driver.cc",
       "../browser/browser_switcher/mock_alternative_browser_driver.h",
+      "../browser/enterprise/connectors/device_trust/attestation/browser/device_attester_unittest.cc",
+      "../browser/enterprise/connectors/device_trust/attestation/browser/profile_attester_unittest.cc",
       "../browser/enterprise/connectors/device_trust/attestation/desktop/desktop_attestation_service_unittest.cc",
       "../browser/enterprise/connectors/device_trust/browser/signing_key_policy_observer.cc",
       "../browser/enterprise/profile_token_management/profile_token_navigation_throttle_unittest.cc",
diff --git a/chrome/test/data/ash/back_gesture/page_prevent_default.html b/chrome/test/data/ash/back_gesture/page_prevent_default.html
index 970bb82..4eddc30 100644
--- a/chrome/test/data/ash/back_gesture/page_prevent_default.html
+++ b/chrome/test/data/ash/back_gesture/page_prevent_default.html
@@ -11,6 +11,7 @@
 #prevent_default_id {
     width: 100%;
     height: 100%;
+    touch-action: none;
 }
 </style>
 <body>
diff --git a/chrome/test/data/webui/chromeos/personalization_app/ambient_subpage_element_test.ts b/chrome/test/data/webui/chromeos/personalization_app/ambient_subpage_element_test.ts
index 79edfc5..9114888d 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/ambient_subpage_element_test.ts
+++ b/chrome/test/data/webui/chromeos/personalization_app/ambient_subpage_element_test.ts
@@ -208,7 +208,7 @@
     personalizationStore.setReducersEnabled(true);
     personalizationStore.expectAction(
         AmbientActionName.SET_AMBIENT_MODE_ENABLED);
-    toggleRow.click();
+    toggleButton.click();
     let action = await personalizationStore.waitForAction(
                      AmbientActionName.SET_AMBIENT_MODE_ENABLED) as
         SetAmbientModeEnabledAction;
@@ -218,7 +218,7 @@
 
     personalizationStore.expectAction(
         AmbientActionName.SET_AMBIENT_MODE_ENABLED);
-    toggleRow.click();
+    toggleButton.click();
     action = await personalizationStore.waitForAction(
                  AmbientActionName.SET_AMBIENT_MODE_ENABLED) as
         SetAmbientModeEnabledAction;
@@ -241,7 +241,7 @@
 
     personalizationStore.expectAction(
         AmbientActionName.SET_AMBIENT_MODE_ENABLED);
-    toggleRow.$.toggle.click();
+    toggleButton.click();
     let action = await personalizationStore.waitForAction(
                      AmbientActionName.SET_AMBIENT_MODE_ENABLED) as
         SetAmbientModeEnabledAction;
@@ -249,7 +249,7 @@
 
     personalizationStore.expectAction(
         AmbientActionName.SET_AMBIENT_MODE_ENABLED);
-    toggleRow.$.toggle.click();
+    toggleButton.click();
     action = await personalizationStore.waitForAction(
                  AmbientActionName.SET_AMBIENT_MODE_ENABLED) as
         SetAmbientModeEnabledAction;
diff --git a/chrome/test/data/webui/cr_elements/cr_lottie_test.ts b/chrome/test/data/webui/cr_elements/cr_lottie_test.ts
index 9e1181a..8547c8cc 100644
--- a/chrome/test/data/webui/cr_elements/cr_lottie_test.ts
+++ b/chrome/test/data/webui/cr_elements/cr_lottie_test.ts
@@ -7,7 +7,7 @@
 
 import {CrLottieElement} from 'chrome://resources/cr_elements/cr_lottie/cr_lottie.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertEquals, assertNotEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {MockController, MockMethod} from 'chrome://webui-test/mock_controller.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 // clang-format on
@@ -117,6 +117,49 @@
         context.getImageData(canvas.width / 2, canvas.height / 2, 1, 1).data);
   }
 
+
+  /**
+   * @return bool true if all elements in a and b are equal.
+   */
+  function arrayEquals(a: any[], b: any[]) {
+    if (a.length !== b.length) {
+      return false;
+    }
+    for (let i = 0; i < a.length; ++i) {
+      if (a[i] !== b[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Waits until a pixel of the given color has rendered by polling. If the
+   * pixel does not appear after a given timeout, fails the test.
+   *
+   * @param color color of the pixel to wait for.
+   */
+  async function pollUntilRendered(color: number[]) {
+    // Handle a timeout here so that if the pixel does not appear in a
+    // reasonably long time frame, the specific mocha test fails. Otherwise, the
+    // C++ harness would timeout with no information of which test failed to
+    // render the pixel.
+    const timeoutNotice = 'TIMEOUT';
+    const timeout = new Promise(resolve => {
+      setTimeout(() => resolve(timeoutNotice), 10000);
+    });
+    const polling = new Promise(async resolve => {
+      while (true) {
+        if (arrayEquals(await samplePixel(), color)) {
+          resolve({});
+          return;
+        }
+      }
+    });
+    const result = await Promise.race([polling, timeout]);
+    assertNotEquals(result, timeoutNotice, 'Pixel ' + color + ' not rendered');
+  }
+
   test('TestResize', async () => {
     createLottieElement(/*autoplay=*/ true);
     await waitForInitializeEvent;
@@ -227,42 +270,13 @@
   });
 
   test('TestRenderFrame', async function() {
-    // TODO(crbug.com/1108915): Offscreen canvas has a race issue when used in
-    // this test framework.
-    if (1) {
-      this.skip();
-    }
-
-    // To ensure that we capture a frame from the animation and not an empty
-    // frame, we delay the capture by 2 seconds.
-    // Note: This issue is only observed in tests.
-    const kRaceTimeout = 2000;
-
     createLottieElement(/*autoplay=*/ true);
     await waitForInitializeEvent;
     await waitForPlayingEvent;
-
-    const waitForFrameRender = new Promise<void>(function(resolve) {
-      window.setTimeout(resolve, kRaceTimeout);
-    });
-
-    await waitForFrameRender;
-
-    assertDeepEquals(GREEN_PIXEL, await samplePixel());
+    await pollUntilRendered(GREEN_PIXEL);
   });
 
   test('TestChangeAnimationUrl', async function() {
-    // TODO(crbug.com/1108915): Offscreen canvas has a race issue when used in
-    // this test framework.
-    if (1) {
-      this.skip();
-    }
-
-    // To ensure that we capture a frame from the animation and not an empty
-    // frame, we delay the capture by 2 seconds.
-    // Note: This issue is only observed in tests.
-    const kRaceTimeout = 2000;
-
     createLottieElement(/*autoplay=*/ true);
     await waitForInitializeEvent;
     await waitForPlayingEvent;
@@ -272,25 +286,15 @@
     waitForInitializeEvent =
         eventToPromise('cr-lottie-initialized', crLottieElement);
     waitForPlayingEvent = eventToPromise('cr-lottie-playing', crLottieElement);
+    await pollUntilRendered(GREEN_PIXEL);
 
     crLottieElement.animationUrl = SAMPLE_LOTTIE_BLUE;
 
     // The previous animation should be cleared and stopped between loading.
-    // Unfortunately since the offscreen canvas is rendered asynchronously,
-    // there is no way to grab a frame in between events and have it guaranteed
-    // to be the empty frame. At least wait for the `cr-lottie-stopped` event.
     await waitForStoppedEvent;
-
     await waitForInitializeEvent;
     await waitForPlayingEvent;
-
-    const waitForFrameRender = new Promise<void>(function(resolve) {
-      setTimeout(resolve, kRaceTimeout);
-    });
-
-    await waitForFrameRender;
-
-    assertDeepEquals(BLUE_PIXEL, await samplePixel());
+    await pollUntilRendered(BLUE_PIXEL);
   });
 
   test('TestHidden', async () => {
diff --git a/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts b/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
index ce2dd57..4c564748 100644
--- a/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/history_clusters/module_test.ts
@@ -55,6 +55,7 @@
         id: BigInt(111),
         visits: createLayoutSuitableSampleVisits(layout),
         label: '',
+        tabGroupName: 'My Tab Group Name',
         labelMatchPositions: [],
         relatedSearches: createRelatedSearches(numRelatedSearches),
         imageUrl: undefined,
@@ -206,11 +207,13 @@
               openAllButton.innerText.trim());
           openAllButton.click();
 
-          const urls = await handler.whenCalled('openUrlsInTabGroup');
+          const [urls, tabGroupName] =
+              await handler.whenCalled('openUrlsInTabGroup');
           assertEquals(3, urls.length);
           assertEquals(`${GOOGLE_SEARCH_BASE_URL}?q=foo`, urls[0].url);
           assertEquals('https://www.foo.com/1', urls[1].url);
           assertEquals('https://www.foo.com/2', urls[2].url);
+          assertEquals('My Tab Group Name', tabGroupName);
         });
 
     test('Backend is notified when module is dismissed', async () => {
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 10e8827..e13929a 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -523,8 +523,37 @@
   }
 };
 
-TEST_F('CrSettingsPerformancePageTest', 'All', function() {
-  mocha.run();
+TEST_F('CrSettingsPerformancePageTest', 'Controls', function() {
+  runMochaSuite('PerformancePage');
+});
+
+TEST_F('CrSettingsPerformancePageTest', 'ExceptionList', function() {
+  runMochaSuite('TabDiscardExceptionList');
+});
+
+var CrSettingsPerformancePageMultistateTest =
+    class extends CrSettingsBrowserTest {
+  /** @override */
+  get browsePreload() {
+    return 'chrome://settings/test_loader.html?module=settings/performance_page_test.js';
+  }
+
+  /** @override */
+  get featureListInternal() {
+    return {
+      enabled: [
+        'performance_manager::features::kHighEfficiencyMultistateMode',
+      ],
+    };
+  }
+};
+
+TEST_F('CrSettingsPerformancePageMultistateTest', 'Controls', function() {
+  runMochaSuite('PerformancePageMultistate');
+});
+
+TEST_F('CrSettingsPerformancePageMultistateTest', 'ExceptionList', function() {
+  runMochaSuite('TabDiscardExceptionList');
 });
 
 var CrSettingsBatteryPageTest = class extends CrSettingsBrowserTest {
diff --git a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
index 6368d19..3917e68 100644
--- a/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
+++ b/chrome/test/data/webui/settings/passwords_and_autofill_fake_data.ts
@@ -514,6 +514,7 @@
   requestedIbans: number = 0;
   removedIbans: number = 0;
   isValidIban: number = 0;
+  authenticateUserAndFlipMandatoryAuthToggle: number = 0;
 }
 
 /**
@@ -544,6 +545,7 @@
       'removeIban',
       'addVirtualCard',
       'isValidIban',
+      'authenticateUserAndFlipMandatoryAuthToggle',
     ]);
 
     // Set these to have non-empty data.
@@ -625,6 +627,10 @@
     return Promise.resolve(this.isUserVerifyingPlatformAuthenticatorAvailable_);
   }
 
+  authenticateUserAndFlipMandatoryAuthToggle() {
+    this.methodCalled('authenticateUserAndFlipMandatoryAuthToggle');
+  }
+
   /**
    * Verifies expectations.
    */
@@ -653,5 +659,9 @@
     assertEquals(
         expected.removedIbans, this.getCallCount('removeIban'),
         'removedIbans mismatch');
+    assertEquals(
+        expected.authenticateUserAndFlipMandatoryAuthToggle,
+        this.getCallCount('authenticateUserAndFlipMandatoryAuthToggle'),
+        'authenticateUserAndFlipMandatoryAuthToggle mismatch');
   }
 }
diff --git a/chrome/test/data/webui/settings/payments_section_test.ts b/chrome/test/data/webui/settings/payments_section_test.ts
index 31d271e..df036a1d 100644
--- a/chrome/test/data/webui/settings/payments_section_test.ts
+++ b/chrome/test/data/webui/settings/payments_section_test.ts
@@ -10,7 +10,7 @@
 import {isMac, isWindows} from 'chrome://resources/js/platform.js';
 
 import {createCreditCardEntry, TestPaymentsManager} from './passwords_and_autofill_fake_data.js';
-import {createPaymentsSection, getLocalAndServerCreditCardListItems} from './payments_section_utils.js';
+import {createPaymentsSection, getLocalAndServerCreditCardListItems, getDefaultExpectations} from './payments_section_utils.js';
 import {TestMetricsBrowserProxy} from './test_metrics_browser_proxy.js';
 
 // clang-format on
@@ -425,4 +425,57 @@
 
         assertFalse(!!mandatoryAuthToggle);
       });
+
+  test(
+      'verifyMandatoryAuthToggleDoesTriggerUserAuthWhenClicked',
+      async function() {
+        loadTimeData.overrideValues({deviceAuthAvailable: true});
+
+        const section = await createPaymentsSection(
+            /*creditCards=*/[], /*ibans=*/[], /*upiIds=*/[], {
+              credit_card_enabled: {value: true},
+              payment_methods_mandatory_reauth: {value: false},
+            });
+        const mandatoryAuthToggle =
+            section.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+                '#mandatoryAuthToggle');
+
+        if (isMac || isWindows) {
+          const paymentsManagerProxy =
+              PaymentsManagerImpl.getInstance() as TestPaymentsManager;
+          const expectations = getDefaultExpectations();
+
+          assertTrue(!!mandatoryAuthToggle);
+          mandatoryAuthToggle.click();
+          expectations.authenticateUserAndFlipMandatoryAuthToggle = 1;
+          paymentsManagerProxy.assertExpectations(expectations);
+        } else {
+          assertFalse(!!mandatoryAuthToggle);
+        }
+      });
+
+  test(
+      'verifyMandatoryAuthToggleDoesNotTriggersUserAuthWhenNotClicked',
+      async function() {
+        loadTimeData.overrideValues({deviceAuthAvailable: true});
+
+        const section = await createPaymentsSection(
+            /*creditCards=*/[], /*ibans=*/[], /*upiIds=*/[], {
+              credit_card_enabled: {value: true},
+              payment_methods_mandatory_reauth: {value: false},
+            });
+        const mandatoryAuthToggle =
+            section.shadowRoot!.querySelector<SettingsToggleButtonElement>(
+                '#mandatoryAuthToggle');
+        const paymentsManagerProxy =
+            PaymentsManagerImpl.getInstance() as TestPaymentsManager;
+        const expectations = getDefaultExpectations();
+
+        if (isMac || isWindows) {
+          assertTrue(!!mandatoryAuthToggle);
+        } else {
+          assertFalse(!!mandatoryAuthToggle);
+        }
+        paymentsManagerProxy.assertExpectations(expectations);
+      });
 });
diff --git a/chrome/test/data/webui/settings/payments_section_utils.ts b/chrome/test/data/webui/settings/payments_section_utils.ts
index 34da2c46..4d459e83 100644
--- a/chrome/test/data/webui/settings/payments_section_utils.ts
+++ b/chrome/test/data/webui/settings/payments_section_utils.ts
@@ -39,6 +39,8 @@
 /**
  * Returns the default expectations from TestPaymentsManager. Adjust the
  * values as needed.
+ * `requestedCreditCards`, `listeningCreditCards` and `requestedIbans` are
+ * defaulted to 1 as they are always called during page initialization.
  */
 export function getDefaultExpectations(): PaymentsManagerExpectations {
   const expected = new PaymentsManagerExpectations();
@@ -50,6 +52,7 @@
   expected.requestedIbans = 1;
   expected.removedIbans = 0;
   expected.isValidIban = 0;
+  expected.authenticateUserAndFlipMandatoryAuthToggle = 0;
   return expected;
 }
 
diff --git a/chrome/test/data/webui/settings/performance_page_test.ts b/chrome/test/data/webui/settings/performance_page_test.ts
index 961e4573d..813087a 100644
--- a/chrome/test/data/webui/settings/performance_page_test.ts
+++ b/chrome/test/data/webui/settings/performance_page_test.ts
@@ -5,33 +5,55 @@
 import 'chrome://settings/settings.js';
 
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {CrIconButtonElement} from 'chrome://settings/lazy_load.js';
-import {HIGH_EFFICIENCY_MODE_PREF, HighEfficiencyModeExceptionListAction, HighEfficiencyModeState, PerformanceBrowserProxyImpl, PerformanceMetricsProxyImpl, SettingsPerformancePageElement, TAB_DISCARD_EXCEPTIONS_MANAGED_PREF, TAB_DISCARD_EXCEPTIONS_OVERFLOW_SIZE, TAB_DISCARD_EXCEPTIONS_PREF, TabDiscardExceptionAddDialogElement, TabDiscardExceptionEditDialogElement, TabDiscardExceptionEntryElement, TabDiscardExceptionListElement} from 'chrome://settings/settings.js';
+import {CrIconButtonElement, IronCollapseElement, SettingsRadioGroupElement} from 'chrome://settings/lazy_load.js';
+import {HIGH_EFFICIENCY_MODE_PREF, HighEfficiencyModeExceptionListAction, HighEfficiencyModeState, PerformanceBrowserProxyImpl, PerformanceMetricsProxyImpl, SettingsDropdownMenuElement, SettingsPerformancePageElement, TAB_DISCARD_EXCEPTIONS_MANAGED_PREF, TAB_DISCARD_EXCEPTIONS_OVERFLOW_SIZE, TAB_DISCARD_EXCEPTIONS_PREF, TabDiscardExceptionAddDialogElement, TabDiscardExceptionEditDialogElement, TabDiscardExceptionEntryElement, TabDiscardExceptionListElement} from 'chrome://settings/settings.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {TestPerformanceBrowserProxy} from './test_performance_browser_proxy.js';
 import {TestPerformanceMetricsProxy} from './test_performance_metrics_proxy.js';
 
-suite('PerformancePage', function() {
-  const CrPolicyStrings = {
-    controlledSettingPolicy: 'policy',
-  };
-  let performancePage: SettingsPerformancePageElement;
-  let performanceBrowserProxy: TestPerformanceBrowserProxy;
-  let performanceMetricsProxy: TestPerformanceMetricsProxy;
-  let tabDiscardExceptionsList: TabDiscardExceptionListElement;
+const highEfficiencyModeDummyPrefs = {
+  high_efficiency_mode: {
+    state: {
+      type: chrome.settingsPrivate.PrefType.NUMBER,
+      value: HighEfficiencyModeState.DISABLED,
+    },
+    time_before_discard_in_minutes: {
+      type: chrome.settingsPrivate.PrefType.NUMBER,
+      value: 1,
+    },
+  },
+};
 
-  suiteSetup(function() {
-    // Without this, cr-policy-pref-indicators will not have any text, making it
-    // so that they cannot be shown.
-    Object.assign(window, {CrPolicyStrings});
-  });
+/**
+ * Constructs dummy prefs for tab discarding. Needs to be a function so that
+ * list pref values are recreated and not shared between test suites.
+ */
+function tabDiscardingDummyPrefs(): Record<
+    string, Record<string, Omit<chrome.settingsPrivate.PrefObject, 'key'>>> {
+  return {
+    tab_discarding: {
+      exceptions: {
+        type: chrome.settingsPrivate.PrefType.LIST,
+        value: [],
+      },
+      exceptions_managed: {
+        enforcement: chrome.settingsPrivate.Enforcement.ENFORCED,
+        controlledBy: chrome.settingsPrivate.ControlledBy.USER_POLICY,
+        type: chrome.settingsPrivate.PrefType.LIST,
+        value: [],
+      },
+    },
+  };
+}
+
+suite('PerformancePage', function() {
+  let performancePage: SettingsPerformancePageElement;
+  let performanceMetricsProxy: TestPerformanceMetricsProxy;
 
   setup(function() {
-    performanceBrowserProxy = new TestPerformanceBrowserProxy();
-    PerformanceBrowserProxyImpl.setInstance(performanceBrowserProxy);
-
     performanceMetricsProxy = new TestPerformanceMetricsProxy();
     PerformanceMetricsProxyImpl.setInstance(performanceMetricsProxy);
 
@@ -39,30 +61,12 @@
     performancePage = document.createElement('settings-performance-page');
     performancePage.set('prefs', {
       performance_tuning: {
-        high_efficiency_mode: {
-          state: {
-            type: chrome.settingsPrivate.PrefType.NUMBER,
-            value: HighEfficiencyModeState.DISABLED,
-          },
-        },
-        tab_discarding: {
-          exceptions: {
-            type: chrome.settingsPrivate.PrefType.LIST,
-            value: [],
-          },
-          exceptions_managed: {
-            enforcement: chrome.settingsPrivate.Enforcement.ENFORCED,
-            controlledBy: chrome.settingsPrivate.ControlledBy.USER_POLICY,
-            type: chrome.settingsPrivate.PrefType.LIST,
-            value: [],
-          },
-        },
+        ...highEfficiencyModeDummyPrefs,
+        ...tabDiscardingDummyPrefs(),
       },
     });
     document.body.appendChild(performancePage);
     flush();
-
-    tabDiscardExceptionsList = performancePage.$.tabDiscardExceptionsList;
   });
 
   test('testHighEfficiencyModeEnabled', function() {
@@ -77,7 +81,7 @@
     assertFalse(performancePage.$.toggleButton.checked);
   });
 
-  test('testHighEfficiencyModeMetrics', async function() {
+  test('testHighEfficiencyModeChangeState', async function() {
     performancePage.setPrefValue(
         HIGH_EFFICIENCY_MODE_PREF, HighEfficiencyModeState.DISABLED);
 
@@ -85,12 +89,178 @@
     let state = await performanceMetricsProxy.whenCalled(
         'recordHighEfficiencyModeChanged');
     assertEquals(state, HighEfficiencyModeState.ENABLED_ON_TIMER);
+    assertEquals(
+        performancePage.getPref(HIGH_EFFICIENCY_MODE_PREF).value,
+        HighEfficiencyModeState.ENABLED_ON_TIMER);
 
     performanceMetricsProxy.reset();
     performancePage.$.toggleButton.click();
     state = await performanceMetricsProxy.whenCalled(
         'recordHighEfficiencyModeChanged');
     assertEquals(state, HighEfficiencyModeState.DISABLED);
+    assertEquals(
+        performancePage.getPref(HIGH_EFFICIENCY_MODE_PREF).value,
+        HighEfficiencyModeState.DISABLED);
+  });
+});
+
+suite('PerformancePageMultistate', function() {
+  let performancePage: SettingsPerformancePageElement;
+  let performanceMetricsProxy: TestPerformanceMetricsProxy;
+  let enabledOnTimerButton: HTMLElement;
+  let radioGroup: SettingsRadioGroupElement;
+  let radioGroupCollapse: IronCollapseElement;
+  let discardTimeDropdown: SettingsDropdownMenuElement;
+
+  const DISCARD_TIME_PREF =
+      'performance_tuning.high_efficiency_mode.time_before_discard_in_minutes';
+
+  /**
+   * Used to get elements form the performance page that may or may not exist,
+   * such as those inside a dom-if.
+   * TODO(charlesmeng): remove once kHighEfficiencyMultistateMode flag is
+   * cleaned up, since elements can then be selected with $ interface
+   */
+  function getPerformancePageElement<T extends HTMLElement = HTMLElement>(
+      id: string): T {
+    const el = performancePage.shadowRoot!.querySelector<T>(`#${id}`);
+    assertTrue(!!el);
+    assertTrue(el instanceof HTMLElement);
+    return el;
+  }
+
+  setup(function() {
+    performanceMetricsProxy = new TestPerformanceMetricsProxy();
+    PerformanceMetricsProxyImpl.setInstance(performanceMetricsProxy);
+
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    performancePage = document.createElement('settings-performance-page');
+    performancePage.set('prefs', {
+      performance_tuning: {
+        ...highEfficiencyModeDummyPrefs,
+        ...tabDiscardingDummyPrefs(),
+      },
+    });
+    document.body.appendChild(performancePage);
+    flush();
+
+    enabledOnTimerButton = getPerformancePageElement('enabledOnTimerButton');
+    radioGroup = getPerformancePageElement('radioGroup');
+    radioGroupCollapse = getPerformancePageElement('radioGroupCollapse');
+    discardTimeDropdown = getPerformancePageElement('discardTimeDropdown');
+  });
+
+  test('testHighEfficiencyModeDisabled', function() {
+    performancePage.setPrefValue(
+        HIGH_EFFICIENCY_MODE_PREF, HighEfficiencyModeState.DISABLED);
+    assertFalse(performancePage.$.toggleButton.checked);
+    assertFalse(radioGroupCollapse.opened);
+    assertTrue(discardTimeDropdown.disabled);
+  });
+
+  test('testHighEfficiencyModeEnabled', function() {
+    performancePage.setPrefValue(
+        HIGH_EFFICIENCY_MODE_PREF, HighEfficiencyModeState.ENABLED);
+    assertTrue(performancePage.$.toggleButton.checked);
+    assertTrue(radioGroupCollapse.opened);
+    assertEquals(String(HighEfficiencyModeState.ENABLED), radioGroup.selected);
+    assertTrue(discardTimeDropdown.disabled);
+  });
+
+  test('testHighEfficiencyModeEnabledOnTimer', function() {
+    performancePage.setPrefValue(
+        HIGH_EFFICIENCY_MODE_PREF, HighEfficiencyModeState.ENABLED_ON_TIMER);
+    assertTrue(performancePage.$.toggleButton.checked);
+    assertTrue(radioGroupCollapse.opened);
+    assertEquals(
+        String(HighEfficiencyModeState.ENABLED_ON_TIMER), radioGroup.selected);
+    assertFalse(discardTimeDropdown.disabled);
+  });
+
+  test('testHighEfficiencyModeDiscardTime', async function() {
+    performancePage.setPrefValue(
+        HIGH_EFFICIENCY_MODE_PREF, HighEfficiencyModeState.ENABLED_ON_TIMER);
+    performancePage.setPrefValue(DISCARD_TIME_PREF, 120);
+    // Need to wait for dropdown menu to update its selection using a microtask
+    await flushTasks();
+    assertTrue(!!discardTimeDropdown.$.dropdownMenu.options[4]);
+    assertTrue(discardTimeDropdown.$.dropdownMenu.options[4].selected);
+    assertEquals(
+        performancePage.getPref(DISCARD_TIME_PREF).value,
+        Number(discardTimeDropdown.$.dropdownMenu.value));
+
+    assertTrue(!!discardTimeDropdown.$.dropdownMenu.options[3]);
+    const newDiscardTime = discardTimeDropdown.$.dropdownMenu.options[3].value;
+    discardTimeDropdown.$.dropdownMenu.options[3].selected = true;
+    discardTimeDropdown.$.dropdownMenu.dispatchEvent(new CustomEvent('change'));
+    assertEquals(
+        Number(newDiscardTime),
+        performancePage.getPref(DISCARD_TIME_PREF).value);
+  });
+
+  test('testHighEfficiencyModeChangeState', async function() {
+    performancePage.setPrefValue(
+        HIGH_EFFICIENCY_MODE_PREF, HighEfficiencyModeState.DISABLED);
+
+    performancePage.$.toggleButton.click();
+    let state = await performanceMetricsProxy.whenCalled(
+        'recordHighEfficiencyModeChanged');
+    assertEquals(state, HighEfficiencyModeState.ENABLED);
+    assertEquals(
+        performancePage.getPref(HIGH_EFFICIENCY_MODE_PREF).value,
+        HighEfficiencyModeState.ENABLED);
+
+    performanceMetricsProxy.reset();
+    enabledOnTimerButton.click();
+    state = await performanceMetricsProxy.whenCalled(
+        'recordHighEfficiencyModeChanged');
+    assertEquals(state, HighEfficiencyModeState.ENABLED_ON_TIMER);
+    assertEquals(
+        performancePage.getPref(HIGH_EFFICIENCY_MODE_PREF).value,
+        HighEfficiencyModeState.ENABLED_ON_TIMER);
+
+    performanceMetricsProxy.reset();
+    performancePage.$.toggleButton.click();
+    state = await performanceMetricsProxy.whenCalled(
+        'recordHighEfficiencyModeChanged');
+    assertEquals(state, HighEfficiencyModeState.DISABLED);
+    assertEquals(
+        performancePage.getPref(HIGH_EFFICIENCY_MODE_PREF).value,
+        HighEfficiencyModeState.DISABLED);
+  });
+});
+
+suite('TabDiscardExceptionList', function() {
+  const CrPolicyStrings = {
+    controlledSettingPolicy: 'policy',
+  };
+  let performancePage: SettingsPerformancePageElement;
+  let performanceBrowserProxy: TestPerformanceBrowserProxy;
+  let performanceMetricsProxy: TestPerformanceMetricsProxy;
+  let tabDiscardExceptionsList: TabDiscardExceptionListElement;
+
+  suiteSetup(function() {
+    // Without this, cr-policy-pref-indicator will not have any text, making it
+    // so that it cannot be shown.
+    Object.assign(window, {CrPolicyStrings});
+  });
+
+  setup(function() {
+    performanceBrowserProxy = new TestPerformanceBrowserProxy();
+    PerformanceBrowserProxyImpl.setInstance(performanceBrowserProxy);
+
+    performanceMetricsProxy = new TestPerformanceMetricsProxy();
+    PerformanceMetricsProxyImpl.setInstance(performanceMetricsProxy);
+
+    document.body.innerHTML = window.trustedTypes!.emptyHTML;
+    performancePage = document.createElement('settings-performance-page');
+    performancePage.set('prefs', {
+      performance_tuning: tabDiscardingDummyPrefs(),
+    });
+    document.body.appendChild(performancePage);
+    flush();
+
+    tabDiscardExceptionsList = performancePage.$.tabDiscardExceptionsList;
   });
 
   function assertExceptionListEquals(rules: string[], message?: string) {
diff --git a/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.cc b/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.cc
index 24102f98..3b70d89 100644
--- a/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.cc
+++ b/chrome/test/media_router/access_code_cast/access_code_cast_integration_browsertest.cc
@@ -278,17 +278,17 @@
 void AccessCodeCastIntegrationBrowserTest::CloseDialog(
     content::WebContents* dialog_contents) {
   ASSERT_TRUE(dialog_contents);
-  EXPECT_TRUE(ExecuteScript(dialog_contents, std::string(GetElementScript()) +
-                                                 ".cancelButtonPressed();"));
+  EXPECT_TRUE(ExecJs(dialog_contents, std::string(GetElementScript()) +
+                                          ".cancelButtonPressed();"));
 }
 
 void AccessCodeCastIntegrationBrowserTest::SetAccessCode(
     std::string access_code,
     content::WebContents* dialog_contents) {
   ASSERT_TRUE(dialog_contents);
-  EXPECT_TRUE(ExecuteScript(dialog_contents,
-                            GetElementScript() + ".switchToCodeInput();"));
-  EXPECT_TRUE(ExecuteScript(
+  EXPECT_TRUE(
+      ExecJs(dialog_contents, GetElementScript() + ".switchToCodeInput();"));
+  EXPECT_TRUE(ExecJs(
       dialog_contents,
       GetElementScript() + ".setAccessCodeForTest('" + access_code + "');"));
 }
@@ -296,8 +296,8 @@
 void AccessCodeCastIntegrationBrowserTest::PressSubmit(
     content::WebContents* dialog_contents) {
   ASSERT_TRUE(dialog_contents);
-  EXPECT_TRUE(ExecuteScript(dialog_contents,
-                            GetElementScript() + ".addSinkAndCast();"));
+  EXPECT_TRUE(
+      ExecJs(dialog_contents, GetElementScript() + ".addSinkAndCast();"));
 }
 
 void AccessCodeCastIntegrationBrowserTest::PressSubmitAndWaitForClose(
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index bf785484..424111d 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -111,10 +111,12 @@
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(IS_ANDROID)
+#include "base/android/build_info.h"
 #include "chromecast/media/audio/cast_audio_manager_android.h"  // nogncheck
 #include "components/cdm/browser/cdm_message_filter_android.h"
 #include "components/crash/core/app/crashpad.h"
 #include "media/audio/android/audio_manager_android.h"
+#include "media/audio/audio_features.h"
 #else
 #include "chromecast/browser/memory_pressure_controller_impl.h"
 #endif  // BUILDFLAG(IS_ANDROID)
@@ -149,16 +151,33 @@
       cast_network_contexts_(
           std::make_unique<CastNetworkContexts>(GetCorsExemptHeadersList())),
       cast_feature_list_creator_(cast_feature_list_creator) {
-  cast_feature_list_creator_->SetExtraEnableFeatures({
-    &::media::kInternalMediaSession, &features::kNetworkServiceInProcess,
+  std::vector<const base::Feature*> extra_enable_features = {
+    &::media::kInternalMediaSession,
+    &features::kNetworkServiceInProcess,
 #if BUILDFLAG(IS_ANDROID) && BUILDFLAG(ENABLE_VIDEO_CAPTURE_SERVICE)
-        &features::kMojoVideoCapture,
+    &features::kMojoVideoCapture,
 #endif
 #if BUILDFLAG(USE_V4L2_CODEC)
-        // Enable accelerated video decode if v4l2 codec is supported.
-        &::media::kVaapiVideoDecodeLinux,
+    // Enable accelerated video decode if v4l2 codec is supported.
+    &::media::kVaapiVideoDecodeLinux,
 #endif  // BUILDFLAG(USE_V4L2_CODEC)
-  });
+  };
+
+  std::vector<const base::Feature*> extra_disable_features;
+
+#if BUILDFLAG(IS_ANDROID)
+  if (base::android::BuildInfo::GetInstance()->is_tv()) {
+    // Use the software decoder provided by MediaCodec instead of the built in
+    // software decoder. This can improve av sync quality.
+    extra_enable_features.push_back(&::media::kAllowMediaCodecSoftwareDecoder);
+    // Disable AAudio on ATV for a better av sync quality, before we root cause
+    // the issue.
+    extra_disable_features.push_back(&::features::kUseAAudioDriver);
+  }
+#endif
+
+  cast_feature_list_creator_->SetExtraEnableFeatures(extra_enable_features);
+  cast_feature_list_creator_->SetExtraDisableFeatures(extra_disable_features);
 }
 
 CastContentBrowserClient::~CastContentBrowserClient() {
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 8ac452f..fedf219 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-15457.0.0
\ No newline at end of file
+15458.0.0
\ No newline at end of file
diff --git a/chromeos/ash/services/recording/BUILD.gn b/chromeos/ash/services/recording/BUILD.gn
index 1fda135..7a40a269 100644
--- a/chromeos/ash/services/recording/BUILD.gn
+++ b/chromeos/ash/services/recording/BUILD.gn
@@ -8,6 +8,8 @@
 
 source_set("recording") {
   sources = [
+    "audio_capturer.cc",
+    "audio_capturer.h",
     "color_quantization.cc",
     "color_quantization.h",
     "gif_encoder.cc",
diff --git a/chromeos/ash/services/recording/audio_capturer.cc b/chromeos/ash/services/recording/audio_capturer.cc
new file mode 100644
index 0000000..feb4c13
--- /dev/null
+++ b/chromeos/ash/services/recording/audio_capturer.cc
@@ -0,0 +1,66 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/ash/services/recording/audio_capturer.h"
+
+#include "services/audio/public/cpp/device_factory.h"
+
+namespace recording {
+
+AudioCapturer::AudioCapturer(
+    const std::string& device_id,
+    mojo::PendingRemote<media::mojom::AudioStreamFactory> audio_stream_factory,
+    const media::AudioParameters& audio_params,
+    OnAudioCapturedCallback callback)
+    : audio_capturer_(
+          audio::CreateInputDevice(std::move(audio_stream_factory),
+                                   device_id,
+                                   audio::DeadStreamDetection::kEnabled)),
+      on_audio_captured_callback_(std::move(callback)) {
+  audio_capturer_->Initialize(audio_params, /*callback=*/this);
+}
+
+AudioCapturer::~AudioCapturer() = default;
+
+void AudioCapturer::Start() {
+  audio_capturer_->Start();
+}
+
+void AudioCapturer::Stop() {
+  audio_capturer_->Stop();
+}
+
+void AudioCapturer::OnCaptureStarted() {}
+
+void AudioCapturer::Capture(const media::AudioBus* audio_source,
+                            base::TimeTicks audio_capture_time,
+                            double volume,
+                            bool key_pressed) {
+  // This is called on a worker thread created by the `audio_capturer_` (See
+  // `media::AudioDeviceThread`. The given `audio_source` wraps audio data in a
+  // shared memory with the audio service. Calling `audio_capturer_->Stop()`
+  // will destroy that thread and the shared memory mapping before we get a
+  // chance to encode and flush the remaining frames (See
+  // `media::AudioInputDevice::Stop()`, and
+  // `media::AudioInputDevice::AudioThreadCallback::Process()` for details). It
+  // is safer that we own our AudioBuses that are kept alive until encoded and
+  // flushed.
+  // TODO(b/281868597): Consider using an `AudioBusPool` to avoid doing
+  // allocation here on the realtime audio thread.
+  auto audio_data =
+      media::AudioBus::Create(audio_source->channels(), audio_source->frames());
+  audio_source->CopyTo(audio_data.get());
+
+  on_audio_captured_callback_.Run(std::move(audio_data), audio_capture_time);
+}
+
+void AudioCapturer::OnCaptureError(media::AudioCapturerSource::ErrorCode code,
+                                   const std::string& message) {
+  LOG(ERROR) << "AudioCaptureError: code=" << static_cast<uint32_t>(code)
+             << ", " << message;
+}
+
+void AudioCapturer::OnCaptureMuted(bool is_muted) {}
+
+}  // namespace recording
diff --git a/chromeos/ash/services/recording/audio_capturer.h b/chromeos/ash/services/recording/audio_capturer.h
new file mode 100644
index 0000000..4e8a7ca
--- /dev/null
+++ b/chromeos/ash/services/recording/audio_capturer.h
@@ -0,0 +1,70 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_ASH_SERVICES_RECORDING_AUDIO_CAPTURER_H_
+#define CHROMEOS_ASH_SERVICES_RECORDING_AUDIO_CAPTURER_H_
+
+#include <memory>
+#include <string>
+
+#include "base/functional/callback_forward.h"
+#include "base/time/time.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_capturer_source.h"
+#include "media/base/audio_parameters.h"
+#include "media/mojo/mojom/audio_stream_factory.mojom-forward.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+
+namespace recording {
+
+// Defines the type of the callback that will be triggered repeatedly by the
+// audio input device to deliver a stream of buffers containing the captured
+// audio data. Each call will provide an `audio_bus` and the
+// `audio_capture_time` when the first frame of that bus was captured.
+// This callback will be invoked on a worker thread created by the audio input
+// device (`media::AudioDeviceThread`). The provided `audio_bus` owns its own
+// memory.
+using OnAudioCapturedCallback =
+    base::RepeatingCallback<void(std::unique_ptr<media::AudioBus> audio_bus,
+                                 base::TimeTicks audio_capture_time)>;
+
+// Defines an audio capturer that can capture an audio input device whose ID is
+// `device_id`. The provided `audio_stream_factory` will be used so that the
+// underlying `AudioInputDevice` can communicate with audio service via IPC. The
+// provided `audio_params` will be used to initialize the underlying audio
+// capturer. `callback` will be invoked according to the rules specified above.
+class AudioCapturer : public media::AudioCapturerSource::CaptureCallback {
+ public:
+  AudioCapturer(const std::string& device_id,
+                mojo::PendingRemote<media::mojom::AudioStreamFactory>
+                    audio_stream_factory,
+                const media::AudioParameters& audio_params,
+                OnAudioCapturedCallback callback);
+  AudioCapturer(const AudioCapturer&) = delete;
+  AudioCapturer& operator=(const AudioCapturer&) = delete;
+  ~AudioCapturer() override;
+
+  // Starts and stops the audio capture.
+  void Start();
+  void Stop();
+
+  // media::AudioCapturerSource::CaptureCallback:
+  void OnCaptureStarted() override;
+  void Capture(const media::AudioBus* audio_source,
+               base::TimeTicks audio_capture_time,
+               double volume,
+               bool key_pressed) override;
+  void OnCaptureError(media::AudioCapturerSource::ErrorCode code,
+                      const std::string& message) override;
+  void OnCaptureMuted(bool is_muted) override;
+
+ private:
+  scoped_refptr<media::AudioCapturerSource> audio_capturer_;
+
+  const OnAudioCapturedCallback on_audio_captured_callback_;
+};
+
+}  // namespace recording
+
+#endif  // CHROMEOS_ASH_SERVICES_RECORDING_AUDIO_CAPTURER_H_
diff --git a/chromeos/ash/services/recording/recording_service.cc b/chromeos/ash/services/recording/recording_service.cc
index 128bb56..dcb30457 100644
--- a/chromeos/ash/services/recording/recording_service.cc
+++ b/chromeos/ash/services/recording/recording_service.cc
@@ -7,14 +7,17 @@
 #include <cmath>
 #include <cstdint>
 #include <cstdlib>
+#include <memory>
 
 #include "base/check.h"
 #include "base/files/file_path.h"
 #include "base/location.h"
+#include "base/notreached.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
 #include "base/time/time.h"
+#include "chromeos/ash/services/recording/audio_capturer.h"
 #include "chromeos/ash/services/recording/gif_encoder.h"
 #include "chromeos/ash/services/recording/recording_encoder.h"
 #include "chromeos/ash/services/recording/recording_service_constants.h"
@@ -280,9 +283,10 @@
   DCHECK_CALLED_ON_VALID_THREAD(main_thread_checker_);
   refresh_timer_.Stop();
   video_capturer_remote_->Stop();
-  if (audio_capturer_)
-    audio_capturer_->Stop();
-  audio_capturer_.reset();
+  for (auto& capturer : audio_capturers_) {
+    capturer->Stop();
+  }
+  audio_capturers_.clear();
 }
 
 void RecordingService::OnRecordedWindowChangingRoot(
@@ -438,39 +442,6 @@
   DLOG(WARNING) << message;
 }
 
-void RecordingService::OnCaptureStarted() {}
-
-void RecordingService::Capture(const media::AudioBus* audio_source,
-                               base::TimeTicks audio_capture_time,
-                               double volume,
-                               bool key_pressed) {
-  // This is called on a worker thread created by the |audio_capturer_| (See
-  // |media::AudioDeviceThread|. The given |audio_source| wraps audio data in a
-  // shared memory with the audio service. Calling |audio_capturer_->Stop()|
-  // will destroy that thread and the shared memory mapping before we get a
-  // chance to encode and flush the remaining frames (See
-  // media::AudioInputDevice::Stop(), and
-  // media::AudioInputDevice::AudioThreadCallback::Process() for details). It is
-  // safer that we own our AudioBuses that are kept alive until encoded and
-  // flushed.
-  auto audio_data =
-      media::AudioBus::Create(audio_source->channels(), audio_source->frames());
-  audio_source->CopyTo(audio_data.get());
-  main_task_runner_->PostTask(
-      FROM_HERE, base::BindOnce(&RecordingService::OnAudioCaptured,
-                                weak_ptr_factory_.GetWeakPtr(),
-                                std::move(audio_data), audio_capture_time));
-}
-
-void RecordingService::OnCaptureError(
-    media::AudioCapturerSource::ErrorCode code,
-    const std::string& message) {
-  LOG(ERROR) << "AudioCaptureError: code=" << static_cast<uint32_t>(code)
-             << ", " << message;
-}
-
-void RecordingService::OnCaptureMuted(bool is_muted) {}
-
 void RecordingService::StartNewRecording(
     mojo::PendingRemote<mojom::RecordingServiceClient> client,
     mojo::PendingRemote<viz::mojom::FrameSinkVideoCapturer> video_capturer,
@@ -494,7 +465,8 @@
       base::BindOnce(&TerminateServiceImmediately));
 
   current_video_capture_params_ = std::move(capture_params);
-  const bool should_record_audio = microphone_stream_factory.is_valid();
+  const bool should_record_audio = microphone_stream_factory.is_valid() ||
+                                   system_audio_stream_factory.is_valid();
 
   encoder_capabilities_ = CreateEncoderCapabilities(output_file_path);
   encoder_muxer_ = CreateEncoder(
@@ -509,13 +481,23 @@
   if (!should_record_audio)
     return;
 
-  audio_capturer_ = audio::CreateInputDevice(
-      std::move(microphone_stream_factory),
-      std::string(media::AudioDeviceDescription::kDefaultDeviceId),
-      audio::DeadStreamDetection::kEnabled);
-  DCHECK(audio_capturer_);
-  audio_capturer_->Initialize(audio_parameters_, this);
-  audio_capturer_->Start();
+  if (microphone_stream_factory) {
+    audio_capturers_.emplace_back(std::make_unique<AudioCapturer>(
+        media::AudioDeviceDescription::kDefaultDeviceId,
+        std::move(microphone_stream_factory), audio_parameters_,
+        BindRepeatingToMainThread(&RecordingService::OnAudioCaptured)));
+  }
+
+  if (system_audio_stream_factory) {
+    audio_capturers_.emplace_back(std::make_unique<AudioCapturer>(
+        media::AudioDeviceDescription::kLoopbackInputDeviceId,
+        std::move(system_audio_stream_factory), audio_parameters_,
+        BindRepeatingToMainThread(&RecordingService::OnAudioCaptured)));
+  }
+
+  for (auto& capturer : audio_capturers_) {
+    capturer->Start();
+  }
 }
 
 void RecordingService::ReconfigureVideoEncoder() {
@@ -583,9 +565,10 @@
   // capturer. We will stop the recording and flush whatever video chunks we
   // currently have.
   did_failure_occur_ = true;
-  if (audio_capturer_)
-    audio_capturer_->Stop();
-  audio_capturer_.reset();
+  for (auto& capturer : audio_capturers_) {
+    capturer->Stop();
+  }
+  audio_capturers_.clear();
   TerminateRecording(mojom::RecordingStatus::kVizVideoCapturerDisconnected);
 }
 
@@ -596,8 +579,15 @@
   DCHECK(encoder_muxer_);
 
   // We ignore any subsequent frames after a failure.
-  if (did_failure_occur_)
+  if (did_failure_occur_) {
     return;
+  }
+
+  if (audio_capturers_.size() > 1) {
+    // Audio mixing has not been implemented yet.
+    NOTIMPLEMENTED_LOG_ONCE();
+    return;
+  }
 
   encoder_muxer_.AsyncCall(&RecordingEncoder::EncodeAudio)
       .WithArgs(std::move(audio_bus), audio_capture_time);
diff --git a/chromeos/ash/services/recording/recording_service.h b/chromeos/ash/services/recording/recording_service.h
index e0ade5d1..7d3b91a 100644
--- a/chromeos/ash/services/recording/recording_service.h
+++ b/chromeos/ash/services/recording/recording_service.h
@@ -34,6 +34,7 @@
 
 namespace recording {
 
+class AudioCapturer;
 class RecordingServiceTestApi;
 
 // Implements the mojo interface of the recording service which handles
@@ -41,8 +42,7 @@
 // encoded video chunks directly to a file at a path provided to the Record*()
 // functions.
 class RecordingService : public mojom::RecordingService,
-                         public viz::mojom::FrameSinkVideoConsumer,
-                         public media::AudioCapturerSource::CaptureCallback {
+                         public viz::mojom::FrameSinkVideoConsumer {
  public:
   explicit RecordingService(
       mojo::PendingReceiver<mojom::RecordingService> receiver);
@@ -111,16 +111,6 @@
   void OnStopped() override;
   void OnLog(const std::string& message) override;
 
-  // media::AudioCapturerSource::CaptureCallback:
-  void OnCaptureStarted() override;
-  void Capture(const media::AudioBus* audio_source,
-               base::TimeTicks audio_capture_time,
-               double volume,
-               bool key_pressed) override;
-  void OnCaptureError(media::AudioCapturerSource::ErrorCode code,
-                      const std::string& message) override;
-  void OnCaptureMuted(bool is_muted) override;
-
  private:
   friend class RecordingServiceTestApi;
 
@@ -184,9 +174,9 @@
   // which point we request a new refresh video frame.
   void OnRefreshTimerFired();
 
-  // By default, the |encoder_muxer_| will invoke any callback we provide it
+  // By default, the `encoder_muxer_` will invoke any callback we provide it
   // with to notify us of certain events (such as failure errors, or flush done)
-  // on the |encoding_task_runner_|'s sequence. But since these callbacks are
+  // on the `encoding_task_runner_`'s sequence. But since these callbacks are
   // invoked asynchronously from other threads, they may get invoked after this
   // RecordingService instance had been destroyed. Therefore, we need to bind
   // these callbacks to weak ptrs, to prevent them from invoking after this
@@ -204,6 +194,16 @@
                                              std::forward<Args>(args)...));
   }
 
+  // Similar to the above, conveniently binds repeating callbacks to weak ptrs
+  // of this class. The callbacks will only be invoked on the main thread.
+  template <typename Functor, typename... Args>
+  auto BindRepeatingToMainThread(Functor&& functor, Args&&... args) {
+    return base::BindPostTask(
+        main_task_runner_, base::BindRepeating(std::forward<Functor>(functor),
+                                               weak_ptr_factory_.GetWeakPtr(),
+                                               std::forward<Args>(args)...));
+  }
+
   THREAD_CHECKER(main_thread_checker_);
 
   // The audio parameters that will be used when recording audio.
@@ -262,9 +262,11 @@
   mojo::Remote<viz::mojom::FrameSinkVideoCapturer> video_capturer_remote_
       GUARDED_BY_CONTEXT(main_thread_checker_);
 
-  // The audio capturer instance. It is created only if the service is requested
-  // to record audio along side the video.
-  scoped_refptr<media::AudioCapturerSource> audio_capturer_
+  // A list of audio capturers, which can be empty if no audio recording is
+  // requested, or can contain a single capturer if either microphone or system
+  // audio recording was requested, or can contain two capturers if both are
+  // desired to be recorded an mixed together in one stream.
+  std::vector<std::unique_ptr<AudioCapturer>> audio_capturers_
       GUARDED_BY_CONTEXT(main_thread_checker_);
 
   // Abstracts querying the supported capabilities of the currently used encoder
diff --git a/chromeos/ash/services/recording/recording_service_test_api.cc b/chromeos/ash/services/recording/recording_service_test_api.cc
index 73cce16..3c75f01 100644
--- a/chromeos/ash/services/recording/recording_service_test_api.cc
+++ b/chromeos/ash/services/recording/recording_service_test_api.cc
@@ -89,7 +89,13 @@
 bool RecordingServiceTestApi::IsDoingAudioRecording() const {
   DCHECK_CALLED_ON_VALID_THREAD(recording_service_.main_thread_checker_);
 
-  return !!recording_service_.audio_capturer_;
+  return !recording_service_.audio_capturers_.empty();
+}
+
+int RecordingServiceTestApi::GetNumberOfAudioCapturers() const {
+  DCHECK_CALLED_ON_VALID_THREAD(recording_service_.main_thread_checker_);
+
+  return recording_service_.audio_capturers_.size();
 }
 
 }  // namespace recording
diff --git a/chromeos/ash/services/recording/recording_service_test_api.h b/chromeos/ash/services/recording/recording_service_test_api.h
index 8d09958..a6589df 100644
--- a/chromeos/ash/services/recording/recording_service_test_api.h
+++ b/chromeos/ash/services/recording/recording_service_test_api.h
@@ -62,6 +62,10 @@
   // Returns true if the recording service is currently recording audio.
   bool IsDoingAudioRecording() const;
 
+  // Returns the number of audio capturers currently owned by the recording
+  // service.
+  int GetNumberOfAudioCapturers() const;
+
  private:
   // The actual recording service instance.
   RecordingService recording_service_;
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index a06ae06..ffb3ee7 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-115-5735.6-1682934552-benchmark-115.0.5751.0-r1.orderfile.xz
+chromeos-chrome-orderfile-field-115-5735.12-1683543901-benchmark-115.0.5758.0-r1.orderfile.xz
diff --git a/chromeos/ui/frame/frame_header.cc b/chromeos/ui/frame/frame_header.cc
index d00917e..02aff48 100644
--- a/chromeos/ui/frame/frame_header.cc
+++ b/chromeos/ui/frame/frame_header.cc
@@ -115,9 +115,10 @@
 
   AddLayerToRegion(old_layer, views::LayerRegion::kBelow);
 
-  // The old layer is on top and should fade out.
-  old_layer->SetOpacity(1.f);
-  new_layer->SetOpacity(1.f);
+  // The old layer is on top and should fade out. The new layer is given the
+  // opacity as the old layer is currently targeting. This ensures that we don't
+  // change the overall opacity, since it may have been set by something else.
+  new_layer->SetOpacity(old_layer->GetTargetOpacity());
   {
     ui::ScopedLayerAnimationSettings settings(old_layer->GetAnimator());
     settings.SetPreemptionStrategy(
diff --git a/components/autofill/core/browser/autofill_client.cc b/components/autofill/core/browser/autofill_client.cc
index e31ab2ae..2d3148a 100644
--- a/components/autofill/core/browser/autofill_client.cc
+++ b/components/autofill/core/browser/autofill_client.cc
@@ -182,7 +182,8 @@
 }
 
 void AutofillClient::CloseAutofillProgressDialog(
-    bool show_confirmation_before_closing) {
+    bool show_confirmation_before_closing,
+    base::OnceClosure no_interactive_authentication_callback) {
   // This is overridden by platform subclasses. Currently only
   // ChromeAutofillClient (Chrome Desktop & Android) implements this.
 }
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index 3b7eb4dc..999c90f 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -734,7 +734,9 @@
       AutofillProgressDialogType autofill_progress_dialog_type,
       base::OnceClosure cancel_callback);
   virtual void CloseAutofillProgressDialog(
-      bool show_confirmation_before_closing);
+      bool show_confirmation_before_closing,
+      base::OnceClosure no_interactive_authentication_callback =
+          base::OnceClosure());
 
   // Whether the Autocomplete feature of Autofill should be enabled.
   virtual bool IsAutocompleteEnabled() const = 0;
diff --git a/components/autofill/core/browser/autofill_merge_unittest.cc b/components/autofill/core/browser/autofill_merge_unittest.cc
index 22fe034..bf56b4cf 100644
--- a/components/autofill/core/browser/autofill_merge_unittest.cc
+++ b/components/autofill/core/browser/autofill_merge_unittest.cc
@@ -23,7 +23,6 @@
 #include "components/autofill/core/browser/data_model/autofill_profile_comparator.h"
 #include "components/autofill/core/browser/form_data_importer.h"
 #include "components/autofill/core/browser/form_structure.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -207,7 +206,6 @@
 
 AutofillMergeTest::AutofillMergeTest()
     : testing::DataDrivenTest(GetTestDataDir(), kFeatureName, kTestName) {
-  CountryNames::SetLocaleString("en-US");
   for (size_t i = NO_SERVER_DATA; i < MAX_VALID_FIELD_TYPE; ++i) {
     ServerFieldType field_type = ToSafeServerFieldType(i, MAX_VALID_FIELD_TYPE);
     if (field_type == MAX_VALID_FIELD_TYPE)
diff --git a/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc b/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc
index 0bc9db1c..1efb42f1 100644
--- a/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc
+++ b/components/autofill/core/browser/autofill_profile_sync_util_unittest.cc
@@ -9,7 +9,6 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/autofill_structured_address_component.h"
 #include "components/autofill/core/browser/field_types.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/autofill/core/common/autofill_constants.h"
@@ -31,7 +30,6 @@
 const char kGuid[] = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
 const char kGuidInvalid[] = "EDC609ED";
 
-const char kLocaleString[] = "en-US";
 const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
 
 // Returns a profile with all fields set.  Contains identical data to the data
@@ -281,7 +279,6 @@
   AutofillProfileSyncUtilTest() {
     // Fix a time for implicitly constructed use_dates in AutofillProfile.
     test_clock_.SetNow(kJune2017);
-    CountryNames::SetLocaleString(kLocaleString);
     features_.InitAndEnableFeature(
         features::kAutofillEnableSupportForExtraSettingsVisibleFields);
   }
@@ -375,7 +372,6 @@
   // Fix a time for implicitly constructed use_dates in AutofillProfile.
   autofill::TestAutofillClock test_clock;
   test_clock.SetNow(kJune2017);
-  CountryNames::SetLocaleString(kLocaleString);
 
   AutofillProfileSpecifics specifics = ConstructCompleteSpecifics();
   AutofillProfile profile = ConstructCompleteProfile();
@@ -431,8 +427,6 @@
 // into country code.
 TEST_F(AutofillProfileSyncUtilTest,
        CreateAutofillProfileFromSpecifics_CountryNameParsed) {
-  CountryNames::SetLocaleString(kLocaleString);
-
   AutofillProfileSpecifics specifics;
   specifics.set_guid(kGuid);
 
diff --git a/components/autofill/core/browser/browser_autofill_manager.cc b/components/autofill/core/browser/browser_autofill_manager.cc
index 4d4fc942..969d5d58 100644
--- a/components/autofill/core/browser/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/browser_autofill_manager.cc
@@ -65,7 +65,6 @@
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/form_data_importer.h"
 #include "components/autofill/core/browser/form_structure.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/geo/phone_number_i18n.h"
 #include "components/autofill/core/browser/logging/log_manager.h"
 #include "components/autofill/core/browser/metrics/autofill_metrics.h"
@@ -542,8 +541,6 @@
   credit_card_access_manager_ = std::make_unique<CreditCardAccessManager>(
       driver, client, client->GetPersonalDataManager(),
       credit_card_form_event_logger_.get());
-
-  CountryNames::SetLocaleString(app_locale_);
 }
 
 BrowserAutofillManager::~BrowserAutofillManager() {
diff --git a/components/autofill/core/browser/data_model/address_unittest.cc b/components/autofill/core/browser/data_model/address_unittest.cc
index dd178b0e..b7fff65 100644
--- a/components/autofill/core/browser/data_model/address_unittest.cc
+++ b/components/autofill/core/browser/data_model/address_unittest.cc
@@ -11,7 +11,6 @@
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/data_model/address.h"
 #include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,10 +19,7 @@
 
 namespace autofill {
 
-class AddressTest : public testing::Test {
- public:
-  AddressTest() { CountryNames::SetLocaleString("en-US"); }
-};
+class AddressTest : public testing::Test {};
 
 // Test that country data can be properly returned as either a country code or a
 // localized country name.
diff --git a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
index 71b4600..ac043d17 100644
--- a/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
+++ b/components/autofill/core/browser/data_model/autofill_profile_comparator_unittest.cc
@@ -16,7 +16,6 @@
 #include "components/autofill/core/browser/data_model/contact_info.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/common/autofill_clock.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -91,9 +90,7 @@
     using Super::SAME_TOKENS;
   };
 
-  AutofillProfileComparatorTest() {
-    autofill::CountryNames::SetLocaleString(kLocale);
-  }
+  AutofillProfileComparatorTest() = default;
 
   AutofillProfileComparatorTest(const AutofillProfileComparatorTest&) = delete;
   AutofillProfileComparatorTest& operator=(
diff --git a/components/autofill/core/browser/field_filler_unittest.cc b/components/autofill/core/browser/field_filler_unittest.cc
index 5dc8715..d9bc128 100644
--- a/components/autofill/core/browser/field_filler_unittest.cc
+++ b/components/autofill/core/browser/field_filler_unittest.cc
@@ -29,7 +29,6 @@
 #include "components/autofill/core/browser/data_model/credit_card_test_api.h"
 #include "components/autofill/core/browser/field_types.h"
 #include "components/autofill/core/browser/geo/alternative_state_name_map_test_utils.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/common/autofill_features.h"
 #include "components/autofill/core/common/autofill_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -136,9 +135,7 @@
 
  protected:
   AutofillFieldFillerTest()
-      : credit_card_(test::GetCreditCard()), address_(test::GetFullProfile()) {
-    CountryNames::SetLocaleString("en-US");
-  }
+      : credit_card_(test::GetCreditCard()), address_(test::GetFullProfile()) {}
 
   CreditCard* credit_card() { return &credit_card_; }
   AutofillProfile* address() { return &address_; }
@@ -535,10 +532,7 @@
 
 class PhoneNumberTest
     : public AutofillFieldFillerTest,
-      public testing::WithParamInterface<AutofillPhoneFieldFillerTestCase> {
- public:
-  PhoneNumberTest() { CountryNames::SetLocaleString("en-US"); }
-};
+      public testing::WithParamInterface<AutofillPhoneFieldFillerTestCase> {};
 
 TEST_P(PhoneNumberTest, FillPhoneNumber) {
   auto test_case = GetParam();
@@ -596,10 +590,7 @@
 
 class ExpirationYearTest
     : public AutofillFieldFillerTest,
-      public testing::WithParamInterface<AutofillFieldFillerTestCase> {
- public:
-  ExpirationYearTest() { CountryNames::SetLocaleString("en-US"); }
-};
+      public testing::WithParamInterface<AutofillFieldFillerTestCase> {};
 
 TEST_P(ExpirationYearTest, FillExpirationYearInput) {
   auto test_case = GetParam();
@@ -662,10 +653,7 @@
 
 class ExpirationDateTest
     : public AutofillFieldFillerTest,
-      public testing::WithParamInterface<FillUtilExpirationDateTestCase> {
- public:
-  ExpirationDateTest() { CountryNames::SetLocaleString("en-US"); }
-};
+      public testing::WithParamInterface<FillUtilExpirationDateTestCase> {};
 
 TEST_P(ExpirationDateTest, FillExpirationDateInput) {
   auto test_case = GetParam();
@@ -858,8 +846,6 @@
       public testing::WithParamInterface<FillSelectTestCase> {
  public:
   AutofillSelectWithStatesTest() {
-    CountryNames::SetLocaleString("en-US");
-
     base::FilePath file_path;
     CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &file_path));
     file_path = file_path.Append(FILE_PATH_LITERAL("third_party"))
@@ -1017,12 +1003,7 @@
 
 class AutofillSelectWithExpirationMonthTest
     : public AutofillFieldFillerTest,
-      public testing::WithParamInterface<FillWithExpirationMonthTestCase> {
- public:
-  AutofillSelectWithExpirationMonthTest() {
-    CountryNames::SetLocaleString("en-US");
-  }
-};
+      public testing::WithParamInterface<FillWithExpirationMonthTestCase> {};
 
 TEST_P(AutofillSelectWithExpirationMonthTest,
        FillSelectControlWithExpirationMonth) {
@@ -1639,10 +1620,7 @@
 
 class AutofillStateTextTest
     : public AutofillFieldFillerTest,
-      public testing::WithParamInterface<FillStateTextTestCase> {
- public:
-  AutofillStateTextTest() { CountryNames::SetLocaleString("en-US"); }
-};
+      public testing::WithParamInterface<FillStateTextTestCase> {};
 
 TEST_P(AutofillStateTextTest, FillStateText) {
   auto test_case = GetParam();
diff --git a/components/autofill/core/browser/form_parsing/address_field.cc b/components/autofill/core/browser/form_parsing/address_field.cc
index deaaaff8e..1415214 100644
--- a/components/autofill/core/browser/form_parsing/address_field.cc
+++ b/components/autofill/core/browser/form_parsing/address_field.cc
@@ -82,9 +82,6 @@
 constexpr MatchParams kStateMatchType =
     kDefaultMatchParamsWith<MatchFieldType::kSelect, MatchFieldType::kSearch>;
 
-constexpr MatchParams kLandmarkMatchType =
-    kDefaultMatchParamsWith<MatchFieldType::kTextArea, MatchFieldType::kSearch>;
-
 }  // namespace
 
 std::unique_ptr<FormField> AddressField::Parse(
@@ -136,8 +133,8 @@
       continue;
     } else if (address_field->ParseAddress(scanner, page_language,
                                            pattern_source) ||
-               address_field->ParseAddressField(scanner, page_language,
-                                                pattern_source) ||
+               address_field->ParseDependentLocalityCityStateCountryZipCode(
+                   scanner, page_language, pattern_source) ||
                address_field->ParseCompany(scanner, page_language,
                                            pattern_source)) {
       has_trailing_non_labeled_fields = false;
@@ -180,7 +177,7 @@
       address_field->state_ || address_field->zip_ || address_field->zip4_ ||
       address_field->street_name_ || address_field->house_number_ ||
       address_field->country_ || address_field->apartment_number_ ||
-      address_field->dependent_locality_ || address_field->landmark_) {
+      address_field->dependent_locality_) {
     // Don't slurp non-labeled fields at the end into the address.
     if (has_trailing_non_labeled_fields)
       scanner->RewindTo(begin_trailing_non_labeled_fields);
@@ -229,8 +226,6 @@
                     kBaseAddressParserScore, field_candidates);
   AddClassification(apartment_number_, ADDRESS_HOME_APT_NUM,
                     kBaseAddressParserScore, field_candidates);
-  AddClassification(landmark_, ADDRESS_HOME_LANDMARK, kBaseAddressParserScore,
-                    field_candidates);
 }
 
 bool AddressField::ParseCompany(AutofillScanner* scanner,
@@ -526,23 +521,6 @@
                              &state_, {log_manager_, "kStateRe"});
 }
 
-bool AddressField::ParseLandmark(AutofillScanner* scanner,
-                                 const LanguageCode& page_language,
-                                 PatternSource pattern_source) {
-  const bool is_enabled_landmark_parsing = base::FeatureList::IsEnabled(
-      features::kAutofillEnableSupportForExtraSettingsVisibleFields);
-  // TODO(crbug.com/1441904) Remove feature check when launched.
-  if (landmark_ || !is_enabled_landmark_parsing) {
-    return false;
-  }
-
-  base::span<const MatchPatternRef> landmark_patterns =
-      GetMatchPatterns("LANDMARK", page_language, pattern_source);
-  return ParseFieldSpecifics(scanner, kLandmarkRe, kLandmarkMatchType,
-                             landmark_patterns, &landmark_,
-                             {log_manager_, "kLandmarkRe"});
-}
-
 AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelSeparately(
     AutofillScanner* scanner,
     const std::u16string& pattern,
@@ -580,16 +558,17 @@
   return RESULT_MATCH_NONE;
 }
 
-bool AddressField::ParseAddressField(AutofillScanner* scanner,
-                                     const LanguageCode& page_language,
-                                     PatternSource pattern_source) {
+bool AddressField::ParseDependentLocalityCityStateCountryZipCode(
+    AutofillScanner* scanner,
+    const LanguageCode& page_language,
+    PatternSource pattern_source) {
   // The |scanner| is not pointing at a field.
   if (scanner->IsEnd())
     return false;
 
   int num_of_missing_types = 0;
   for (const auto* field :
-       {dependent_locality_, city_, state_, country_, zip_, landmark_}) {
+       {dependent_locality_, city_, state_, country_, zip_}) {
     if (!field)
       ++num_of_missing_types;
   }
@@ -610,9 +589,6 @@
       return ParseCountry(scanner, page_language, pattern_source);
     if (!zip_)
       return ParseZipCode(scanner, page_language, pattern_source);
-    if (!landmark_) {
-      return ParseLandmark(scanner, page_language, pattern_source);
-    }
   }
 
   // Check for matches to both the name and the label.
@@ -633,11 +609,6 @@
       ParseNameAndLabelForCountry(scanner, page_language, pattern_source);
   if (country_result == RESULT_MATCH_NAME_LABEL)
     return true;
-  ParseNameLabelResult landmark_result =
-      ParseNameAndLabelForLandmark(scanner, page_language, pattern_source);
-  if (landmark_result == RESULT_MATCH_NAME_LABEL) {
-    return true;
-  }
   ParseNameLabelResult zip_result =
       ParseNameAndLabelForZipCode(scanner, page_language, pattern_source);
   if (zip_result == RESULT_MATCH_NAME_LABEL)
@@ -660,9 +631,6 @@
       return SetFieldAndAdvanceCursor(scanner, &state_);
     if (country_result != RESULT_MATCH_NONE)
       return SetFieldAndAdvanceCursor(scanner, &country_);
-    if (landmark_result != RESULT_MATCH_NONE) {
-      return SetFieldAndAdvanceCursor(scanner, &landmark_);
-    }
     if (zip_result != RESULT_MATCH_NONE)
       return ParseZipCode(scanner, page_language, pattern_source);
   }
@@ -693,9 +661,6 @@
       return SetFieldAndAdvanceCursor(scanner, &state_);
     if (country_result == result)
       return SetFieldAndAdvanceCursor(scanner, &country_);
-    if (landmark_result == result) {
-      return SetFieldAndAdvanceCursor(scanner, &landmark_);
-    }
     if (zip_result == result)
       return ParseZipCode(scanner, page_language, pattern_source);
   }
@@ -824,22 +789,4 @@
       {log_manager_, "kCountryLocationRe"});
 }
 
-AddressField::ParseNameLabelResult AddressField::ParseNameAndLabelForLandmark(
-    AutofillScanner* scanner,
-    const LanguageCode& page_language,
-    PatternSource pattern_source) {
-  const bool is_enabled_landmark_parsing = base::FeatureList::IsEnabled(
-      features::kAutofillEnableSupportForExtraSettingsVisibleFields);
-  // TODO(crbug.com/1441904) Remove feature check when launched.
-  if (landmark_ || !is_enabled_landmark_parsing) {
-    return RESULT_MATCH_NONE;
-  }
-
-  base::span<const MatchPatternRef> landmark_patterns =
-      GetMatchPatterns("LANDMARK", page_language, pattern_source);
-  return ParseNameAndLabelSeparately(scanner, kLandmarkRe, kLandmarkMatchType,
-                                     landmark_patterns, &landmark_,
-                                     {log_manager_, "kLandmarkRe"});
-}
-
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_parsing/address_field.h b/components/autofill/core/browser/form_parsing/address_field.h
index 6a9f2fd9..af3e2d9 100644
--- a/components/autofill/core/browser/form_parsing/address_field.h
+++ b/components/autofill/core/browser/form_parsing/address_field.h
@@ -83,16 +83,13 @@
                   const LanguageCode& page_language,
                   PatternSource pattern_source);
 
-  bool ParseLandmark(AutofillScanner* scanner,
-                     const LanguageCode& page_language,
-                     PatternSource pattern_source);
-
   // Parses the current field pointed to by |scanner|, if it exists, and tries
   // to determine if the field's type corresponds to one of the following:
-  // dependent locality, city, state, country, zip, landmark or none of those.
-  bool ParseAddressField(AutofillScanner* scanner,
-                         const LanguageCode& page_language,
-                         PatternSource pattern_source);
+  // dependent locality, city, state, country, zip, or none of those.
+  bool ParseDependentLocalityCityStateCountryZipCode(
+      AutofillScanner* scanner,
+      const LanguageCode& page_language,
+      PatternSource pattern_source);
 
   // Like ParseFieldSpecifics(), but applies |pattern| against the name and
   // label of the current field separately. If the return value is
@@ -130,11 +127,6 @@
       const LanguageCode& page_language,
       PatternSource pattern_source);
 
-  ParseNameLabelResult ParseNameAndLabelForLandmark(
-      AutofillScanner* scanner,
-      const LanguageCode& page_language,
-      PatternSource pattern_source);
-
   ParseNameLabelResult ParseNameAndLabelForState(
       AutofillScanner* scanner,
       const LanguageCode& page_language,
@@ -184,9 +176,6 @@
   // This field is not a raw_ptr<> because it was filtered by the rewriter for:
   // #addr-of
   RAW_PTR_EXCLUSION AutofillField* country_ = nullptr;
-  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
-  // #addr-of
-  RAW_PTR_EXCLUSION AutofillField* landmark_ = nullptr;
 };
 
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_parsing/address_field_unittest.cc b/components/autofill/core/browser/form_parsing/address_field_unittest.cc
index f2be5ff..68912c55 100644
--- a/components/autofill/core/browser/form_parsing/address_field_unittest.cc
+++ b/components/autofill/core/browser/form_parsing/address_field_unittest.cc
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 #include "components/autofill/core/browser/form_parsing/address_field.h"
-#include "components/autofill/core/browser/field_types.h"
 
 #include <memory>
 
@@ -134,17 +133,6 @@
   ClassifyAndVerify();
 }
 
-// Tests that the landmark is correctly classified.
-TEST_P(AddressFieldTest, ParseLandmark) {
-  // TODO(crbug.com/1441904): Remove once launched.
-  base::test::ScopedFeatureList enabled;
-  enabled.InitAndEnableFeature(
-      features::kAutofillEnableSupportForExtraSettingsVisibleFields);
-
-  AddTextFormFieldData("landmark", "Landmark", ADDRESS_HOME_LANDMARK);
-  ClassifyAndVerify();
-}
-
 TEST_P(AddressFieldTest, ParseCity) {
   AddTextFormFieldData("city", "City", ADDRESS_HOME_CITY);
   ClassifyAndVerify();
@@ -190,10 +178,8 @@
        ParseDependentLocalityCityStateCountryZipcodeTogether) {
   // TODO(crbug.com/1157405): Remove once launched.
   base::test::ScopedFeatureList enabled;
-  enabled.InitWithFeatures(
-      {features::kAutofillEnableDependentLocalityParsing,
-       features::kAutofillEnableSupportForExtraSettingsVisibleFields},
-      {});
+  enabled.InitAndEnableFeature(
+      features::kAutofillEnableDependentLocalityParsing);
 
   AddTextFormFieldData("neighborhood", "Neighborhood",
                        ADDRESS_HOME_DEPENDENT_LOCALITY);
@@ -201,7 +187,6 @@
   AddTextFormFieldData("state", "State", ADDRESS_HOME_STATE);
   AddTextFormFieldData("country", "Country", ADDRESS_HOME_COUNTRY);
   AddTextFormFieldData("zip", "Zip", ADDRESS_HOME_ZIP);
-  AddTextFormFieldData("landmark", "Landmark", ADDRESS_HOME_LANDMARK);
   ClassifyAndVerify();
 }
 
diff --git a/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json b/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json
index 83a4a66..d5c4b67 100644
--- a/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json
+++ b/components/autofill/core/browser/form_parsing/resources/legacy_regex_patterns.json
@@ -1224,35 +1224,6 @@
       }
     ]
   },
-  "LANDMARK": {
-    "en": [
-      {
-        "positive_pattern": "landmark",
-        "positive_score": 1.1,
-        "negative_pattern": null,
-        "match_field_attributes": ["LABEL", "NAME"],
-        "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"]
-      }
-    ],
-    "pt": [
-      {
-        "positive_pattern": "(?:ponto|complemento).*referência",
-        "positive_score": 1.1,
-        "negative_pattern": null,
-        "match_field_attributes": ["LABEL", "NAME"],
-        "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"]
-      }
-    ],
-    "es": [
-      {
-        "positive_pattern": "punto.*referencia",
-        "positive_score": 1.1,
-        "negative_pattern": null,
-        "match_field_attributes": ["LABEL", "NAME"],
-        "match_field_input_types": ["TEXT", "TEXT_AREA", "SEARCH"]
-      }
-    ]
-  },
   "SEARCH_TERM": {
     "en": [
       {
diff --git a/components/autofill/core/browser/geo/country_names.cc b/components/autofill/core/browser/geo/country_names.cc
index 451fbb9..900e576 100644
--- a/components/autofill/core/browser/geo/country_names.cc
+++ b/components/autofill/core/browser/geo/country_names.cc
@@ -8,8 +8,8 @@
 #include <memory>
 #include <utility>
 
+#include "base/i18n/rtl.h"
 #include "base/lazy_instance.h"
-#include "base/memory/singleton.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/synchronization/lock.h"
@@ -19,11 +19,6 @@
 namespace autofill {
 namespace {
 
-// A copy of the application locale string, which should be ready for
-// CountryName's construction.
-static base::LazyInstance<std::string>::DestructorAtExit g_application_locale =
-    LAZY_INSTANCE_INITIALIZER;
-
 // Computes the value for CountryNames::common_names_.
 std::map<std::string, std::string> GetCommonNames() {
   std::map<std::string, std::string> common_names;
@@ -61,34 +56,19 @@
 
 // static
 CountryNames* CountryNames::GetInstance() {
-  return base::Singleton<CountryNames>::get();
-}
-
-// static
-void CountryNames::SetLocaleString(const std::string& locale) {
-  DCHECK(!locale.empty());
-  // Application locale should never be empty. The empty value of
-  // |g_application_locale| means that it has not been initialized yet.
-  std::string* storage = g_application_locale.Pointer();
-  if (storage->empty()) {
-    *storage = locale;
-  }
-  // TODO(crbug.com/579971) CountryNames currently cannot adapt to changed
-  // locale without Chrome's restart.
+  static base::NoDestructor<CountryNames> instance(
+      base::i18n::GetConfiguredLocale());
+  return instance.get();
 }
 
 CountryNames::CountryNames(const std::string& locale_name)
     : application_locale_name_(locale_name),
-      default_locale_name_(std::string("en_US")),
+      default_locale_name_(std::string("en-US")),
       country_names_for_default_locale_(default_locale_name_),
       country_names_for_application_locale_(application_locale_name_),
       common_names_(GetCommonNames()),
       localized_country_names_cache_(10) {}
 
-CountryNames::CountryNames() : CountryNames(g_application_locale.Get()) {
-  DCHECK(!g_application_locale.Get().empty());
-}
-
 CountryNames::~CountryNames() = default;
 
 const std::string CountryNames::GetCountryCode(
diff --git a/components/autofill/core/browser/geo/country_names.h b/components/autofill/core/browser/geo/country_names.h
index 8c0345e..e5f6ae6 100644
--- a/components/autofill/core/browser/geo/country_names.h
+++ b/components/autofill/core/browser/geo/country_names.h
@@ -11,14 +11,10 @@
 #include <utility>
 
 #include "base/containers/lru_cache.h"
+#include "base/no_destructor.h"
 #include "base/synchronization/lock.h"
 #include "components/autofill/core/browser/geo/country_names_for_locale.h"
 
-namespace base {
-template <typename T>
-struct DefaultSingletonTraits;
-}  // namespace base
-
 namespace autofill {
 
 // A singleton class that encapsulates mappings from country names to their
@@ -32,11 +28,6 @@
   CountryNames(const CountryNames&) = delete;
   CountryNames& operator=(const CountryNames&) = delete;
 
-  // Tells CountryNames, what is the application locale. Only the first supplied
-  // value is used, further calls result in no changes.  Call this on the UI
-  // thread, before first using CountryNames. `locale` must not be empty.
-  static void SetLocaleString(const std::string& locale);
-
   // Returns the country code corresponding to the `country_name` queried for
   // the application and default locale.
   const std::string GetCountryCode(const std::u16string& country_name) const;
@@ -69,7 +60,7 @@
   // Create CountryNames for the default locale.
   CountryNames();
 
-  friend struct base::DefaultSingletonTraits<CountryNames>;
+  friend base::NoDestructor<CountryNames>;
 
   // Caches localized country name for a locale that is neither the application
   // or default locale. The Cache is keyed by the locale_name and contains
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.cc b/components/autofill/core/browser/payments/credit_card_access_manager.cc
index fa774ce0..d80ee10 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.cc
@@ -1125,29 +1125,53 @@
   virtual_card_unmask_response_details_ = response_details;
   if (result == AutofillClient::PaymentsRpcResult::kSuccess) {
     if (!response_details.real_pan.empty()) {
-      // Show confirmation on the progress dialog and then dismiss it.
-      client_->CloseAutofillProgressDialog(
-          /*show_confirmation_before_closing=*/true);
-
       // If the real pan is not empty, then complete card information has been
       // fetched from the server (this is ensured in Payments Client). Pass the
-      // unmasked card to |accessor_| and end the session.
-      CreditCard card = *card_;
+      // unmasked card to `accessor_` and end the session.
       DCHECK_EQ(response_details.card_type,
                 AutofillClient::PaymentsRpcCardType::kVirtualCard);
-      card.SetNumber(base::UTF8ToUTF16(response_details.real_pan));
-      card.SetExpirationMonthFromString(
+      card_->SetNumber(base::UTF8ToUTF16(response_details.real_pan));
+      card_->SetExpirationMonthFromString(
           base::UTF8ToUTF16(response_details.expiration_month),
           /*app_locale=*/std::string());
-      card.SetExpirationYearFromString(
+      card_->SetExpirationYearFromString(
           base::UTF8ToUTF16(response_details.expiration_year));
-      accessor_->OnCreditCardFetched(CreditCardFetchResult::kSuccess, &card,
-                                     base::UTF8ToUTF16(response_details.dcvv));
-      autofill_metrics::LogServerCardUnmaskResult(
-          autofill_metrics::ServerCardUnmaskResult::kRiskBasedUnmasked,
-          AutofillClient::PaymentsRpcCardType::kVirtualCard,
-          autofill_metrics::VirtualCardUnmaskFlowType::kUnspecified);
-      Reset();
+      // Check if we need to authenticate the user before filling the virtual
+      // card.
+      if (personal_data_manager_
+              ->IsAutofillPaymentMethodsMandatoryReauthEnabled()) {
+        // On some operating systems (for example, macOS and Windows), the
+        // device authentication prompt freezes Chrome. Thus we can only trigger
+        // the prompt after the progress dialog has been closed, which we can do
+        // by using the `no_interactive_authentication_callback` parameter in
+        // `AutofillClient::CloseAutofillProgressDialog()`.
+        // TODO(crbug.com/1427216): Implement this flow for Android as well.
+        client_->CloseAutofillProgressDialog(
+            /*show_confirmation_before_closing=*/false,
+            /*no_interactive_authentication_callback=*/base::BindOnce(
+                // `StartDeviceAuthenticationForFilling()` will asynchronously
+                // trigger the re-authentication flow, so we should avoid
+                // calling `Reset()` until the re-authentication flow is
+                // complete.
+                &CreditCardAccessManager::StartDeviceAuthenticationForFilling,
+                weak_ptr_factory_.GetWeakPtr(), accessor_, card_.get(),
+                base::UTF8ToUTF16(response_details.dcvv)));
+      } else {
+        client_->CloseAutofillProgressDialog(
+            /*show_confirmation_before_closing=*/true);
+        accessor_->OnCreditCardFetched(
+            CreditCardFetchResult::kSuccess, card_.get(),
+            base::UTF8ToUTF16(response_details.dcvv));
+        autofill_metrics::LogServerCardUnmaskResult(
+            autofill_metrics::ServerCardUnmaskResult::kRiskBasedUnmasked,
+            AutofillClient::PaymentsRpcCardType::kVirtualCard,
+            autofill_metrics::VirtualCardUnmaskFlowType::kUnspecified);
+        // `accessor_->OnCreditCardFetched()` makes a copy of `card` and `cvc`
+        // before it asynchronously fills them into the form. Thus we can safely
+        // call `Reset()` here, and we should as from this class' point of view
+        // the authentication flow is complete.
+        Reset();
+      }
       return;
     }
 
@@ -1402,6 +1426,10 @@
 
   is_authentication_in_progress_ = true;
 
+  CreditCard::RecordType record_type = card->record_type();
+  CHECK(record_type == CreditCard::LOCAL_CARD ||
+        record_type == CreditCard::VIRTUAL_CARD);
+
   // TODO(crbug.com/1427216): Add the iOS branching logic as well.
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
   device_authenticator->AuthenticateWithMessage(
@@ -1414,7 +1442,9 @@
   // DeviceAuthenticator::AuthenticateWithMessage() with the correct message
   // once it is supported. Currently, the message is "Verify it's you".
   device_authenticator->Authenticate(
-      device_reauth::DeviceAuthRequester::kLocalCardAutofill,
+      record_type == CreditCard::LOCAL_CARD
+          ? device_reauth::DeviceAuthRequester::kLocalCardAutofill
+          : device_reauth::DeviceAuthRequester::kVirtualCardAutofill,
       base::BindOnce(
           &CreditCardAccessManager::OnDeviceAuthenticationResponseForFilling,
           weak_ptr_factory_.GetWeakPtr(), accessor, card, cvc),
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager.h b/components/autofill/core/browser/payments/credit_card_access_manager.h
index 84afa1c..f5fd0e13 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager.h
+++ b/components/autofill/core/browser/payments/credit_card_access_manager.h
@@ -180,6 +180,12 @@
     return ShouldOfferFidoOptInDialog(response);
   }
 
+  void OnVirtualCardUnmaskResponseReceivedForTesting(
+      AutofillClient::PaymentsRpcResult result,
+      payments::PaymentsClient::UnmaskResponseDetails& response_details) {
+    OnVirtualCardUnmaskResponseReceived(result, response_details);
+  }
+
 #if BUILDFLAG(IS_ANDROID)
   bool ShouldOfferFidoAuthForTesting() { return ShouldOfferFidoAuth(); }
 #endif
@@ -193,11 +199,6 @@
                            PreflightCallRateLimited);
   FRIEND_TEST_ALL_PREFIXES(CreditCardAccessManagerTest,
                            UnmaskAuthFlowEvent_AlsoLogsVirtualCardSubhistogram);
-  FRIEND_TEST_ALL_PREFIXES(CreditCardAccessManagerTest,
-                           RiskBasedVirtualCardUnmasking_Success);
-  FRIEND_TEST_ALL_PREFIXES(
-      CreditCardAccessManagerTest,
-      RiskBasedVirtualCardUnmasking_AuthenticationRequired_OtpOnly);
   FRIEND_TEST_ALL_PREFIXES(
       CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoAndOtp_PrefersFido);
@@ -218,9 +219,6 @@
       RiskBasedVirtualCardUnmasking_CreditCardAccessManagerReset_TriggersOtpAuthenticatorResetOnFlowCancelled);
   FRIEND_TEST_ALL_PREFIXES(
       CreditCardAccessManagerTest,
-      RiskBasedVirtualCardUnmasking_Failure_MerchantOptedOut);
-  FRIEND_TEST_ALL_PREFIXES(
-      CreditCardAccessManagerTest,
       RiskBasedVirtualCardUnmasking_Failure_NoOptionReturned);
   FRIEND_TEST_ALL_PREFIXES(
       CreditCardAccessManagerTest,
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 1c1c4d8b..fb479731 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -468,7 +468,7 @@
     if (fido_authenticator_is_user_opted_in)
       response.fido_request_options = GetTestRequestOptions();
 #endif
-    credit_card_access_manager_->OnVirtualCardUnmaskResponseReceived(
+    credit_card_access_manager_->OnVirtualCardUnmaskResponseReceivedForTesting(
         AutofillClient::PaymentsRpcResult::kSuccess, response);
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
@@ -649,6 +649,9 @@
     CreditCardAccessManagerTest::SetUp();
     feature_list_.InitWithFeatureState(
         features::kAutofillEnablePaymentsMandatoryReauth, FeatureFlagIsOn());
+    autofill_client_.GetPrefs()->SetBoolean(
+        prefs::kAutofillPaymentMethodsMandatoryReauth,
+        /*value=*/PrefIsEnabled());
   }
 
   bool FeatureFlagIsOn() const { return std::get<0>(GetParam()); }
@@ -659,6 +662,35 @@
     return std::get<2>(GetParam());
   }
 
+  void SetUpDeviceAuthenticatorResponseMock() {
+    // We should only expect an AuthenticateWithMessage() call if the feature
+    // flag is on and the pref is enabled.
+    if (FeatureFlagIsOn() && PrefIsEnabled()) {
+      ON_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>(
+                  autofill_client_.GetDeviceAuthenticator().get()),
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+              AuthenticateWithMessage)
+#elif BUILDFLAG(IS_ANDROID)
+              Authenticate)
+#endif
+          .WillByDefault(testing::WithArg<1>(
+              testing::Invoke([mandatory_reauth_response_is_success =
+                                   MandatoryReauthResponseIsSuccess()](
+                                  base::OnceCallback<void(bool)> callback) {
+                std::move(callback).Run(mandatory_reauth_response_is_success);
+              })));
+    } else {
+      EXPECT_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>(
+                      autofill_client_.GetDeviceAuthenticator().get()),
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+                  AuthenticateWithMessage)
+#elif BUILDFLAG(IS_ANDROID)
+                  Authenticate)
+#endif
+          .Times(0);
+    }
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
@@ -667,41 +699,13 @@
 // Mandatory Re-Auth feature.
 TEST_P(CreditCardAccessManagerMandatoryReauthTest,
        MandatoryReauth_FetchLocalCard) {
-  autofill_client_.GetPrefs()->SetBoolean(
-      prefs::kAutofillPaymentMethodsMandatoryReauth, /*value=*/PrefIsEnabled());
   CreateLocalCard(kTestGUID, kTestNumber);
   CreditCard* card = personal_data().GetCreditCardByGUID(kTestGUID);
 
   credit_card_access_manager_->PrepareToFetchCreditCard();
   WaitForCallbacks();
 
-  // We should only expect an AuthenticateWithMessage() call if the feature flag
-  // is on and the pref is enabled.
-  if (FeatureFlagIsOn() && PrefIsEnabled()) {
-    ON_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>(
-                autofill_client_.GetDeviceAuthenticator().get()),
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-            AuthenticateWithMessage)
-#elif BUILDFLAG(IS_ANDROID)
-            Authenticate)
-#endif
-        .WillByDefault(testing::WithArg<1>(
-            testing::Invoke([mandatory_reauth_response_is_success =
-                                 MandatoryReauthResponseIsSuccess()](
-                                base::OnceCallback<void(bool)> callback) {
-              std::move(callback).Run(mandatory_reauth_response_is_success);
-            })));
-  } else {
-    EXPECT_CALL(*static_cast<device_reauth::MockDeviceAuthenticator*>(
-                    autofill_client_.GetDeviceAuthenticator().get()),
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-                AuthenticateWithMessage)
-#elif BUILDFLAG(IS_ANDROID)
-                Authenticate)
-#endif
-        .Times(0);
-  }
-
+  SetUpDeviceAuthenticatorResponseMock();
   credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
 
   // The only time we should expect an error is if the feature flag is on, the
@@ -717,6 +721,48 @@
   }
 }
 
+// Tests that retrieving virtual cards works correctly in the context of the
+// Mandatory Re-Auth feature.
+TEST_P(CreditCardAccessManagerMandatoryReauthTest,
+       MandatoryReauth_FetchVirtualCard) {
+  CreateServerCard(kTestGUID, kTestNumber, /*masked=*/false, kTestServerId);
+  CreditCard* virtual_card = personal_data().GetCreditCardByGUID(kTestGUID);
+  virtual_card->set_record_type(CreditCard::VIRTUAL_CARD);
+
+  credit_card_access_manager_->FetchCreditCard(virtual_card,
+                                               accessor_->GetWeakPtr());
+
+  // Ensures the UnmaskRequestDetails is populated with correct contents.
+  EXPECT_TRUE(payments_client_->unmask_request()->context_token.empty());
+  EXPECT_FALSE(payments_client_->unmask_request()->risk_data.empty());
+  EXPECT_TRUE(payments_client_->unmask_request()
+                  ->last_committed_primary_main_frame_origin.has_value());
+
+  // Mock server response with valid card information.
+  payments::PaymentsClient::UnmaskResponseDetails response;
+  response.real_pan = "4111111111111111";
+  response.dcvv = "321";
+  response.expiration_month = test::NextMonth();
+  response.expiration_year = test::NextYear();
+  response.card_type = AutofillClient::PaymentsRpcCardType::kVirtualCard;
+
+  SetUpDeviceAuthenticatorResponseMock();
+  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceivedForTesting(
+      AutofillClient::PaymentsRpcResult::kSuccess, response);
+
+  // Expect the accessor receives the correct response.
+  if (FeatureFlagIsOn() && PrefIsEnabled() &&
+      !MandatoryReauthResponseIsSuccess()) {
+    EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kTransientError);
+  } else {
+    EXPECT_EQ(accessor_->result(), CreditCardFetchResult::kSuccess);
+    EXPECT_EQ(accessor_->number(), u"4111111111111111");
+    EXPECT_EQ(accessor_->cvc(), u"321");
+    EXPECT_EQ(accessor_->expiry_month(), base::UTF8ToUTF16(test::NextMonth()));
+    EXPECT_EQ(accessor_->expiry_year(), base::UTF8ToUTF16(test::NextYear()));
+  }
+}
+
 INSTANTIATE_TEST_SUITE_P(,
                          CreditCardAccessManagerMandatoryReauthTest,
                          testing::Combine(testing::Bool(),
@@ -2469,7 +2515,7 @@
   response.expiration_month = test::NextMonth();
   response.expiration_year = test::NextYear();
   response.card_type = AutofillClient::PaymentsRpcCardType::kVirtualCard;
-  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceived(
+  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceivedForTesting(
       AutofillClient::PaymentsRpcResult::kSuccess, response);
 
   // Expect the accessor receives the correct response.
@@ -2645,7 +2691,7 @@
   payments::PaymentsClient::UnmaskResponseDetails response;
   response.context_token = "fake_context_token";
   response.fido_request_options = GetTestRequestOptions();
-  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceived(
+  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceivedForTesting(
       AutofillClient::PaymentsRpcResult::kSuccess, response);
 
   // Expect the CreditCardAccessManager invokes the FIDO authenticator.
@@ -2706,7 +2752,7 @@
           {CardUnmaskChallengeOptionType::kSmsOtp})[0];
   response.card_unmask_challenge_options.emplace_back(challenge_option);
   response.fido_request_options = GetTestRequestOptions();
-  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceived(
+  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceivedForTesting(
       AutofillClient::PaymentsRpcResult::kSuccess, response);
 
   // Expect the CreditCardAccessManager invokes the FIDO authenticator.
@@ -2826,7 +2872,7 @@
   payments::PaymentsClient::UnmaskResponseDetails response;
   response.context_token = "fake_context_token";
   response.fido_request_options = GetTestRequestOptions();
-  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceived(
+  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceivedForTesting(
       AutofillClient::PaymentsRpcResult::kSuccess, response);
 
   // Expect the CreditCardAccessManager to end the session.
@@ -2874,7 +2920,7 @@
   // Mock server response with no challenge options.
   payments::PaymentsClient::UnmaskResponseDetails response;
   response.context_token = "fake_context_token";
-  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceived(
+  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceivedForTesting(
       AutofillClient::PaymentsRpcResult::kPermanentFailure, response);
 
   // Expect the CreditCardAccessManager to end the session.
@@ -2919,7 +2965,7 @@
 
   // Mock server response with no challenge options.
   payments::PaymentsClient::UnmaskResponseDetails response;
-  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceived(
+  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceivedForTesting(
       AutofillClient::PaymentsRpcResult::kVcnRetrievalPermanentFailure,
       response);
 
@@ -2956,7 +3002,7 @@
 
   payments::PaymentsClient::UnmaskResponseDetails response;
   response.autofill_error_dialog_context = autofill_error_dialog_context;
-  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceived(
+  credit_card_access_manager_->OnVirtualCardUnmaskResponseReceivedForTesting(
       AutofillClient::PaymentsRpcResult::kVcnRetrievalTryAgainFailure,
       response);
 
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 6963a32..57d0c70 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -42,7 +42,6 @@
 #include "components/autofill/core/browser/geo/address_i18n.h"
 #include "components/autofill/core/browser/geo/autofill_country.h"
 #include "components/autofill/core/browser/geo/country_data.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/geo/phone_number_i18n.h"
 #include "components/autofill/core/browser/manual_testing_import.h"
 #include "components/autofill/core/browser/metrics/autofill_metrics.h"
@@ -319,7 +318,6 @@
     StrikeDatabaseBase* strike_database,
     AutofillImageFetcher* image_fetcher,
     bool is_off_the_record) {
-  CountryNames::SetLocaleString(app_locale_);
   database_helper_->Init(profile_database, account_database);
 
   SetPrefService(pref_service);
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h
index 61e8424..d199f2c 100644
--- a/components/autofill/core/browser/test_autofill_client.h
+++ b/components/autofill/core/browser/test_autofill_client.h
@@ -411,6 +411,14 @@
     autofill_error_dialog_context_ = context;
   }
 
+  void CloseAutofillProgressDialog(
+      bool show_confirmation_before_closing,
+      base::OnceClosure no_user_perceived_authentication_callback) override {
+    if (no_user_perceived_authentication_callback) {
+      std::move(no_user_perceived_authentication_callback).Run();
+    }
+  }
+
   bool IsAutocompleteEnabled() const override { return true; }
 
   bool IsPasswordManagerEnabled() override { return true; }
diff --git a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
index 7ba80351..f62fd9d2a 100644
--- a/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_profile_sync_bridge_unittest.cc
@@ -24,7 +24,6 @@
 #include "base/uuid.h"
 #include "components/autofill/core/browser/autofill_profile_sync_util.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/webdata/autofill_change.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
@@ -268,7 +267,6 @@
   void SetUp() override {
     // Fix a time for implicitly constructed use_dates in AutofillProfile.
     test_clock_.SetNow(kJune2017);
-    CountryNames::SetLocaleString(kLocaleString);
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     db_.AddTable(&table_);
     db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
index 68e2d910..cc12d49 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_sync_bridge_unittest.cc
@@ -22,7 +22,6 @@
 #include "components/autofill/core/browser/data_model/autofill_metadata.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/webdata/autofill_sync_bridge_test_util.h"
 #include "components/autofill/core/browser/webdata/autofill_sync_bridge_util.h"
@@ -90,8 +89,6 @@
 const char kLocalAddr1ServerId[] = "e171e3ed-858a-4dd5-9bf3-8517f14ba5fc";
 const char kLocalAddr2ServerId[] = "fa232b9a-f248-4e5a-8d76-d46f821c0c5f";
 
-const char kLocaleString[] = "en-US";
-
 const char kDefaultCacheGuid[] = "CacheGuid";
 
 base::Time UseDateFromProtoValue(int64_t use_date_proto_value) {
@@ -299,7 +296,6 @@
   void SetUp() override {
     // Fix a time for implicitly constructed use_dates in AutofillProfile.
     test_clock_.SetNow(kDefaultTime);
-    CountryNames::SetLocaleString(kLocaleString);
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     db_.AddTable(&table_);
     db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc
index 4b55796..3abb9f9 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc
@@ -19,7 +19,6 @@
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/webdata/autofill_sync_bridge_util.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
@@ -53,7 +52,6 @@
 using testing::NiceMock;
 using testing::Return;
 
-const char kLocaleString[] = "en-US";
 const char kDefaultCacheGuid[] = "CacheGuid";
 
 void ExtractAutofillOfferSpecificsFromDataBatch(
@@ -134,7 +132,6 @@
       const AutofillWalletOfferSyncBridgeTest&) = delete;
 
   void SetUp() override {
-    CountryNames::SetLocaleString(kLocaleString);
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     db_.AddTable(&table_);
     db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
index 9ec3576..a525d84c 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge_unittest.cc
@@ -24,7 +24,6 @@
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/payments/payments_customer_data.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/webdata/autofill_sync_bridge_test_util.h"
@@ -82,7 +81,6 @@
 const char kCustomerDataClientTag[] = "deadbeef";
 const char kCloudTokenDataClientTag[] = "token";
 
-const char kLocaleString[] = "en-US";
 const base::Time kJune2017 = base::Time::FromDoubleT(1497552271);
 
 const char kDefaultCacheGuid[] = "CacheGuid";
@@ -223,7 +221,6 @@
   void SetUp() override {
     // Fix a time for implicitly constructed use_dates in AutofillProfile.
     test_clock_.SetNow(kJune2017);
-    CountryNames::SetLocaleString(kLocaleString);
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     db_.AddTable(&table_);
     db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc
index 4e2540c..6d0e121 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_usage_data_sync_bridge_unittest.cc
@@ -16,7 +16,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/task_environment.h"
 #include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/test_autofill_clock.h"
 #include "components/autofill/core/browser/webdata/autofill_sync_bridge_util.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
@@ -43,7 +42,6 @@
 using testing::NiceMock;
 using testing::Return;
 
-const char kLocaleString[] = "en-US";
 const std::string kExpectedClientTagAndStorageKey =
     "VirtualCardUsageData|12345|google|https://www.google.com";
 
@@ -63,7 +61,6 @@
 class AutofillWalletUsageDataSyncBridgeTest : public testing::Test {
  public:
   void SetUp() override {
-    CountryNames::SetLocaleString(kLocaleString);
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     db_.AddTable(&table_);
     db_.Init(temp_dir_.GetPath().AppendASCII("SyncTestWebDatabase"));
diff --git a/components/autofill/core/common/autofill_regex_constants.h b/components/autofill/core/common/autofill_regex_constants.h
index 863233c..a692ae54 100644
--- a/components/autofill/core/common/autofill_regex_constants.h
+++ b/components/autofill/core/common/autofill_regex_constants.h
@@ -179,10 +179,6 @@
     u"|((\\b|_|\\*)(eyalet|[şs]ehir|[İii̇]l(imiz)?|kent)(\\b|_|\\*))"  // tr
     u"|^시[·・]?도"                                                   // ko-KR
     u"|provinci";                                                     // id
-inline constexpr char16_t kLandmarkRe[] =
-    u"landmark"
-    u"|(?:ponto|complemento).*referência"  // pt-BR, pt-PT
-    u"|punto.*referencia";                 // es
 
 /////////////////////////////////////////////////////////////////////////////
 // search_field.cc
diff --git a/components/device_reauth/device_authenticator.h b/components/device_reauth/device_authenticator.h
index a08d9e1..8d0ea1c9 100644
--- a/components/device_reauth/device_authenticator.h
+++ b/components/device_reauth/device_authenticator.h
@@ -61,7 +61,11 @@
   // Settings page on Android.
   kPaymentMethodsReauthInSettings = 10,
 
-  kMaxValue = kPaymentMethodsReauthInSettings,
+  // The prompt displayed when the user is trying to autofill a virtual card,
+  // and re-auth is triggered.
+  kVirtualCardAutofill = 11,
+
+  kMaxValue = kVirtualCardAutofill,
 };
 
 // This interface encapsulates operations related to biometric authentication.
diff --git a/components/device_signals/core/browser/mock_user_permission_service.h b/components/device_signals/core/browser/mock_user_permission_service.h
index d1f75ee8..6a5af88b 100644
--- a/components/device_signals/core/browser/mock_user_permission_service.h
+++ b/components/device_signals/core/browser/mock_user_permission_service.h
@@ -15,13 +15,14 @@
   MockUserPermissionService();
   ~MockUserPermissionService() override;
 
-  MOCK_METHOD(bool, ShouldCollectConsent, (), (override));
+  MOCK_METHOD(bool, HasUserConsented, (), (const override));
+  MOCK_METHOD(bool, ShouldCollectConsent, (), (const override));
   MOCK_METHOD(UserPermission,
               CanUserCollectSignals,
               (const UserContext&),
-              (override));
+              (const override));
 
-  MOCK_METHOD(UserPermission, CanCollectSignals, (), (override));
+  MOCK_METHOD(UserPermission, CanCollectSignals, (), (const override));
   MOCK_METHOD(void, ResetUserConsentIfNeeded, (), (override));
 };
 
diff --git a/components/device_signals/core/browser/user_permission_service.h b/components/device_signals/core/browser/user_permission_service.h
index 8a19cfc..abbb06f 100644
--- a/components/device_signals/core/browser/user_permission_service.h
+++ b/components/device_signals/core/browser/user_permission_service.h
@@ -50,18 +50,24 @@
 
   // Returns true if consent is required based on the current context and is
   // missing.
-  virtual bool ShouldCollectConsent() = 0;
+  virtual bool ShouldCollectConsent() const = 0;
 
   // Will verify whether context-aware signals can be collected
   // on behalf of the user represented by `user_context`. Returns `kGranted` if
   // collection is allowed.
   virtual UserPermission CanUserCollectSignals(
-      const UserContext& user_context) = 0;
+      const UserContext& user_context) const = 0;
 
   // Will verify whether context-aware signals can be collected
   // based on the current context (e.g. browser-wide management, user logged-in
   // to a Profile). Returns `kGranted` if collection is allowed.
-  virtual UserPermission CanCollectSignals() = 0;
+  virtual UserPermission CanCollectSignals() const = 0;
+
+  // Returns whether the user has explicitly agreed to device signals being
+  // shared or not. Depending on the current management context, the returned
+  // value could be false even though signals can be collected. This function
+  // is exposed publicly mostly for debugging purposes.
+  virtual bool HasUserConsented() const = 0;
 
   // Will evaluate whether the user's consent should be reset or not based on
   // the current management context.
diff --git a/components/device_signals/core/browser/user_permission_service_impl.cc b/components/device_signals/core/browser/user_permission_service_impl.cc
index 7236e8e0..0b3e1f71 100644
--- a/components/device_signals/core/browser/user_permission_service_impl.cc
+++ b/components/device_signals/core/browser/user_permission_service_impl.cc
@@ -43,7 +43,11 @@
   return weak_factory_.GetWeakPtr();
 }
 
-bool UserPermissionServiceImpl::ShouldCollectConsent() {
+bool UserPermissionServiceImpl::HasUserConsented() const {
+  return user_prefs_->GetBoolean(prefs::kDeviceSignalsConsentReceived);
+}
+
+bool UserPermissionServiceImpl::ShouldCollectConsent() const {
   if (HasUserConsented()) {
     // Already have the user consent, so no need to collect.
     return false;
@@ -71,7 +75,7 @@
 }
 
 UserPermission UserPermissionServiceImpl::CanUserCollectSignals(
-    const UserContext& user_context) {
+    const UserContext& user_context) const {
   // Return "unknown user" if no user ID was given.
   if (user_context.user_id.empty()) {
     return UserPermission::kMissingUser;
@@ -104,7 +108,7 @@
   return UserPermission::kGranted;
 }
 
-UserPermission UserPermissionServiceImpl::CanCollectSignals() {
+UserPermission UserPermissionServiceImpl::CanCollectSignals() const {
   if (HasUserConsented()) {
     return UserPermission::kGranted;
   }
@@ -153,10 +157,6 @@
       prefs::kUnmanagedDeviceSignalsConsentFlowEnabled);
 }
 
-bool UserPermissionServiceImpl::HasUserConsented() const {
-  return user_prefs_->GetBoolean(prefs::kDeviceSignalsConsentReceived);
-}
-
 bool UserPermissionServiceImpl::IsDeviceCloudManaged() const {
   return management_service_->HasManagementAuthority(
       policy::EnterpriseManagementAuthority::CLOUD_DOMAIN);
diff --git a/components/device_signals/core/browser/user_permission_service_impl.h b/components/device_signals/core/browser/user_permission_service_impl.h
index 23d493f..a5148a2 100644
--- a/components/device_signals/core/browser/user_permission_service_impl.h
+++ b/components/device_signals/core/browser/user_permission_service_impl.h
@@ -38,20 +38,17 @@
   base::WeakPtr<UserPermissionServiceImpl> GetWeakPtr();
 
   // UserPermissionService:
-  bool ShouldCollectConsent() override;
+  bool ShouldCollectConsent() const override;
   UserPermission CanUserCollectSignals(
-      const UserContext& user_context) override;
-  UserPermission CanCollectSignals() override;
+      const UserContext& user_context) const override;
+  UserPermission CanCollectSignals() const override;
+  bool HasUserConsented() const override;
   void ResetUserConsentIfNeeded() override;
 
  private:
   // Returns true if the specific consent flow policy is enabled.
   bool IsConsentFlowPolicyEnabled() const;
 
-  // Returns whether the user has explicitly agreed to device signals being
-  // shared or not.
-  bool HasUserConsented() const;
-
   // Returns true if the device is Cloud-managed.
   bool IsDeviceCloudManaged() const;
 
diff --git a/components/global_media_controls/public/views/media_item_ui_view.cc b/components/global_media_controls/public/views/media_item_ui_view.cc
index be8d843..bf99a91 100644
--- a/components/global_media_controls/public/views/media_item_ui_view.cc
+++ b/components/global_media_controls/public/views/media_item_ui_view.cc
@@ -166,7 +166,7 @@
         std::make_unique<views::SlideOutController>(this, this);
 
     if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsModernUI)) {
-      footer_view_ = footer_view_.get();
+      footer_view_ = footer_view.get();
       view = std::make_unique<
           media_message_center::MediaNotificationViewModernImpl>(
           this, std::move(item), std::move(dismiss_button_placeholder),
diff --git a/components/omnibox/browser/autocomplete_controller.cc b/components/omnibox/browser/autocomplete_controller.cc
index 5c6c056f..3a73645 100644
--- a/components/omnibox/browser/autocomplete_controller.cc
+++ b/components/omnibox/browser/autocomplete_controller.cc
@@ -1006,7 +1006,8 @@
     // If not all providers are done, merge the old and new matches before
     // sorting.
     result_.TransferOldMatches(input_, &old_matches_to_reuse);
-  } else if (OmniboxFieldTrial::IsMlUrlScoringEnabled()) {
+  } else if (OmniboxFieldTrial::IsMlUrlScoringEnabled() &&
+             provider_client_->GetAutocompleteScoringModelService()) {
     // The async scoring model is only run once all the providers are done. Use
     // a WeakPtr since the model is not owned and `this` may no longer be alive.
     // `SortCullAndAnnotateResult()` is called when the model is done.
diff --git a/components/permissions/unused_site_permissions_service.cc b/components/permissions/unused_site_permissions_service.cc
index 3dd9574..8954e41 100644
--- a/components/permissions/unused_site_permissions_service.cc
+++ b/components/permissions/unused_site_permissions_service.cc
@@ -9,6 +9,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/run_loop.h"
 #include "base/task/task_traits.h"
 #include "base/task/thread_pool.h"
@@ -38,6 +39,15 @@
 
 namespace permissions {
 namespace {
+// Reflects the maximum number of days between a permissions being revoked and
+// the time when the user regrants the permission through the unused site
+// permission module of Safete Check. The maximum number of days is determined
+// by `kRevocationCleanUpThreshold`.
+size_t kAllowAgainMetricsExclusiveMaxCount = 31;
+
+// Using a single bucket per day, following the value of
+// |kAllowAgainMetricsExclusiveMaxCount|.
+size_t kAllowAgainMetricsBuckets = 31;
 
 // Called on a background thread.
 UnusedSitePermissionsService::UnusedPermissionMap GetUnusedPermissionsMap(
@@ -190,6 +200,16 @@
   hcsm_->SetWebsiteSettingCustomScope(
       info.primary_pattern, info.secondary_pattern,
       ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, {});
+
+  // Record the days elapsed from auto-revocation to regrant.
+  base::Time revoked_time =
+      info.metadata.expiration -
+      content_settings::features::
+          kSafetyCheckUnusedSitePermissionsRevocationCleanUpThreshold.Get();
+  base::UmaHistogramCustomCounts(
+      "Settings.SafetyCheck.UnusedSitePermissionsAllowAgainDays",
+      (clock_->Now() - revoked_time).InDays(), 0,
+      kAllowAgainMetricsExclusiveMaxCount, kAllowAgainMetricsBuckets);
 }
 
 void UnusedSitePermissionsService::UndoRegrantPermissionsForOrigin(
diff --git a/components/permissions/unused_site_permissions_service_unittest.cc b/components/permissions/unused_site_permissions_service_unittest.cc
index 5503b6d..6e85d0a 100644
--- a/components/permissions/unused_site_permissions_service_unittest.cc
+++ b/components/permissions/unused_site_permissions_service_unittest.cc
@@ -7,6 +7,7 @@
 #include <list>
 #include <memory>
 #include "base/memory/scoped_refptr.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/time/clock.h"
@@ -110,7 +111,7 @@
   const base::TimeDelta precision =
       content_settings::GetCoarseVisitedTimePrecision();
 
-  // Add one setting for url1 and two settings for url2.
+  // Add one setting for `url1` and two settings for `url2`.
   hcsm()->SetContentSettingDefaultScope(
       url1, url1, type1, ContentSetting::CONTENT_SETTING_ALLOW, constraint);
   hcsm()->SetContentSettingDefaultScope(
@@ -130,7 +131,7 @@
   EXPECT_EQ(service()->GetTrackedUnusedPermissionsForTesting().size(), 3u);
   EXPECT_EQ(GetRevokedUnusedPermissions(hcsm()).size(), 0u);
 
-  // Visit url2 and check that the corresponding content setting got updated.
+  // Visit `url2` and check that the corresponding content setting got updated.
   UnusedSitePermissionsService::TabHelper::CreateForWebContents(web_contents(),
                                                                 service());
   NavigateAndCommit(url2);
@@ -159,7 +160,7 @@
   EXPECT_EQ(url2_str, service()
                           ->GetTrackedUnusedPermissionsForTesting()[1]
                           .source.primary_pattern.ToString());
-  // url1 should be on revoked permissions list.
+  // `url1` should be on revoked permissions list.
   EXPECT_EQ(GetRevokedUnusedPermissions(hcsm()).size(), 1u);
   std::string url1_str =
       ContentSettingsPattern::FromURLNoWildcard(url1).ToString();
@@ -189,7 +190,7 @@
   // Travel through time for 20 days.
   clock()->Advance(base::Days(20));
 
-  // Only url1 should be tracked because it is the only single origin url.
+  // Only `url1` should be tracked because it is the only single origin url.
   service()->UpdateUnusedPermissionsForTesting();
   EXPECT_EQ(service()->GetTrackedUnusedPermissionsForTesting().size(), 1u);
   auto tracked_origin = service()->GetTrackedUnusedPermissionsForTesting()[0];
@@ -344,7 +345,7 @@
   permission_type_list.Append(static_cast<int32_t>(type));
   dict.Set(kRevokedKey, base::Value::List(std::move(permission_type_list)));
 
-  // Add url1 and url2 to revoked permissions list.
+  // Add `url1` and `url2` to revoked permissions list.
   hcsm()->SetWebsiteSettingDefaultScope(
       GURL(url1), GURL(url1),
       ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
@@ -361,21 +362,21 @@
       &revoked_permissions_list);
   EXPECT_EQ(2U, revoked_permissions_list.size());
 
-  // Allow the permission for url1 again
+  // Allow the permission for `url1` again
   service()->RegrantPermissionsForOrigin(url::Origin::Create(GURL(url1)));
 
-  // Check there is only url2 in revoked permissions list.
+  // Check there is only `url2` in revoked permissions list.
   hcsm()->GetSettingsForOneType(
       ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
       &revoked_permissions_list);
   EXPECT_EQ(1U, revoked_permissions_list.size());
 
-  // Check if the permissions of url1 is regranted.
+  // Check if the permissions of `url1` is regranted.
   EXPECT_EQ(ContentSetting::CONTENT_SETTING_ALLOW,
             hcsm()->GetContentSetting(GURL(url1), GURL(url1), type));
 
-  // Undoing the changes should add url1 back to the list of revoked permissions
-  // and reset its permissions.
+  // Undoing the changes should add `url1` back to the list of revoked
+  // permissions and reset its permissions.
   service()->UndoRegrantPermissionsForOrigin({type}, absl::nullopt,
                                              url::Origin::Create(GURL(url1)));
 
@@ -510,7 +511,7 @@
   permission_type_list.Append(static_cast<int32_t>(type));
   dict.Set(kRevokedKey, base::Value::List(std::move(permission_type_list)));
 
-  // Add url1 and url2 to revoked permissions list.
+  // Add `url1` and `url2` to revoked permissions list.
   hcsm()->SetWebsiteSettingDefaultScope(
       GURL(url1), GURL(url1),
       ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
@@ -584,4 +585,47 @@
   ASSERT_FALSE(days_since_revocation.has_value());
 }
 
+TEST_F(UnusedSitePermissionsServiceTest, RecordRegrantMetricForAllowAgain) {
+  const std::string url = "https://example.com:443";
+  base::Value::Dict dict = base::Value::Dict();
+  base::Value::List permission_type_list = base::Value::List();
+  permission_type_list.Append(
+      static_cast<int32_t>(ContentSettingsType::GEOLOCATION));
+  dict.Set(kRevokedKey, base::Value::List(std::move(permission_type_list)));
+
+  auto cleanUpThreshold =
+      content_settings::features::
+          kSafetyCheckUnusedSitePermissionsRevocationCleanUpThreshold.Get();
+  const content_settings::ContentSettingConstraints constraint{
+      .expiration = clock()->Now() + cleanUpThreshold};
+
+  // Add `url` to revoked permissions list.
+  hcsm()->SetWebsiteSettingDefaultScope(
+      GURL(url), GURL(url),
+      ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
+      base::Value(dict.Clone()), constraint);
+
+  // Assert there is 1 origin in revoked permissions list.
+  ContentSettingsForOneType revoked_permissions_list;
+  hcsm()->GetSettingsForOneType(
+      ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS,
+      &revoked_permissions_list);
+  ASSERT_EQ(1U, revoked_permissions_list.size());
+
+  // Advance 14 days; this will be the expected histogram sample.
+  clock()->Advance(base::Days(14));
+  base::HistogramTester histogram_tester;
+
+  // Allow the permission for `url` again
+  service()->RegrantPermissionsForOrigin(url::Origin::Create(GURL(url)));
+
+  // Only a single entry should be recorded in the histogram.
+  const std::vector<base::Bucket> buckets = histogram_tester.GetAllSamples(
+      "Settings.SafetyCheck.UnusedSitePermissionsAllowAgainDays");
+  EXPECT_EQ(1U, buckets.size());
+  // The recorded metric should be the elapsed days since the revocation.
+  histogram_tester.ExpectUniqueSample(
+      "Settings.SafetyCheck.UnusedSitePermissionsAllowAgainDays", 14, 1);
+}
+
 }  // namespace permissions
diff --git a/components/remote_cocoa/app_shim/immersive_mode_controller.h b/components/remote_cocoa/app_shim/immersive_mode_controller.h
index e1cdcea..2b4398b7 100644
--- a/components/remote_cocoa/app_shim/immersive_mode_controller.h
+++ b/components/remote_cocoa/app_shim/immersive_mode_controller.h
@@ -102,6 +102,9 @@
   void LayoutWindowWithAnchorView(NSWindow* window, NSView* anchor_view);
 
  private:
+  // Get offscreen y origin. Used for moving overlay windows offscreen.
+  double GetOffscreenYOrigin();
+
   bool enabled_ = false;
 
   NSWindow* const browser_window_;
diff --git a/components/remote_cocoa/app_shim/immersive_mode_controller.mm b/components/remote_cocoa/app_shim/immersive_mode_controller.mm
index 361dcf1..3592a6c 100644
--- a/components/remote_cocoa/app_shim/immersive_mode_controller.mm
+++ b/components/remote_cocoa/app_shim/immersive_mode_controller.mm
@@ -510,6 +510,20 @@
   return false;
 }
 
+double ImmersiveModeController::GetOffscreenYOrigin() {
+  // Get the height of the screen. Using this as the y origin will move a window
+  // offscreen.
+  double y = browser_window_.screen.frame.size.height;
+
+  // Make sure to make it past the safe area insets, otherwise some portion
+  // of the window may still be displayed.
+  if (@available(macOS 12.0, *)) {
+    y += browser_window_.screen.safeAreaInsets.top;
+  }
+
+  return y;
+}
+
 void ImmersiveModeController::LayoutWindowWithAnchorView(NSWindow* window,
                                                          NSView* anchor_view) {
   // Find the anchor view's point on screen (bottom left).
@@ -528,21 +542,18 @@
     // state we don't want the window on screen, otherwise it may mask input to
     // the browser view. In all other cases will not enter this branch and the
     // window will be placed at the same coordinates as the anchor view.
-    // If the toolbar is hidden (mojom::ToolbarVisibilityStyle::kNone) also move
-    // the window offscreen.
-    if (anchor_view.visibleRect.size.height != anchor_view.frame.size.height ||
-        last_used_style_ == mojom::ToolbarVisibilityStyle::kNone) {
-      // Move the window off the top of the screen.
-      point_on_screen.y = browser_window_.screen.frame.size.height;
-
-      // Make sure to make it past the safe area insets, otherwise some portion
-      // of the window may still be displayed.
-      if (@available(macOS 12.0, *)) {
-        point_on_screen.y += browser_window_.screen.safeAreaInsets.top;
-      }
+    if (anchor_view.visibleRect.size.height != anchor_view.frame.size.height) {
+      point_on_screen.y = GetOffscreenYOrigin();
     }
   }
 
+  // If the toolbar is hidden (mojom::ToolbarVisibilityStyle::kNone) also move
+  // the window offscreen. This applies to all versions of macOS where Chrome
+  // can be run.
+  if (last_used_style_ == mojom::ToolbarVisibilityStyle::kNone) {
+    point_on_screen.y = GetOffscreenYOrigin();
+  }
+
   [window setFrameOrigin:point_on_screen];
 }
 
diff --git a/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.mm b/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.mm
index 2c717a06..cccd9d8 100644
--- a/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.mm
+++ b/components/remote_cocoa/app_shim/immersive_mode_tabbed_controller.mm
@@ -135,6 +135,13 @@
       break;
   }
   ImmersiveModeController::UpdateToolbarVisibility(style);
+
+  // macOS 10.15 does not call `OnTitlebarFrameDidChange` as often as newer
+  // versions of macOS. Add a layout call here and in `RevealLock` and
+  // `RevealUnlock` to pickup the slack. There is no harm in extra layout calls
+  // on newer versions of macOS, -setFrameOrigin: is essentially a NOP when the
+  // frame size doesn't change.
+  LayoutWindowWithAnchorView(tab_window_, tab_content_view_);
 }
 
 void ImmersiveModeTabbedController::AddController() {
@@ -164,6 +171,7 @@
 
   // Call after TitlebarReveal() for a proper layout.
   ImmersiveModeController::RevealLock();
+  LayoutWindowWithAnchorView(tab_window_, tab_content_view_);
 }
 
 void ImmersiveModeTabbedController::RevealUnlock() {
@@ -176,6 +184,7 @@
 
   // Call after TitlebarHide() for a proper layout.
   ImmersiveModeController::RevealUnlock();
+  LayoutWindowWithAnchorView(tab_window_, tab_content_view_);
 }
 
 void ImmersiveModeTabbedController::TitlebarReveal() {
diff --git a/components/saved_tab_groups/saved_tab_group_model.cc b/components/saved_tab_groups/saved_tab_group_model.cc
index 4c27d8f1..d6bebe1 100644
--- a/components/saved_tab_groups/saved_tab_group_model.cc
+++ b/components/saved_tab_groups/saved_tab_group_model.cc
@@ -9,6 +9,8 @@
 #include <vector>
 
 #include "base/metrics/histogram_functions.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
 #include "base/observer_list.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/uuid.h"
@@ -21,6 +23,19 @@
 #include "components/tab_groups/tab_group_visual_data.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
+namespace {
+void RecordGroupDeletedMetric(const SavedTabGroup* removed_group) {
+  const base::TimeDelta duration_saved =
+      base::Time::Now() - removed_group->creation_time_windows_epoch_micros();
+
+  base::UmaHistogramCounts1M("TabGroups.SavedTabGroupLifespan",
+                             duration_saved.InMinutes());
+
+  base::RecordAction(
+      base::UserMetricsAction("TabGroups_SavedTabGroups_Deleted"));
+}
+}  // anonymous namespace
+
 SavedTabGroupModel::SavedTabGroupModel() = default;
 SavedTabGroupModel::~SavedTabGroupModel() = default;
 
@@ -83,6 +98,7 @@
   const int index = GetIndexOf(tab_group_id).value();
   base::Uuid removed_guid = Get(tab_group_id)->saved_guid();
   std::unique_ptr<SavedTabGroup> removed_group = RemoveImpl(index);
+
   UpdateGroupPositionsImpl();
   for (auto& observer : observers_) {
     observer.SavedTabGroupRemovedLocally(removed_group.get());
@@ -98,6 +114,7 @@
   const int index = GetIndexOf(id).value();
   base::Uuid removed_guid = Get(id)->saved_guid();
   std::unique_ptr<SavedTabGroup> removed_group = RemoveImpl(index);
+
   UpdateGroupPositionsImpl();
   for (auto& observer : observers_) {
     observer.SavedTabGroupRemovedLocally(removed_group.get());
@@ -369,15 +386,6 @@
   }
 }
 
-void SavedTabGroupModel::RecordGroupDeletedMetric(
-    const SavedTabGroup* removed_group) {
-  const base::TimeDelta duration_saved =
-      base::Time::Now() - removed_group->creation_time_windows_epoch_micros();
-
-  base::UmaHistogramCounts1M("TabGroups.SavedTabGroupLifespan",
-                             duration_saved.InMinutes());
-}
-
 void SavedTabGroupModel::UpdateGroupPositionsImpl() {
   for (size_t i = 0; i < saved_tab_groups_.size(); ++i)
     saved_tab_groups_[i].SetPosition(i);
@@ -473,6 +481,9 @@
   for (auto& observer : observers_) {
     observer.SavedTabGroupUpdatedLocally(saved_group.saved_guid());
   }
+
+  base::RecordAction(
+      base::UserMetricsAction("TabGroups_SavedTabGroups_Closed"));
 }
 
 void SavedTabGroupModel::OnGroupOpenedInTabStrip(
diff --git a/components/saved_tab_groups/saved_tab_group_model.h b/components/saved_tab_groups/saved_tab_group_model.h
index f27f4be..edd5620a 100644
--- a/components/saved_tab_groups/saved_tab_group_model.h
+++ b/components/saved_tab_groups/saved_tab_group_model.h
@@ -143,8 +143,6 @@
   void RemoveObserver(SavedTabGroupModelObserver* observer);
 
  private:
-  void RecordGroupDeletedMetric(const SavedTabGroup* removed_group);
-
   // Updates all group positions to match the index they are currently stored
   // at.
   void UpdateGroupPositionsImpl();
diff --git a/components/tab_groups/tab_group_visual_data.h b/components/tab_groups/tab_group_visual_data.h
index 5f79aa27..e042003 100644
--- a/components/tab_groups/tab_group_visual_data.h
+++ b/components/tab_groups/tab_group_visual_data.h
@@ -44,6 +44,8 @@
     return !(*this == other);
   }
 
+  void SetTitle(const std::u16string& title) { title_ = title; }
+
  private:
   std::u16string title_;
   tab_groups::TabGroupColorId color_;
diff --git a/components/webdata_services/web_data_service_wrapper.cc b/components/webdata_services/web_data_service_wrapper.cc
index 282a621..9961850d 100644
--- a/components/webdata_services/web_data_service_wrapper.cc
+++ b/components/webdata_services/web_data_service_wrapper.cc
@@ -13,7 +13,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "base/task/thread_pool.h"
 #include "build/build_config.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "components/autofill/core/browser/webdata/autocomplete_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autofill_profile_sync_bridge.h"
 #include "components/autofill/core/browser/webdata/autofill_table.h"
@@ -122,13 +121,6 @@
 #endif
   profile_database_->LoadDatabase();
 
-  // Ensure the CountryNames instance has the locale set. It is used in
-  // the autofill profile bridge, but putting it into the bridge directly
-  // creates a data race with PDM, where the locale is set as well. This is
-  // a tmp solution until the bug below is resolved.
-  // TODO(1430250): Find a unified place for setting the locale
-  autofill::CountryNames::SetLocaleString(application_locale);
-
   profile_autofill_web_data_ =
       base::MakeRefCounted<autofill::AutofillWebDataService>(
           profile_database_, ui_task_runner, db_task_runner);
diff --git a/content/browser/attribution_reporting/attribution_config.cc b/content/browser/attribution_reporting/attribution_config.cc
index a9b1af6..556ebd7 100644
--- a/content/browser/attribution_reporting/attribution_config.cc
+++ b/content/browser/attribution_reporting/attribution_config.cc
@@ -115,6 +115,10 @@
     return false;
   }
 
+  if (max_aggregatable_reports_per_source <= 0) {
+    return false;
+  }
+
   return true;
 }
 
diff --git a/content/browser/attribution_reporting/attribution_config.h b/content/browser/attribution_reporting/attribution_config.h
index f21245de..af63ac7 100644
--- a/content/browser/attribution_reporting/attribution_config.h
+++ b/content/browser/attribution_reporting/attribution_config.h
@@ -108,6 +108,8 @@
     double null_reports_rate_include_source_registration_time = .008;
     double null_reports_rate_exclude_source_registration_time = .05;
 
+    int max_aggregatable_reports_per_source = 20;
+
     // When adding new members, the corresponding `Validate()` definition and
     // `operator==()` definition in `attribution_interop_parser_unittest.cc`
     // should also be updated.
diff --git a/content/browser/attribution_reporting/attribution_debug_report.cc b/content/browser/attribution_reporting/attribution_debug_report.cc
index 25abc04..ad685732 100644
--- a/content/browser/attribution_reporting/attribution_debug_report.cc
+++ b/content/browser/attribution_reporting/attribution_debug_report.cc
@@ -57,6 +57,8 @@
   kTriggerAggregateInsufficientBudget,
   kTriggerAggregateStorageLimit,
   kTriggerAggregateReportWindowPassed,
+  // TODO(crbug.com/1442939): Add an interop test for this.
+  kTriggerAggregateExcessiveReports,
   kTriggerUnknownError,
 };
 
@@ -182,6 +184,10 @@
       return DataTypeIfCookieSet(
           DebugDataType::kTriggerAggregateReportWindowPassed,
           is_debug_cookie_set);
+    case AggregatableResult::kExcessiveReports:
+      return DataTypeIfCookieSet(
+          DebugDataType::kTriggerAggregateExcessiveReports,
+          is_debug_cookie_set);
   }
 }
 
@@ -229,6 +235,8 @@
       return "trigger-aggregate-storage-limit";
     case DebugDataType::kTriggerAggregateReportWindowPassed:
       return "trigger-aggregate-report-window-passed";
+    case DebugDataType::kTriggerAggregateExcessiveReports:
+      return "trigger-aggregate-excessive-reports";
     case DebugDataType::kTriggerUnknownError:
       return "trigger-unknown-error";
   }
@@ -292,6 +300,7 @@
     case DebugDataType::kTriggerAggregateInsufficientBudget:
     case DebugDataType::kTriggerAggregateStorageLimit:
     case DebugDataType::kTriggerAggregateReportWindowPassed:
+    case DebugDataType::kTriggerAggregateExcessiveReports:
     case DebugDataType::kTriggerUnknownError:
       NOTREACHED();
       return base::Value::Dict();
@@ -338,6 +347,9 @@
     case DebugDataType::kTriggerAggregateInsufficientBudget:
       SetLimit(data_body, result.limits().aggregatable_budget_per_source);
       break;
+    case DebugDataType::kTriggerAggregateExcessiveReports:
+      SetLimit(data_body, result.limits().max_aggregatable_reports_per_source);
+      break;
     case DebugDataType::kTriggerReportingOriginLimit:
       SetLimit(data_body,
                result.limits().rate_limits_max_attribution_reporting_origins);
diff --git a/content/browser/attribution_reporting/attribution_debug_report_unittest.cc b/content/browser/attribution_reporting/attribution_debug_report_unittest.cc
index 944cd2bc..6290bc16 100644
--- a/content/browser/attribution_reporting/attribution_debug_report_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_debug_report_unittest.cc
@@ -759,6 +759,20 @@
          },
          "type": "trigger-aggregate-insufficient-budget"
        }])json"},
+      {AggregatableResult::kExcessiveReports,
+       /*new_aggregatable_report=*/absl::nullopt,
+       CreateReportResult::Limits{.max_aggregatable_reports_per_source = 10},
+       /*source_debug_key=*/absl::nullopt,
+       /*trigger_debug_key=*/absl::nullopt,
+       R"json([{
+         "body": {
+           "attribution_destination": "https://conversion.test",
+           "limit": "10",
+           "source_event_id": "123",
+           "source_site": "https://impression.test"
+         },
+         "type": "trigger-aggregate-excessive-reports"
+       }])json"},
       {AggregatableResult::kNoMatchingSourceFilterData,
        /*new_aggregatable_report=*/absl::nullopt, CreateReportResult::Limits(),
        /*source_debug_key=*/absl::nullopt,
diff --git a/content/browser/attribution_reporting/attribution_internals.mojom b/content/browser/attribution_reporting/attribution_internals.mojom
index 3d4aae8..bd953f24 100644
--- a/content/browser/attribution_reporting/attribution_internals.mojom
+++ b/content/browser/attribution_reporting/attribution_internals.mojom
@@ -143,12 +143,12 @@
     kDeduplicated,
     kReportWindowPassed,
     kNotRegistered,
+    kExcessiveReports,
 
     // Event-level statuses:
     kLowPriority,
     kNoised,
     kNoMatchingConfigurations,
-    kExcessiveEventLevelReports,
 
     // Aggregatable statuses:
     kNoHistograms,
diff --git a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
index 6f6d19d..5c397c1 100644
--- a/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
+++ b/content/browser/attribution_reporting/attribution_internals_handler_impl.cc
@@ -476,7 +476,7 @@
     case EventLevelStatus::kNoMatchingConfigurations:
       return WebUITriggerStatus::kNoMatchingConfigurations;
     case EventLevelStatus::kExcessiveReports:
-      return WebUITriggerStatus::kExcessiveEventLevelReports;
+      return WebUITriggerStatus::kExcessiveReports;
     case EventLevelStatus::kReportWindowPassed:
       return WebUITriggerStatus::kReportWindowPassed;
     case EventLevelStatus::kNotRegistered:
@@ -512,6 +512,8 @@
       return WebUITriggerStatus::kDeduplicated;
     case AggregatableStatus::kReportWindowPassed:
       return WebUITriggerStatus::kReportWindowPassed;
+    case AggregatableStatus::kExcessiveReports:
+      return WebUITriggerStatus::kExcessiveReports;
   }
 }
 
diff --git a/content/browser/attribution_reporting/attribution_interop_parser_unittest.cc b/content/browser/attribution_reporting/attribution_interop_parser_unittest.cc
index 6e4b738..c74d2bf 100644
--- a/content/browser/attribution_reporting/attribution_interop_parser_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_interop_parser_unittest.cc
@@ -79,7 +79,8 @@
         config.aggregatable_budget_per_source, config.min_delay,
         config.delay_span,
         config.null_reports_rate_include_source_registration_time,
-        config.null_reports_rate_exclude_source_registration_time);
+        config.null_reports_rate_exclude_source_registration_time,
+        config.max_aggregatable_reports_per_source);
   };
   return tie(a) == tie(b);
 }
diff --git a/content/browser/attribution_reporting/attribution_manager_impl.cc b/content/browser/attribution_reporting/attribution_manager_impl.cc
index 0009171c..c1cbc0c9 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl.cc
@@ -198,11 +198,11 @@
                                 result.event_level_status());
   static_assert(
       AttributionTrigger::AggregatableResult::kMaxValue ==
-          AttributionTrigger::AggregatableResult::kReportWindowPassed,
-      "Bump version of Conversions.AggregatableReport.CreateReportStatus3 "
+          AttributionTrigger::AggregatableResult::kExcessiveReports,
+      "Bump version of Conversions.AggregatableReport.CreateReportStatus4 "
       "histogram.");
   base::UmaHistogramEnumeration(
-      "Conversions.AggregatableReport.CreateReportStatus3",
+      "Conversions.AggregatableReport.CreateReportStatus4",
       result.aggregatable_status());
 }
 
diff --git a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
index 52c19e8..8f48d4a3 100644
--- a/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_manager_impl_unittest.cc
@@ -1376,7 +1376,7 @@
       "Conversions.CreateReportStatus7",
       AttributionTrigger::EventLevelResult::kNoMatchingImpressions, 1);
   histograms.ExpectUniqueSample(
-      "Conversions.AggregatableReport.CreateReportStatus3",
+      "Conversions.AggregatableReport.CreateReportStatus4",
       AttributionTrigger::AggregatableResult::kNotRegistered, 1);
 }
 
@@ -1638,7 +1638,7 @@
       "Conversions.CreateReportStatus7",
       AttributionTrigger::EventLevelResult::kProhibitedByBrowserPolicy, 1);
   histograms.ExpectUniqueSample(
-      "Conversions.AggregatableReport.CreateReportStatus3",
+      "Conversions.AggregatableReport.CreateReportStatus4",
       AttributionTrigger::AggregatableResult::kProhibitedByBrowserPolicy, 1);
 }
 
diff --git a/content/browser/attribution_reporting/attribution_storage_delegate.cc b/content/browser/attribution_reporting/attribution_storage_delegate.cc
index f64a1df..7b3fa12 100644
--- a/content/browser/attribution_reporting/attribution_storage_delegate.cc
+++ b/content/browser/attribution_reporting/attribution_storage_delegate.cc
@@ -68,6 +68,11 @@
   return config_.aggregate_limit.aggregatable_budget_per_source;
 }
 
+int AttributionStorageDelegate::GetMaxAggregatableReportsPerSource() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return config_.aggregate_limit.max_aggregatable_reports_per_source;
+}
+
 uint64_t AttributionStorageDelegate::SanitizeTriggerData(
     uint64_t trigger_data,
     SourceType source_type) const {
diff --git a/content/browser/attribution_reporting/attribution_storage_delegate.h b/content/browser/attribution_reporting/attribution_storage_delegate.h
index 70e3666e3..b7645253 100644
--- a/content/browser/attribution_reporting/attribution_storage_delegate.h
+++ b/content/browser/attribution_reporting/attribution_storage_delegate.h
@@ -152,6 +152,8 @@
   // per source.
   int64_t GetAggregatableBudgetPerSource() const;
 
+  int GetMaxAggregatableReportsPerSource() const;
+
   // Sanitizes `trigger_data` according to the data limits for `source_type`.
   uint64_t SanitizeTriggerData(uint64_t trigger_data,
                                attribution_reporting::mojom::SourceType) const;
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.cc b/content/browser/attribution_reporting/attribution_storage_sql.cc
index 87b0470..a5505b7 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.cc
+++ b/content/browser/attribution_reporting/attribution_storage_sql.cc
@@ -1235,7 +1235,8 @@
         *new_aggregatable_report,
         source_to_attribute->source.aggregatable_budget_consumed(),
         source_to_attribute->num_aggregatable_reports, aggregatable_dedup_key,
-        limits.aggregatable_budget_per_source);
+        limits.aggregatable_budget_per_source,
+        limits.max_aggregatable_reports_per_source);
   }
 
   if (store_event_level_status == EventLevelResult::kInternalError ||
@@ -2870,13 +2871,19 @@
     int64_t aggregatable_budget_consumed,
     int num_aggregatable_reports,
     absl::optional<uint64_t> dedup_key,
-    absl::optional<int64_t>& aggregatable_budget_per_source) {
+    absl::optional<int64_t>& aggregatable_budget_per_source,
+    absl::optional<int>& max_aggregatable_reports_per_source) {
   const auto* aggregatable_attribution =
       absl::get_if<AttributionReport::AggregatableAttributionData>(
           &report.data());
   DCHECK(aggregatable_attribution);
 
-  // TODO(csharrison): Fail here if reports are too high.
+  if (num_aggregatable_reports >=
+      delegate_->GetMaxAggregatableReportsPerSource()) {
+    max_aggregatable_reports_per_source =
+        delegate_->GetMaxAggregatableReportsPerSource();
+    return AggregatableResult::kExcessiveReports;
+  }
 
   switch (AggregatableAttributionAllowedForBudgetLimit(
       *aggregatable_attribution, aggregatable_budget_consumed)) {
diff --git a/content/browser/attribution_reporting/attribution_storage_sql.h b/content/browser/attribution_reporting/attribution_storage_sql.h
index dae3c01..c0c405a 100644
--- a/content/browser/attribution_reporting/attribution_storage_sql.h
+++ b/content/browser/attribution_reporting/attribution_storage_sql.h
@@ -318,7 +318,8 @@
       int64_t aggregatable_budget_consumed,
       int num_aggregatable_reports,
       absl::optional<uint64_t> dedup_key,
-      absl::optional<int64_t>& aggregatable_budget_per_source)
+      absl::optional<int64_t>& aggregatable_budget_per_source,
+      absl::optional<int>& max_aggregatable_reports_per_source)
       VALID_CONTEXT_REQUIRED(sequence_checker_);
 
   [[nodiscard]] bool StoreAttributionReport(AttributionReport& report)
diff --git a/content/browser/attribution_reporting/attribution_storage_unittest.cc b/content/browser/attribution_reporting/attribution_storage_unittest.cc
index 7504b8c..87df3151 100644
--- a/content/browser/attribution_reporting/attribution_storage_unittest.cc
+++ b/content/browser/attribution_reporting/attribution_storage_unittest.cc
@@ -3583,4 +3583,16 @@
   delegate()->set_null_aggregatable_reports({});
 }
 
+TEST_F(AttributionStorageTest, MaximumAggregatableReportsPerSource) {
+  auto source = TestAggregatableSourceProvider().GetBuilder().Build();
+  storage()->StoreSource(source);
+  AttributionTrigger trigger = DefaultAggregatableTriggerBuilder().Build();
+  for (int i = 0; i < 20; i++) {
+    EXPECT_EQ(AttributionTrigger::AggregatableResult::kSuccess,
+              MaybeCreateAndStoreAggregatableReport(trigger));
+  }
+  EXPECT_EQ(AttributionTrigger::AggregatableResult::kExcessiveReports,
+            MaybeCreateAndStoreAggregatableReport(trigger));
+}
+
 }  // namespace content
diff --git a/content/browser/attribution_reporting/attribution_test_utils.cc b/content/browser/attribution_reporting/attribution_test_utils.cc
index be4bb8f..1161ff3 100644
--- a/content/browser/attribution_reporting/attribution_test_utils.cc
+++ b/content/browser/attribution_reporting/attribution_test_utils.cc
@@ -714,6 +714,8 @@
       return out << "deduplicated";
     case AttributionTrigger::AggregatableResult::kReportWindowPassed:
       return out << "reportWindowPassed";
+    case AttributionTrigger::AggregatableResult::kExcessiveReports:
+      return out << "excessiveReports";
   }
 }
 
diff --git a/content/browser/attribution_reporting/attribution_trigger.h b/content/browser/attribution_reporting/attribution_trigger.h
index b6ddac6..b57f68d8 100644
--- a/content/browser/attribution_reporting/attribution_trigger.h
+++ b/content/browser/attribution_reporting/attribution_trigger.h
@@ -64,7 +64,8 @@
     kProhibitedByBrowserPolicy = 10,
     kDeduplicated = 11,
     kReportWindowPassed = 12,
-    kMaxValue = kReportWindowPassed,
+    kExcessiveReports = 13,
+    kMaxValue = kExcessiveReports,
   };
 
   AttributionTrigger(attribution_reporting::SuitableOrigin reporting_origin,
diff --git a/content/browser/attribution_reporting/create_report_result.cc b/content/browser/attribution_reporting/create_report_result.cc
index eb674690..f87c67b 100644
--- a/content/browser/attribution_reporting/create_report_result.cc
+++ b/content/browser/attribution_reporting/create_report_result.cc
@@ -85,6 +85,9 @@
   DCHECK_EQ(limits.aggregatable_budget_per_source.has_value(),
             aggregatable_status_ == AggregatableResult::kInsufficientBudget);
 
+  DCHECK_EQ(limits.max_aggregatable_reports_per_source.has_value(),
+            aggregatable_status_ == AggregatableResult::kExcessiveReports);
+
   DCHECK_EQ(
       limits.rate_limits_max_attribution_reporting_origins.has_value(),
       event_level_status_ == EventLevelResult::kExcessiveReportingOrigins ||
diff --git a/content/browser/attribution_reporting/create_report_result.h b/content/browser/attribution_reporting/create_report_result.h
index fa7f67b..f6cd3e1d 100644
--- a/content/browser/attribution_reporting/create_report_result.h
+++ b/content/browser/attribution_reporting/create_report_result.h
@@ -37,6 +37,10 @@
     // `absl::nullopt` unless `aggregatable_status_` is
     // `kNoCapacityForConversionDestination`.
     absl::optional<int> max_aggregatable_reports_per_destination;
+
+    // `absl::nullopt` unless `aggregatable_status_` is
+    // `kExcessiveReports`..
+    absl::optional<int> max_aggregatable_reports_per_source;
   };
 
   CreateReportResult(
diff --git a/content/browser/devtools/devtools_background_services_context_impl.cc b/content/browser/devtools/devtools_background_services_context_impl.cc
index 1b13960..de97d22d 100644
--- a/content/browser/devtools/devtools_background_services_context_impl.cc
+++ b/content/browser/devtools/devtools_background_services_context_impl.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 
+#include "base/check_deref.h"
 #include "base/observer_list.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/time/time.h"
@@ -56,13 +57,13 @@
 DevToolsBackgroundServicesContextImpl::DevToolsBackgroundServicesContextImpl(
     BrowserContext* browser_context,
     scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
-    : browser_context_(browser_context),
+    : browser_context_(CHECK_DEREF(browser_context)),
       service_worker_context_(std::move(service_worker_context)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
   auto expiration_times =
       GetContentClient()->browser()->GetDevToolsBackgroundServiceExpirations(
-          browser_context_);
+          &*browser_context_);
 
   for (const auto& expiration_time : expiration_times) {
     DCHECK(devtools::proto::BackgroundService_IsValid(expiration_time.first));
@@ -98,7 +99,7 @@
   expiration_times_[service] = expiration_time;
 
   GetContentClient()->browser()->UpdateDevToolsBackgroundServiceExpiration(
-      browser_context_, service, expiration_time);
+      &*browser_context_, service, expiration_time);
 
   for (EventObserver& observer : observers_)
     observer.OnRecordingStateChanged(/* should_record= */ true, service);
@@ -110,7 +111,7 @@
 
   expiration_times_[service] = base::Time();
   GetContentClient()->browser()->UpdateDevToolsBackgroundServiceExpiration(
-      browser_context_, service, base::Time());
+      &*browser_context_, service, base::Time());
 
   for (EventObserver& observer : observers_)
     observer.OnRecordingStateChanged(/* should_record= */ false, service);
diff --git a/content/browser/devtools/devtools_background_services_context_impl.h b/content/browser/devtools/devtools_background_services_context_impl.h
index 0d7b526..d35278c 100644
--- a/content/browser/devtools/devtools_background_services_context_impl.h
+++ b/content/browser/devtools/devtools_background_services_context_impl.h
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "base/functional/callback_forward.h"
+#include "base/memory/raw_ref.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
@@ -116,7 +117,7 @@
 
   void OnRecordingTimeExpired(devtools::proto::BackgroundService service);
 
-  BrowserContext* browser_context_;
+  const raw_ref<BrowserContext> browser_context_;
   scoped_refptr<ServiceWorkerContextWrapper> service_worker_context_;
 
   // Maps from the background service to the time up until the events can be
diff --git a/content/browser/devtools/protocol/io_handler.h b/content/browser/devtools/protocol/io_handler.h
index fe1784d1..59aba1a 100644
--- a/content/browser/devtools/protocol/io_handler.h
+++ b/content/browser/devtools/protocol/io_handler.h
@@ -5,6 +5,7 @@
 #ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_IO_HANDLER_H_
 #define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_IO_HANDLER_H_
 
+#include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/devtools/protocol/devtools_domain_handler.h"
 #include "content/browser/devtools/protocol/io.h"
@@ -47,7 +48,7 @@
 
   std::unique_ptr<IO::Frontend> frontend_;
   DevToolsIOContext* io_context_;
-  BrowserContext* browser_context_;
+  raw_ptr<BrowserContext> browser_context_;
   StoragePartition* storage_partition_;
   base::WeakPtrFactory<IOHandler> weak_factory_{this};
 };
diff --git a/content/browser/devtools/protocol/network_handler.h b/content/browser/devtools/protocol/network_handler.h
index f966a2d..65408a0c 100644
--- a/content/browser/devtools/protocol/network_handler.h
+++ b/content/browser/devtools/protocol/network_handler.h
@@ -10,6 +10,7 @@
 
 #include "base/containers/flat_set.h"
 #include "base/containers/unique_ptr_adapters.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/unguessable_token.h"
 #include "content/browser/devtools/protocol/devtools_domain_handler.h"
@@ -339,7 +340,7 @@
   DevToolsIOContext* const io_context_;
 
   std::unique_ptr<Network::Frontend> frontend_;
-  BrowserContext* browser_context_;
+  raw_ptr<BrowserContext> browser_context_;
   StoragePartition* storage_partition_;
   RenderFrameHostImpl* host_;
   bool enabled_;
diff --git a/content/browser/devtools/protocol/service_worker_handler.h b/content/browser/devtools/protocol/service_worker_handler.h
index 8632a9d..493cb19 100644
--- a/content/browser/devtools/protocol/service_worker_handler.h
+++ b/content/browser/devtools/protocol/service_worker_handler.h
@@ -9,6 +9,7 @@
 
 #include <set>
 
+#include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "content/browser/devtools/protocol/devtools_domain_handler.h"
 #include "content/browser/devtools/protocol/service_worker.h"
@@ -80,7 +81,7 @@
   std::unique_ptr<ServiceWorker::Frontend> frontend_;
   bool enabled_;
   scoped_refptr<ServiceWorkerContextWatcher> context_watcher_;
-  BrowserContext* browser_context_;
+  raw_ptr<BrowserContext> browser_context_;
   StoragePartitionImpl* storage_partition_;
 
   base::WeakPtrFactory<ServiceWorkerHandler> weak_factory_{this};
diff --git a/content/browser/resources/attribution_reporting/attribution_internals.ts b/content/browser/resources/attribution_reporting/attribution_internals.ts
index 8990b51..0e03c8e 100644
--- a/content/browser/resources/attribution_reporting/attribution_internals.ts
+++ b/content/browser/resources/attribution_reporting/attribution_internals.ts
@@ -1017,8 +1017,8 @@
       return 'Failure: Prohibited by browser policy';
     case WebUITrigger_Status.kNoMatchingConfigurations:
       return 'Rejected: no matching event-level configurations';
-    case WebUITrigger_Status.kExcessiveEventLevelReports:
-      return 'Failure: Excessive event-level reports';
+    case WebUITrigger_Status.kExcessiveReports:
+      return 'Failure: Excessive reports';
     default:
       assertNotReached();
   }
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index be41409..e4c3a49 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -6346,6 +6346,33 @@
   EXPECT_EQ(AuthenticatorMakeCredential().status, AuthenticatorStatus::SUCCESS);
 }
 
+TEST_F(PINAuthenticatorImplTest, AppIdExcludeExtensionWithPinRequiredError) {
+  // Some alwaysUv authenticators apply the alwaysUv logic even when up=false.
+  // That causes them to return `kCtap2ErrPinRequired` to appIdExclude probes
+  // which broke makeCredential at one point. See crbug.com/1443039.
+  NavigateAndCommit(GURL(kTestOrigin1));
+
+  device::VirtualCtap2Device::Config config;
+  config.always_uv = true;
+  config.always_uv_for_up_false = true;
+  config.pin_support = true;
+  config.pin_uv_auth_token_support = true;
+  config.ctap2_versions = {device::Ctap2Version::kCtap2_1};
+  virtual_device_factory_->SetCtap2Config(config);
+
+  test_client_.expected = {{PINReason::kSet, kTestPIN16}};
+
+  PublicKeyCredentialCreationOptionsPtr options =
+      GetTestPublicKeyCredentialCreationOptions();
+  options->authenticator_selection->user_verification_requirement =
+      device::UserVerificationRequirement::kRequired;
+  options->appid_exclude = kTestOrigin1;
+  options->exclude_credentials = GetTestCredentials();
+
+  EXPECT_EQ(AuthenticatorMakeCredential(std::move(options)).status,
+            AuthenticatorStatus::SUCCESS);
+}
+
 class InternalUVAuthenticatorImplTest : public UVAuthenticatorImplTest {
  public:
   struct TestCase {
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index 180cfe64..7d4f448 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -82,6 +82,7 @@
     "browser_main_runner.h",
     "browser_message_filter.cc",
     "browser_message_filter.h",
+    "browser_or_resource_context.cc",
     "browser_or_resource_context.h",
     "browser_plugin_guest_delegate.cc",
     "browser_plugin_guest_delegate.h",
diff --git a/content/public/browser/browser_or_resource_context.cc b/content/public/browser/browser_or_resource_context.cc
new file mode 100644
index 0000000..1ab5b126
--- /dev/null
+++ b/content/public/browser/browser_or_resource_context.cc
@@ -0,0 +1,33 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/public/browser/browser_or_resource_context.h"
+
+#include "base/check_deref.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/resource_context.h"
+
+namespace content {
+
+BrowserOrResourceContext::BrowserOrResourceContext() = default;
+
+BrowserOrResourceContext::BrowserOrResourceContext(
+    BrowserContext* browser_context)
+    : storage_(raw_ref(CHECK_DEREF(browser_context))) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+}
+
+BrowserOrResourceContext::BrowserOrResourceContext(
+    ResourceContext* resource_context)
+    : storage_(raw_ref(CHECK_DEREF(resource_context))) {
+  DCHECK_CURRENTLY_ON(BrowserThread::IO);
+}
+
+BrowserOrResourceContext::~BrowserOrResourceContext() = default;
+BrowserOrResourceContext::BrowserOrResourceContext(
+    const BrowserOrResourceContext&) = default;
+BrowserOrResourceContext& BrowserOrResourceContext::operator=(
+    const BrowserOrResourceContext&) = default;
+
+}  // namespace content
diff --git a/content/public/browser/browser_or_resource_context.h b/content/public/browser/browser_or_resource_context.h
index b451074..9cf4618 100644
--- a/content/public/browser/browser_or_resource_context.h
+++ b/content/public/browser/browser_or_resource_context.h
@@ -6,99 +6,69 @@
 #define CONTENT_PUBLIC_BROWSER_BROWSER_OR_RESOURCE_CONTEXT_H_
 
 #include <cstddef>
-#include <type_traits>
 
-#include "base/check_op.h"
-#include "base/memory/raw_ptr_exclusion.h"
+#include "base/memory/raw_ref.h"
+#include "content/common/content_export.h"
 #include "content/public/browser/browser_thread.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
 namespace content {
 
 class BrowserContext;
 class ResourceContext;
 
-// A class holding either a BrowserContext* or a ResourceContext*.
-// This class should hold a BrowserContext* when constructed on the UI thread
-// and a ResourceContext* when constructed on the IO thread. This object must
-// only be accessed on the thread it was constructed and does not allow
-// converting between the two pointer types.
-class BrowserOrResourceContext final {
+// A class holding either BrowserContext*, a ResourceContext*, or nothing. This
+// class should hold a BrowserContext* when constructed on the UI thread and a
+// ResourceContext* when constructed on the IO thread. This object must only be
+// accessed on the thread it was constructed and does not allow converting
+// between the two pointer types.
+class CONTENT_EXPORT BrowserOrResourceContext final {
  public:
-  BrowserOrResourceContext() {
-    union_.browser_context_ = nullptr;
-    flavour_ = kNullFlavour;
-  }
+  BrowserOrResourceContext();
+  ~BrowserOrResourceContext();
 
-  // BrowserOrResourceContext is implicitly constructible from either
-  // BrowserContext* or ResourceContext*.  Neither of the constructor arguments
-  // can be null (enforced by DCHECKs and in some cases at compile time).
-  explicit BrowserOrResourceContext(BrowserContext* browser_context) {
-    DCHECK(browser_context);
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    union_.browser_context_ = browser_context;
-    flavour_ = kBrowserContextFlavour;
-  }
+  BrowserOrResourceContext(const BrowserOrResourceContext&);
+  BrowserOrResourceContext& operator=(const BrowserOrResourceContext&);
 
-  explicit BrowserOrResourceContext(ResourceContext* resource_context) {
-    DCHECK(resource_context);
-    DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    union_.resource_context_ = resource_context;
-    flavour_ = kResourceContextFlavour;
-  }
+  // BrowserOrResourceContext is constructible from either BrowserContext* or
+  // ResourceContext*.
+  // TODO(dcheng): Change this to take a ref.
+  explicit BrowserOrResourceContext(BrowserContext* browser_context);
+
+  // TODO(dcheng): Change this to take a ref.
+  explicit BrowserOrResourceContext(ResourceContext* resource_context);
+
   BrowserOrResourceContext(std::nullptr_t) = delete;
 
-  // BrowserOrResourceContext has a trivial, default destructor.
-  ~BrowserOrResourceContext() = default;
-
-  // BrowserOrResourceContext is trivially copyable.
-  BrowserOrResourceContext(const BrowserOrResourceContext& other) = default;
-  BrowserOrResourceContext& operator=(const BrowserOrResourceContext& other) =
-      default;
-
+  // Returns true if `this` is not null.
   explicit operator bool() const {
-    return (union_.resource_context_ != nullptr &&
-            union_.browser_context_ != nullptr);
+    return !absl::holds_alternative<absl::monostate>(storage_);
   }
 
-  // To be called only on the UI thread.  In DCHECK-enabled builds will verify
-  // that this object has kBrowserContextFlavour (implying that the returned
-  // BrowserContext* is valid and non-null.
+  // To be called only on the UI thread. Will CHECK() if `this` does not hold a
+  // `BrowserContext*`.
+  // TODO(dcheng): Change this to return a ref.
   BrowserContext* ToBrowserContext() const {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    CHECK_EQ(kBrowserContextFlavour, flavour_);
-    return union_.browser_context_;
+    return &absl::get<raw_ref<BrowserContext>>(storage_).get();
   }
 
-  // To be called only on the IO thread.  In DCHECK-enabled builds will verify
-  // that this object has kResourceContextFlavour (implying that the returned
-  // ResourceContext* is valid and non-null.
+  // To be called only on the UI thread. Will CHECK() if `this` does not hold a
+  // `ResourceContext*`.
+  // TODO(dcheng): Change this to return a ref.
   ResourceContext* ToResourceContext() const {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
-    CHECK_EQ(kResourceContextFlavour, flavour_);
-    return union_.resource_context_;
+    return &absl::get<raw_ref<ResourceContext>>(storage_).get();
   }
 
  private:
-  union Union {
-    // This field is not a raw_ptr<> because it was filtered by the rewriter
-    // for: #union
-    RAW_PTR_EXCLUSION BrowserContext* browser_context_;
-    // This field is not a raw_ptr<> because it was filtered by the rewriter
-    // for: #union
-    RAW_PTR_EXCLUSION ResourceContext* resource_context_;
-  } union_;
-
-  enum Flavour {
-    kNullFlavour,
-    kBrowserContextFlavour,
-    kResourceContextFlavour,
-  } flavour_;
+  // `absl::monostate` corresponds to the null state.
+  absl::variant<absl::monostate,
+                raw_ref<BrowserContext>,
+                raw_ref<ResourceContext>>
+      storage_;
 };
 
-static_assert(
-    std::is_trivially_copyable<BrowserOrResourceContext>::value,
-    "BrowserOrResourceContext should be trivially copyable in release builds.");
-
 }  // namespace content
 
 #endif  // CONTENT_PUBLIC_BROWSER_BROWSER_OR_RESOURCE_CONTEXT_H_
diff --git a/device/fido/features.cc b/device/fido/features.cc
index 849e9e5..8a79497 100644
--- a/device/fido/features.cc
+++ b/device/fido/features.cc
@@ -65,4 +65,9 @@
              "WebAuthenticationAndroidCredMan",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Added in M115. Remove in or after M118.
+BASE_FEATURE(kWebAuthnPinRequiredMeansNotRecognized,
+             "WebAuthenticationPinRequiredMeansNotRecognized",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 }  // namespace device
diff --git a/device/fido/features.h b/device/fido/features.h
index 861c871a..0f94ac2 100644
--- a/device/fido/features.h
+++ b/device/fido/features.h
@@ -63,6 +63,10 @@
 COMPONENT_EXPORT(DEVICE_FIDO)
 BASE_DECLARE_FEATURE(kWebAuthnAndroidCredMan);
 
+// Count kCtap2ErrPinRequired as meaning not recognised.
+COMPONENT_EXPORT(DEVICE_FIDO)
+BASE_DECLARE_FEATURE(kWebAuthnPinRequiredMeansNotRecognized);
+
 }  // namespace device
 
 #endif  // DEVICE_FIDO_FEATURES_H_
diff --git a/device/fido/fido_device.cc b/device/fido/fido_device.cc
index 484e3b1..b8e4eb24 100644
--- a/device/fido/fido_device.cc
+++ b/device/fido/fido_device.cc
@@ -10,6 +10,7 @@
 #include "base/functional/bind.h"
 #include "components/device_event_log/device_event_log.h"
 #include "device/fido/device_response_converter.h"
+#include "device/fido/features.h"
 #include "device/fido/fido_constants.h"
 
 namespace device {
@@ -84,7 +85,12 @@
   return status == CtapDeviceResponseCode::kCtap2ErrInvalidCredential ||
          status == CtapDeviceResponseCode::kCtap2ErrNoCredentials ||
          status == CtapDeviceResponseCode::kCtap2ErrLimitExceeded ||
-         status == CtapDeviceResponseCode::kCtap2ErrRequestTooLarge;
+         status == CtapDeviceResponseCode::kCtap2ErrRequestTooLarge ||
+         // Some alwaysUv devices return this, even for up=false. See
+         // crbug.com/1443039.
+         (base::FeatureList::IsEnabled(
+              kWebAuthnPinRequiredMeansNotRecognized) &&
+          status == CtapDeviceResponseCode::kCtap2ErrPinRequired);
 }
 
 }  // namespace device
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc
index eadbf51..a46b5f8 100644
--- a/device/fido/virtual_ctap2_device.cc
+++ b/device/fido/virtual_ctap2_device.cc
@@ -987,16 +987,15 @@
           AuthenticatorSupportedOptions::ClientPinAvailability::
               kSupportedAndPinSet;
 
-  // (CTAP2.1) 5. "If the alwaysUv option ID is present and true and the "up"
-  // option is present and true then:"
-  if (options.always_uv && user_presence_required) {
+  // (CTAP2.1) 5. "If the alwaysUv option ID is present and true"
+  if (options.always_uv &&
+      (user_presence_required || config_.always_uv_for_up_false)) {
     // 5.1 "If the authenticator is not protected by some form of user
     // verification:"
     if (!can_do_uv) {
       // 5.1.1 "If the clientPin option ID is present: (clientPin is supported)"
-      if (options.client_pin_availability ==
-          AuthenticatorSupportedOptions::ClientPinAvailability::
-              kSupportedAndPinSet) {
+      if (options.client_pin_availability !=
+          AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported) {
         return CtapDeviceResponseCode::kCtap2ErrPinRequired;
       } else {
         return CtapDeviceResponseCode::kCtap2ErrOperationDenied;
diff --git a/device/fido/virtual_ctap2_device.h b/device/fido/virtual_ctap2_device.h
index c2c9c00..89f1ffd6 100644
--- a/device/fido/virtual_ctap2_device.h
+++ b/device/fido/virtual_ctap2_device.h
@@ -87,6 +87,10 @@
     // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#sctn-minpinlength-extension
     bool min_pin_length_extension_support = false;
     bool always_uv = false;
+    // always_uv_for_up_false applies the alwaysUv logic for getAssertion, even
+    // when up=false. This does't seem correct, per CTAP 2.2, but some
+    // authenticators do it.
+    bool always_uv_for_up_false = false;
     // The space available to store a large blob. In real authenticators this
     // may change depending on the number of resident credentials. We treat this
     // as a fixed size area for the large blob.
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index a60e42b..d190e8c 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -68,6 +68,7 @@
 #include "third_party/libyuv/include/libyuv/planar_functions.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
+#include "third_party/skia/include/core/SkGraphics.h"
 #include "third_party/skia/include/core/SkPromiseImageTexture.h"
 #include "third_party/skia/include/core/SkSurface.h"
 #include "third_party/skia/include/core/SkSurfaceProps.h"
@@ -2904,14 +2905,16 @@
   // Unlock all font handles. This needs to be deferred until
   // SkSurface::flush since that flushes batched Gr operations
   // in skia that access the glyph data.
-  // TODO(khushalsagar): We just unlocked a bunch of handles, do we need to
-  // give a call to skia to attempt to purge any unlocked handles?
   if (!font_manager_->Unlock(locked_handles_)) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glRasterCHROMIUM",
                        "Invalid font discardable handle.");
   }
   locked_handles_.clear();
 
+  // We just unlocked a bunch of handles. Give a call to skia to
+  // attempt to purge any unlocked handles.
+  SkGraphics::PurgePinnedFontCache();
+
   // We just flushed a tile's worth of GPU work from the SkSurface in
   // flush above. Yield to the Scheduler to allow pre-emption before
   // processing more commands.
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc
index b1c794f7..e3e8860 100644
--- a/gpu/ipc/service/gles2_command_buffer_stub.cc
+++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -431,6 +431,23 @@
     }
   }
 
+  if (IsWebGLContextType(init_params.attribs.context_type)) {
+    gl::GLDisplayEGL* display_egl = display->GetAs<gl::GLDisplayEGL>();
+    if (display_egl) {
+      UMA_HISTOGRAM_ENUMERATION("GPU.WebGLDisplayType",
+                                display_egl->GetDisplayType(),
+                                gl::DISPLAY_TYPE_MAX);
+
+      constexpr uint64_t kLargeCanvasNumPixels = 128 * 128;
+      uint64_t surface_area = surface_->GetSize().Area64();
+      if (surface_area >= kLargeCanvasNumPixels) {
+        UMA_HISTOGRAM_ENUMERATION("GPU.WebGLDisplayTypeLarge",
+                                  display_egl->GetDisplayType(),
+                                  gl::DISPLAY_TYPE_MAX);
+      }
+    }
+  }
+
   manager->delegate()->DidCreateContextSuccessfully();
   initialized_ = true;
   return gpu::ContextResult::kSuccess;
diff --git a/infra/config/generated/builders/try/android-nougat-x86-rel/properties.json b/infra/config/generated/builders/try/android-nougat-x86-rel/properties.json
index 60ba7d2..6a04db2c 100644
--- a/infra/config/generated/builders/try/android-nougat-x86-rel/properties.json
+++ b/infra/config/generated/builders/try/android-nougat-x86-rel/properties.json
@@ -56,9 +56,6 @@
     ],
     "use_java_coverage": true
   },
-  "$build/flakiness": {
-    "check_for_flakiness": true
-  },
   "$recipe_engine/resultdb/test_presentation": {
     "column_keys": [],
     "grouping_keys": [
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index 7bc5d74..9954e003 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -71887,7 +71887,7 @@
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builder:ios15-beta-simulator"
       dimensions: "cpu:x86-64"
-      dimensions: "os:Mac-12"
+      dimensions: "os:Mac-13"
       dimensions: "pool:luci.chromium.try"
       exe {
         cipd_package: "infra/chromium/bootstrapper/${platform}"
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 2cbbd81..1205861 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -316,7 +316,9 @@
             condition = builder_config.rts_condition.QUICK_RUN_ONLY,
         ),
     ),
-    check_for_flakiness = True,
+    # crbug/1434778 - disabling due to high flake rate. See crbug/1422481 for
+    # root cause.
+    # check_for_flakiness = True,
     compilator = "android-nougat-x86-rel-compilator",
     coverage_test_types = ["unit", "overall"],
     experiments = {
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index 26ff1d8..606901c1 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -519,6 +519,7 @@
 ios_builder(
     name = "ios15-beta-simulator",
     mirrors = ["ci/ios15-beta-simulator"],
+    os = os.MAC_13,
 )
 
 ios_builder(
diff --git a/ios/chrome/app/memory_monitor.mm b/ios/chrome/app/memory_monitor.mm
index 5bf4aa1..07e846f2 100644
--- a/ios/chrome/app/memory_monitor.mm
+++ b/ios/chrome/app/memory_monitor.mm
@@ -12,6 +12,8 @@
 #import "base/functional/bind.h"
 #import "base/location.h"
 #import "base/mac/foundation_util.h"
+#import "base/metrics/histogram_functions.h"
+#import "base/metrics/histogram_macros.h"
 #import "base/strings/sys_string_conversions.h"
 #import "base/system/sys_info.h"
 #import "base/task/thread_pool.h"
@@ -25,27 +27,136 @@
 #endif
 
 namespace {
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class IOSStorageCapacity {
+  kUnknown = 0,
+  k0GB = 1,
+  k16GB = 2,
+  k32GB = 3,
+  k64GB = 4,
+  k128GB = 5,
+  k256GB = 6,
+  k512GB = 7,
+  k1TB = 8,
+  k2TB = 9,
+  kMaxValue = k2TB,
+};
+
+// Returns the appropriate storage capacity enum value given the `capacity` in
+// GB. An exact match is not required and the halfway points between steps was
+// chosen as the separators to account for errors in the calculation.
+IOSStorageCapacity StorageCapacityEnumFromGB(int capacity) {
+  if (capacity == 0) {
+    return IOSStorageCapacity::k0GB;
+  }
+  if (capacity < 8) {
+    return IOSStorageCapacity::kUnknown;
+  }
+  if (capacity < 24) {
+    return IOSStorageCapacity::k16GB;
+  }
+  if (capacity < 48) {
+    return IOSStorageCapacity::k32GB;
+  }
+  if (capacity < 96) {
+    return IOSStorageCapacity::k64GB;
+  }
+  if (capacity < 192) {
+    return IOSStorageCapacity::k128GB;
+  }
+  if (capacity < 384) {
+    return IOSStorageCapacity::k256GB;
+  }
+  if (capacity < 768) {
+    return IOSStorageCapacity::k512GB;
+  }
+  if (capacity < 1500) {
+    return IOSStorageCapacity::k1TB;
+  }
+  if (capacity < 2500) {
+    return IOSStorageCapacity::k2TB;
+  }
+  return IOSStorageCapacity::kUnknown;
+}
+
 // Delay between each invocations of `UpdateMemoryValues`.
 constexpr base::TimeDelta kMemoryMonitorDelay = base::Seconds(30);
 
 // Checks the values of free RAM and free disk space and updates crash keys with
 // these values. Also updates available free disk space for PreviousSessionInfo.
 void UpdateMemoryValues() {
+  static bool first_update_since_launch = true;
+
   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
                                                 base::BlockingType::WILL_BLOCK);
   const int free_memory =
       static_cast<int>(base::SysInfo::AmountOfAvailablePhysicalMemory() / 1024);
 
   NSURL* fileURL = [[NSURL alloc] initFileURLWithPath:NSHomeDirectory()];
-  NSDictionary* results = [fileURL resourceValuesForKeys:@[
-    NSURLVolumeAvailableCapacityForImportantUsageKey
-  ]
-                                                   error:nil];
+  NSArray* keys = @[ NSURLVolumeAvailableCapacityForImportantUsageKey ];
+  if (first_update_since_launch) {
+    keys = @[
+      NSURLVolumeAvailableCapacityForImportantUsageKey,
+      NSURLVolumeAvailableCapacityForOpportunisticUsageKey,
+      NSURLVolumeAvailableCapacityKey,
+      NSURLVolumeTotalCapacityKey,
+    ];
+  }
+  NSDictionary* results = [fileURL resourceValuesForKeys:keys error:nil];
   int free_disk_space_kilobytes = -1;
   if (results) {
     NSNumber* available_bytes =
         results[NSURLVolumeAvailableCapacityForImportantUsageKey];
     free_disk_space_kilobytes = [available_bytes integerValue] / 1024;
+
+    if (first_update_since_launch) {
+      NSNumber* total_capacity_bytes = results[NSURLVolumeTotalCapacityKey];
+      int total_capacity_gigabytes =
+          [total_capacity_bytes integerValue] / 1000 / 1000 / 1000;
+      base::UmaHistogramEnumeration(
+          "IOS.SandboxMetrics.TotalCapacity",
+          StorageCapacityEnumFromGB(total_capacity_gigabytes));
+      if (total_capacity_gigabytes == 0) {
+        return;
+      }
+
+      UMA_HISTOGRAM_MEMORY_LARGE_MB(
+          "IOS.SandboxMetrics.CapacityForImportantUsage",
+          free_disk_space_kilobytes / 1024);
+      float important_usage_capacity_percentage =
+          [available_bytes floatValue] / [total_capacity_bytes floatValue];
+      UMA_HISTOGRAM_PERCENTAGE(
+          "IOS.SandboxMetrics.CapacityForImportantUsagePercentage",
+          static_cast<int>(100 * important_usage_capacity_percentage));
+
+      NSNumber* opportunistic_bytes =
+          results[NSURLVolumeAvailableCapacityForOpportunisticUsageKey];
+      int free_disk_space_opportunistic_megabytes =
+          [opportunistic_bytes integerValue] / 1024 / 1024;
+      UMA_HISTOGRAM_MEMORY_LARGE_MB(
+          "IOS.SandboxMetrics.CapacityForOpportunisticUsage",
+          free_disk_space_opportunistic_megabytes);
+      float opportunistic_usage_capacity_percentage =
+          [opportunistic_bytes floatValue] / [total_capacity_bytes floatValue];
+      UMA_HISTOGRAM_PERCENTAGE(
+          "IOS.SandboxMetrics.CapacityForOpportunisticUsagePercentage",
+          static_cast<int>(100 * opportunistic_usage_capacity_percentage));
+
+      NSNumber* available_capacity_bytes =
+          results[NSURLVolumeAvailableCapacityKey];
+      int available_capacity_megabytes =
+          [available_capacity_bytes integerValue] / 1024 / 1024;
+      UMA_HISTOGRAM_MEMORY_LARGE_MB("IOS.SandboxMetrics.AvailableCapacity",
+                                    available_capacity_megabytes);
+      float capacity_percentage = [available_capacity_bytes floatValue] /
+                                  [total_capacity_bytes floatValue];
+      UMA_HISTOGRAM_PERCENTAGE("IOS.SandboxMetrics.AvailableCapacityPercentage",
+                               static_cast<int>(100 * capacity_percentage));
+
+      first_update_since_launch = false;
+    }
   }
 
   // As a workaround to crbug.com/1247282, dispatch back to the main thread.
diff --git a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address+AutofillProfile_unittest.mm b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address+AutofillProfile_unittest.mm
index e47bcc7..d6169bf0 100644
--- a/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address+AutofillProfile_unittest.mm
+++ b/ios/chrome/browser/ui/autofill/manual_fill/manual_fill_address+AutofillProfile_unittest.mm
@@ -7,7 +7,6 @@
 #import "base/strings/sys_string_conversions.h"
 #import "base/strings/utf_string_conversions.h"
 #import "components/autofill/core/browser/data_model/autofill_profile.h"
-#import "components/autofill/core/browser/geo/country_names.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "testing/platform_test.h"
 #import "url/gurl.h"
@@ -47,8 +46,6 @@
   NSString* phoneNumber = @"6502345678";
   NSString* emailAddress = @"john@doe";
 
-  autofill::CountryNames::SetLocaleString("en-US");
-
   AutofillProfile* profile = new AutofillProfile();
   SetProfileFieldTypeValue(profile, autofill::NAME_FIRST, firstName);
   SetProfileFieldTypeValue(profile, autofill::NAME_MIDDLE, middleName);
@@ -98,8 +95,6 @@
   NSString* phoneNumber = @"6502345678";
   NSString* emailAddress = @"john@doe";
 
-  autofill::CountryNames::SetLocaleString("en-US");
-
   AutofillProfile* profile = new AutofillProfile();
   SetProfileFieldTypeValue(profile, autofill::NAME_FIRST, firstName);
   SetProfileFieldTypeValue(profile, autofill::NAME_MIDDLE_INITIAL,
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index 853cbc5..7b80cff 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-bbc31285da67d50752e9bd72bfb6c01758c6cd1b
\ No newline at end of file
+b78344250b79b80ba14afa82080861fe472558bb
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index c0c753d..9fbe3f40 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-08eff3d5e7a8471d561bbb4499aa821e89e22d41
\ No newline at end of file
+65c58546d6e92f7e047fc8f7aa7fbc7c77c97d95
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 8ece80c..a91871d 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-76276fab13f6fd16ab157a5a2f4a33512a016970
\ No newline at end of file
+1ffdb3c577b5097e0adecd156549f180c30bb38c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index 91d6740..58d3e0b 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-210b2e869ec1f3cdd212432c4286c1bc10dc305b
\ No newline at end of file
+af0b66459f238599c6e407d62b05770a0760f193
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index 07d3d84..3f005dc 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-3310c83d0e63ec9288c22a5faf3d980c42646897
\ No newline at end of file
+3dcec0d65f6085fb4d51ebb4395a0380087ff6fc
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 1e87b37..417f8ca 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-8aeec579b97f1e6ea25b55725e182f4ff469a77a
\ No newline at end of file
+01ed508cf6b382a6a6fd79f635c54332aaf0b9a4
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index be48f251..7a5b523 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-54f8f47e2cf8a729c13d69e966d72f7187473e3d
\ No newline at end of file
+337e6067c30f421e66451cb28830b9238fd6ddab
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 171194b..9bd4311 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-9e65ba6a320ab4b83793fe7d13fa14a544d4ee35
\ No newline at end of file
+04cb7678ee8bf29ffb6c578526d3ee768f26a4e3
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index 67e0e892..49b4acd 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-6fefe30ca2dfc836849bd08836617041771b76cf
\ No newline at end of file
+f29a6f8fc8e1a76ee7237544f90c4a23573f9526
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index 313f36e..bbfd79c 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-23689ab660f72c37eda6de75c45fe72a010019e8
\ No newline at end of file
+5c8a35bbceea1e7af92f2e314bf7185814c2cb53
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index fc68e179..2995f21 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-59e656c5f55142e30567fd4c371b3e35643cdbce
\ No newline at end of file
+26938b79f4235a640d743e635f1584bd5d78a881
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 27d745c..f21c623 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-1f138196a3df78e8567fa0f9981f61cb6017dbc7
\ No newline at end of file
+359736bb8b2d6ea701d9c59656e34496bb4137e8
\ No newline at end of file
diff --git a/ios/web_view/internal/autofill/cwv_autofill_profile_unittest.mm b/ios/web_view/internal/autofill/cwv_autofill_profile_unittest.mm
index 748e5d8..8d6866a 100644
--- a/ios/web_view/internal/autofill/cwv_autofill_profile_unittest.mm
+++ b/ios/web_view/internal/autofill/cwv_autofill_profile_unittest.mm
@@ -11,7 +11,6 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/autofill_type.h"
 #include "components/autofill/core/browser/data_model/autofill_profile.h"
-#include "components/autofill/core/browser/geo/country_names.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
@@ -31,7 +30,6 @@
     ui::ResourceBundle::InitSharedInstanceWithLocale(
         l10n_util::GetLocaleOverride(), /*delegate=*/nullptr,
         ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES);
-    autofill::CountryNames::SetLocaleString(l10n_util::GetLocaleOverride());
   }
 
   ~CWVAutofillProfileTest() override {
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 4cc47611..1eb891b2 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -673,24 +673,6 @@
 );
 #endif
 
-// Allow Global Media Controls in system tray of CrOS.
-BASE_FEATURE(kGlobalMediaControlsForChromeOS,
-             "GlobalMediaControlsForChromeOS",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-constexpr base::FeatureParam<kCrosGlobalMediaControlsPinOptions>::Option
-    kCrosGlobalMediaControlsParamOptions[] = {
-        {kCrosGlobalMediaControlsPinOptions::kPin, "default-pinned"},
-        {kCrosGlobalMediaControlsPinOptions::kNotPin, "default-unpinned"},
-        {kCrosGlobalMediaControlsPinOptions::kHeuristic, "heuristic"}};
-
-constexpr base::FeatureParam<kCrosGlobalMediaControlsPinOptions>
-    kCrosGlobalMediaControlsPinParam(
-        &kGlobalMediaControlsForChromeOS,
-        "CrosGlobalMediaControlsPinParam",
-        kCrosGlobalMediaControlsPinOptions::kHeuristic,
-        &kCrosGlobalMediaControlsParamOptions);
-
 // Show picture-in-picture button in Global Media Controls.
 BASE_FEATURE(kGlobalMediaControlsPictureInPicture,
              "GlobalMediaControlsPictureInPicture",
diff --git a/media/base/media_switches.h b/media/base/media_switches.h
index fd5a333..3c4e360 100644
--- a/media/base/media_switches.h
+++ b/media/base/media_switches.h
@@ -214,7 +214,6 @@
 #if !BUILDFLAG(IS_ANDROID)
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kMediaRemotingWithoutFullscreen);
 #endif
-MEDIA_EXPORT BASE_DECLARE_FEATURE(kGlobalMediaControlsForChromeOS);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kGlobalMediaControlsPictureInPicture);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kGlobalMediaControlsSeamlessTransfer);
 MEDIA_EXPORT BASE_DECLARE_FEATURE(kGlobalMediaControlsModernUI);
@@ -462,17 +461,6 @@
 MEDIA_EXPORT bool IsOutOfProcessVideoDecodingEnabled();
 #endif  // BUILDFLAG(ALLOW_OOP_VIDEO_DECODER)
 
-enum class kCrosGlobalMediaControlsPinOptions {
-  kPin,
-  kNotPin,
-  kHeuristic,
-};
-
-// Feature param used to force default pin/unpin for global media controls in
-// CrOS.
-MEDIA_EXPORT extern const base::FeatureParam<kCrosGlobalMediaControlsPinOptions>
-    kCrosGlobalMediaControlsPinParam;
-
 // Return bitmask of audio formats supported by EDID.
 MEDIA_EXPORT uint32_t GetPassthroughAudioFormats();
 
diff --git a/media/formats/mp4/writable_box_definitions.cc b/media/formats/mp4/writable_box_definitions.cc
index 5952eb0..c797c22 100644
--- a/media/formats/mp4/writable_box_definitions.cc
+++ b/media/formats/mp4/writable_box_definitions.cc
@@ -6,6 +6,9 @@
 
 namespace media::mp4::writable_boxes {
 
+Movie::Movie() = default;
+Movie::~Movie() = default;
+
 MovieExtends::MovieExtends() = default;
 MovieExtends::~MovieExtends() = default;
 
diff --git a/media/formats/mp4/writable_box_definitions.h b/media/formats/mp4/writable_box_definitions.h
index dea7ab5..0f6d9cb 100644
--- a/media/formats/mp4/writable_box_definitions.h
+++ b/media/formats/mp4/writable_box_definitions.h
@@ -10,24 +10,84 @@
 #include "base/time/time.h"
 #include "media/base/media_export.h"
 #include "media/formats/mp4/fourccs.h"
+#include "ui/gfx/geometry/size.h"
 
 namespace media::mp4::writable_boxes {
 
-// Box header without version.
-struct Box {
-  uint32_t total_size;
-  FourCC fourcc;
+enum class TrackHeaderFlags : uint16_t {
+  kTrackEnabled = 0x0001,
+  kTrackInMovie = 0x0002,
+  kTrackInPreview = 0x0004,
 };
 
+// Box header without version.
+struct MEDIA_EXPORT Box {};
+
 // Box header with version and flags.
-struct FullBox : Box {
+struct MEDIA_EXPORT FullBox : Box {
   // version 1 is 64 bits where applicable, 0 is 32 bits.
   uint8_t version;
   uint32_t flags : 24;
 };
 
+// Media sample table (`stsd`) box.
+struct MEDIA_EXPORT SampleDescription : FullBox {
+  uint32_t entry_count;
+  // TODO: add optional `avc1` or `mp4a` box.
+};
+
+// Media sample table (`stbl`) box.
+struct MEDIA_EXPORT SampleTable : Box {
+  SampleDescription sample_description;
+  // TODO: add `stsc`, `stts`, `stsz`, `stco` box.
+};
+
+// Media information (`minf`) box.
+struct MEDIA_EXPORT MediaInformation : Box {
+  SampleTable sample_table;
+  // TODO: add `vmhd`, `dinf` box.
+};
+
+// Media Handler (`hdlr`) box.
+struct MEDIA_EXPORT MediaHandler : FullBox {
+  mp4::FourCC handler_type;
+  std::string name;
+};
+
+// Media header (`mdhd`) box.
+struct MEDIA_EXPORT MediaHeader : FullBox {
+  base::Time creation_time;
+  base::Time modification_time;
+  uint32_t timescale;
+  base::TimeDelta duration;
+  std::string language;  // 3 letters code ISO-639-2/T language.
+};
+
+// Media (`mdia`) box.
+struct MEDIA_EXPORT Media : Box {
+  MediaHeader header;
+  MediaHandler handler;
+  MediaInformation information;
+};
+
+// Track header (`tkhd`) box.
+struct MEDIA_EXPORT TrackHeader : FullBox {
+  uint32_t track_id;
+  base::Time creation_time;
+  base::Time modification_time;
+  base::TimeDelta duration;
+  bool is_audio;
+  gfx::Size natural_size;
+};
+
+// Track (`trak`) box.
+struct MEDIA_EXPORT Track : Box {
+  TrackHeader header;
+  Media media;
+};
+
 // Track Extends (`trex`) box.
-struct TrackExtends : FullBox {
+struct MEDIA_EXPORT TrackExtends : FullBox {
   uint32_t track_id;
   uint32_t default_sample_description_index;
   base::TimeDelta default_sample_duration;
@@ -69,8 +129,11 @@
 };
 
 // Movie (`moov`) box.
-struct Movie : Box {
+struct MEDIA_EXPORT Movie : Box {
+  Movie();
+  ~Movie();
   MovieHeader header;
+  std::vector<Track> tracks;
   MovieExtends extends;
 };
 
diff --git a/media/gpu/h264_decoder.cc b/media/gpu/h264_decoder.cc
index e533d9f..75c14e5 100644
--- a/media/gpu/h264_decoder.cc
+++ b/media/gpu/h264_decoder.cc
@@ -253,13 +253,8 @@
         prev_pic_order_cnt_msb = prev_pic_order_cnt_lsb = 0;
       } else {
         if (prev_ref_has_memmgmnt5_) {
-          if (prev_ref_field_ != H264Picture::FIELD_BOTTOM) {
-            prev_pic_order_cnt_msb = 0;
-            prev_pic_order_cnt_lsb = prev_ref_top_field_order_cnt_;
-          } else {
-            prev_pic_order_cnt_msb = 0;
-            prev_pic_order_cnt_lsb = 0;
-          }
+          prev_pic_order_cnt_msb = 0;
+          prev_pic_order_cnt_lsb = prev_ref_top_field_order_cnt_;
         } else {
           prev_pic_order_cnt_msb = prev_ref_pic_order_cnt_msb_;
           prev_pic_order_cnt_lsb = prev_ref_pic_order_cnt_lsb_;
@@ -281,20 +276,12 @@
         pic->pic_order_cnt_msb = prev_pic_order_cnt_msb;
       }
 
-      if (pic->field != H264Picture::FIELD_BOTTOM) {
-        pic->top_field_order_cnt =
-            pic->pic_order_cnt_msb + pic->pic_order_cnt_lsb;
-      }
+      pic->top_field_order_cnt =
+          pic->pic_order_cnt_msb + pic->pic_order_cnt_lsb;
 
-      if (pic->field != H264Picture::FIELD_TOP) {
-        if (pic->field == H264Picture::FIELD_NONE) {
-          pic->bottom_field_order_cnt =
-              pic->top_field_order_cnt + pic->delta_pic_order_cnt_bottom;
-        } else {
-          pic->bottom_field_order_cnt =
-              pic->pic_order_cnt_msb + pic->pic_order_cnt_lsb;
-        }
-      }
+      pic->bottom_field_order_cnt =
+          pic->top_field_order_cnt + pic->delta_pic_order_cnt_bottom;
+
       break;
     }
 
@@ -342,20 +329,12 @@
       if (!pic->nal_ref_idc)
         expected_pic_order_cnt += sps->offset_for_non_ref_pic;
 
-      if (pic->field == H264Picture::FIELD_NONE) {
-        pic->top_field_order_cnt =
-            expected_pic_order_cnt + pic->delta_pic_order_cnt0;
-        pic->bottom_field_order_cnt = pic->top_field_order_cnt +
-                                      sps->offset_for_top_to_bottom_field +
-                                      pic->delta_pic_order_cnt1;
-      } else if (pic->field != H264Picture::FIELD_BOTTOM) {
-        pic->top_field_order_cnt =
-            expected_pic_order_cnt + pic->delta_pic_order_cnt0;
-      } else {
-        pic->bottom_field_order_cnt = expected_pic_order_cnt +
-                                      sps->offset_for_top_to_bottom_field +
-                                      pic->delta_pic_order_cnt0;
-      }
+      pic->top_field_order_cnt =
+          expected_pic_order_cnt + pic->delta_pic_order_cnt0;
+      pic->bottom_field_order_cnt = pic->top_field_order_cnt +
+                                    sps->offset_for_top_to_bottom_field +
+                                    pic->delta_pic_order_cnt1;
+
       break;
     }
 
@@ -380,14 +359,9 @@
         temp_pic_order_cnt = 2 * (pic->frame_num_offset + pic->frame_num);
       }
 
-      if (pic->field == H264Picture::FIELD_NONE) {
-        pic->top_field_order_cnt = temp_pic_order_cnt;
-        pic->bottom_field_order_cnt = temp_pic_order_cnt;
-      } else if (pic->field == H264Picture::FIELD_BOTTOM) {
-        pic->bottom_field_order_cnt = temp_pic_order_cnt;
-      } else {
-        pic->top_field_order_cnt = temp_pic_order_cnt;
-      }
+      pic->top_field_order_cnt = temp_pic_order_cnt;
+      pic->bottom_field_order_cnt = temp_pic_order_cnt;
+
       break;
     }
 
@@ -396,18 +370,8 @@
       return false;
   }
 
-  switch (pic->field) {
-    case H264Picture::FIELD_NONE:
-      pic->pic_order_cnt =
-          std::min(pic->top_field_order_cnt, pic->bottom_field_order_cnt);
-      break;
-    case H264Picture::FIELD_TOP:
-      pic->pic_order_cnt = pic->top_field_order_cnt;
-      break;
-    case H264Picture::FIELD_BOTTOM:
-      pic->pic_order_cnt = pic->bottom_field_order_cnt;
-      break;
-  }
+  pic->pic_order_cnt =
+      std::min(pic->top_field_order_cnt, pic->bottom_field_order_cnt);
 
   return true;
 }
@@ -1768,14 +1732,9 @@
   if (pic->idr)
     pic->idr_pic_id = slice_hdr.idr_pic_id;
 
-  if (slice_hdr.field_pic_flag) {
-    pic->field = slice_hdr.bottom_field_flag ? H264Picture::FIELD_BOTTOM
-                                             : H264Picture::FIELD_TOP;
-  } else {
+  if (!slice_hdr.field_pic_flag) {
     pic->field = H264Picture::FIELD_NONE;
-  }
-
-  if (pic->field != H264Picture::FIELD_NONE) {
+  } else {
     DVLOG(1) << "Interlaced video not supported.";
     return false;
   }
diff --git a/media/gpu/v4l2/legacy/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/legacy/v4l2_slice_video_decode_accelerator.cc
index 164ec84..c9d78985 100644
--- a/media/gpu/v4l2/legacy/v4l2_slice_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/legacy/v4l2_slice_video_decode_accelerator.cc
@@ -307,7 +307,7 @@
   input_format_fourcc_ =
       V4L2Device::VideoCodecProfileToV4L2PixFmt(video_profile_, true);
 
-  if (input_format_fourcc_ == V4L2_PIX_FMT_INVALID ||
+  if (!input_format_fourcc_ ||
       !device_->Open(V4L2Device::Type::kDecoder, input_format_fourcc_)) {
     VLOGF(1) << "Failed to open device for profile: " << config.profile
              << " fourcc: " << FourccToString(input_format_fourcc_);
diff --git a/media/gpu/v4l2/legacy/v4l2_video_decode_accelerator.cc b/media/gpu/v4l2/legacy/v4l2_video_decode_accelerator.cc
index e6ac7114..947e36f 100644
--- a/media/gpu/v4l2/legacy/v4l2_video_decode_accelerator.cc
+++ b/media/gpu/v4l2/legacy/v4l2_video_decode_accelerator.cc
@@ -316,7 +316,7 @@
   input_format_fourcc_ =
       V4L2Device::VideoCodecProfileToV4L2PixFmt(config.profile, false);
 
-  if (input_format_fourcc_ == V4L2_PIX_FMT_INVALID ||
+  if (!input_format_fourcc_ ||
       !device_->Open(V4L2Device::Type::kDecoder, input_format_fourcc_)) {
     VLOGF(1) << "Failed to open device for profile: " << config.profile
              << " fourcc: " << FourccToString(input_format_fourcc_);
diff --git a/media/gpu/v4l2/v4l2_device.cc b/media/gpu/v4l2/v4l2_device.cc
index e59918b57..73f2c94 100644
--- a/media/gpu/v4l2/v4l2_device.cc
+++ b/media/gpu/v4l2/v4l2_device.cc
@@ -1583,7 +1583,7 @@
       return V4L2_PIX_FMT_AV1;
   } else {
     DVLOGF(1) << "Unsupported profile: " << GetProfileName(profile);
-    return V4L2_PIX_FMT_INVALID;
+    return 0;
   }
 }
 
diff --git a/media/gpu/v4l2/v4l2_device.h b/media/gpu/v4l2/v4l2_device.h
index 1a82aba..a8b1bed 100644
--- a/media/gpu/v4l2/v4l2_device.h
+++ b/media/gpu/v4l2/v4l2_device.h
@@ -116,8 +116,6 @@
   v4l2_fourcc('Q', '1', '0', 'C') /* Qualcomm 10-bit compressed */
 #endif
 
-#define V4L2_PIX_FMT_INVALID v4l2_fourcc('0', '0', '0', '0')
-
 namespace gfx {
 struct NativePixmapPlane;
 }  // namespace gfx
@@ -676,8 +674,7 @@
     : public base::RefCountedThreadSafe<V4L2Device> {
  public:
   // Utility format conversion functions
-  // If there is no corresponding single- or multi-planar format, returns
-  // V4L2_PIX_FMT_INVALID.
+  // If there is no corresponding single- or multi-planar format, returns 0.
   static uint32_t VideoCodecProfileToV4L2PixFmt(VideoCodecProfile profile,
                                                 bool slice_based);
   // Calculates the largest plane's allocation size requested by a V4L2 device.
diff --git a/media/gpu/v4l2/v4l2_video_decoder.cc b/media/gpu/v4l2/v4l2_video_decoder.cc
index 6ba2cd9d..bd38a81 100644
--- a/media/gpu/v4l2/v4l2_video_decoder.cc
+++ b/media/gpu/v4l2/v4l2_video_decoder.cc
@@ -306,7 +306,8 @@
   for (const auto api : {kStateful, kStateless}) {
     const auto fourcc =
         V4L2Device::VideoCodecProfileToV4L2PixFmt(profile_, api);
-    if (fourcc == V4L2_PIX_FMT_INVALID ||
+    constexpr uint32_t kInvalidV4L2PixFmt = 0;
+    if (fourcc == kInvalidV4L2PixFmt ||
         !device_->Open(V4L2Device::Type::kDecoder, fourcc)) {
       continue;
     }
diff --git a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index aad7497..56f1c0e 100644
--- a/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -212,7 +212,7 @@
 
   output_format_fourcc_ =
       V4L2Device::VideoCodecProfileToV4L2PixFmt(config.output_profile, false);
-  if (output_format_fourcc_ == V4L2_PIX_FMT_INVALID) {
+  if (!output_format_fourcc_) {
     MEDIA_LOG(ERROR, media_log.get())
         << "invalid output_profile=" << GetProfileName(config.output_profile);
     return false;
diff --git a/media/muxers/BUILD.gn b/media/muxers/BUILD.gn
index 4449d4a..e7c97479 100644
--- a/media/muxers/BUILD.gn
+++ b/media/muxers/BUILD.gn
@@ -23,6 +23,8 @@
     "mp4_movie_box_writer.h",
     "mp4_muxer_context.cc",
     "mp4_muxer_context.h",
+    "mp4_type_conversion.cc",
+    "mp4_type_conversion.h",
     "muxer.cc",
     "muxer.h",
     "output_position_tracker.cc",
@@ -48,6 +50,7 @@
     "box_byte_stream_unittest.cc",
     "mp4_muxer_box_writer_unittest.cc",
     "mp4_muxer_context_unittest.cc",
+    "mp4_type_conversion_unittest.cc",
     "output_position_tracker_unittest.cc",
     "webm_muxer_unittest.cc",
   ]
diff --git a/media/muxers/box_byte_stream.cc b/media/muxers/box_byte_stream.cc
index d4007da..4ad5c38 100644
--- a/media/muxers/box_byte_stream.cc
+++ b/media/muxers/box_byte_stream.cc
@@ -25,10 +25,20 @@
   DCHECK(size_offsets_.empty());
 }
 
-void BoxByteStream::StartBox() {
+void BoxByteStream::StartBox(mp4::FourCC fourcc) {
   CHECK(!buffer_.empty());
   size_offsets_.push_back(position_);
   WriteU32(0);
+
+  WriteU32(fourcc);
+}
+
+void BoxByteStream::StartFullBox(mp4::FourCC fourcc,
+                                 uint32_t flags,
+                                 uint8_t version) {
+  StartBox(fourcc);
+  WriteU8(version);
+  WriteU24(flags);
 }
 
 void BoxByteStream::WriteU8(uint8_t value) {
@@ -72,6 +82,14 @@
   position_ += 8;
 }
 
+void BoxByteStream::WriteBytes(const void* buf, size_t len) {
+  CHECK(!buffer_.empty());
+  while (!writer_->WriteBytes(buf, len)) {
+    GrowWriter();
+  }
+  position_ += len;
+}
+
 std::vector<uint8_t> BoxByteStream::Flush() {
   CHECK(!buffer_.empty());
 
diff --git a/media/muxers/box_byte_stream.h b/media/muxers/box_byte_stream.h
index b24c83d..b5bb70f 100644
--- a/media/muxers/box_byte_stream.h
+++ b/media/muxers/box_byte_stream.h
@@ -9,6 +9,7 @@
 
 #include "base/big_endian.h"
 #include "media/base/media_export.h"
+#include "media/formats/mp4/fourccs.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 namespace media {
@@ -27,7 +28,12 @@
   // Writes a uint32_t placeholder value that `EndBox()` or `Flush()` will
   // fill in later.
   // Only works if the current position is the start of a new box.
-  void StartBox();
+  void StartBox(mp4::FourCC fourcc);
+  void StartFullBox(mp4::FourCC fourcc,
+                    uint32_t flags = 0,
+                    // Chromium MP4 Muxer supports 64 bits as a default, but the
+                    // individual box can override it as needed.
+                    uint8_t version = 1);
 
   // Writes primitives types in big endian format. If `value` can be larger than
   // the the type being written, methods will `CHECK()` that `value` fits in the
@@ -37,6 +43,7 @@
   void WriteU24(uint32_t value);
   void WriteU32(uint32_t value);
   void WriteU64(uint64_t value);
+  void WriteBytes(const void* buf, size_t len);
 
   // Ends a writing session. All pending placeholder values in `size_offsets_`
   // are filled in based on their distance from `position_`.
diff --git a/media/muxers/box_byte_stream_unittest.cc b/media/muxers/box_byte_stream_unittest.cc
index fdc08f2..2142ae0 100644
--- a/media/muxers/box_byte_stream_unittest.cc
+++ b/media/muxers/box_byte_stream_unittest.cc
@@ -19,6 +19,7 @@
   kType_24 = 3,
   kType_32 = 4,
   kType_64 = 5,
+  kType_Bytes = 6,
 };
 
 struct DataOrder {
@@ -30,16 +31,21 @@
   // Test basic Write APIs of the BoxByteStream.
   BoxByteStream box_byte_stream;
 
-  DataOrder test_data[] = {
-      {DataType::kType_8, 0x48},        {DataType::kPlaceHolder, 32},
-      {DataType::kType_16, 0x1617},     {DataType::kType_24, 0x242526},
-      {DataType::kType_32, 0x32333435}, {DataType::kType_64, 0x64646667686970},
-      {DataType::kPlaceHolder, 11},     {DataType::kType_8, 0x28},
-      {DataType::kType_16, 0x0},        {DataType::kType_32, 0x0}};
+  DataOrder test_data[] = {{DataType::kType_8, 0x48},
+                           {DataType::kPlaceHolder, 47},
+                           {DataType::kType_16, 0x1617},
+                           {DataType::kType_24, 0x242526},
+                           {DataType::kType_32, 0x32333435},
+                           {DataType::kType_64, 0x64646667686970},
+                           {DataType::kType_Bytes, 0x12345678901234},
+                           {DataType::kPlaceHolder, 15},
+                           {DataType::kType_8, 0x28},
+                           {DataType::kType_16, 0x0},
+                           {DataType::kType_32, 0x0}};
   for (auto& data : test_data) {
     switch (data.type) {
       case DataType::kPlaceHolder:
-        box_byte_stream.StartBox();
+        box_byte_stream.StartBox(mp4::FOURCC_MOOV);
         break;
       case DataType::kType_8:
         box_byte_stream.WriteU8(static_cast<uint8_t>(data.value));
@@ -56,6 +62,9 @@
       case DataType::kType_64:
         box_byte_stream.WriteU64(data.value);
         break;
+      case DataType::kType_Bytes:
+        box_byte_stream.WriteBytes(&data.value, 7);
+        break;
     }
   }
 
@@ -67,6 +76,8 @@
       case DataType::kPlaceHolder:
         EXPECT_TRUE(reader.ReadU32(reinterpret_cast<uint32_t*>(&ret_value)));
         EXPECT_EQ(data.value, ret_value);
+        EXPECT_TRUE(reader.ReadU32(reinterpret_cast<uint32_t*>(&ret_value)));
+        EXPECT_EQ(mp4::FOURCC_MOOV, ret_value);
         break;
       case DataType::kType_8:
         EXPECT_TRUE(reader.ReadU8(reinterpret_cast<uint8_t*>(&ret_value)));
@@ -88,6 +99,10 @@
         EXPECT_TRUE(reader.ReadU64(&ret_value));
         EXPECT_EQ(data.value, ret_value);
         break;
+      case DataType::kType_Bytes:
+        EXPECT_TRUE(reader.ReadBytes(&ret_value, 7));
+        EXPECT_EQ(data.value, ret_value);
+        break;
     }
   }
 }
@@ -96,11 +111,11 @@
   // Test GrowWriter feature.
   BoxByteStream box_byte_stream;
 
-  box_byte_stream.StartBox();
+  box_byte_stream.StartBox(mp4::FOURCC_MOOV);
   for (int i = 0; i < BoxByteStream::kDefaultBufferLimit; ++i) {
     box_byte_stream.WriteU8(0);
   }
-  box_byte_stream.StartBox();
+  box_byte_stream.StartBox(mp4::FOURCC_TRAK);
   box_byte_stream.WriteU16(0x1617);
   box_byte_stream.WriteU32(0);
   box_byte_stream.EndBox();
@@ -110,14 +125,18 @@
   base::BigEndianReader reader(written_data.data(), written_data.size());
 
   uint32_t expected_total_size =
-      4 + BoxByteStream::kDefaultBufferLimit + 4 + 2 + 4;
+      8 + BoxByteStream::kDefaultBufferLimit + 8 + 2 + 4;
   uint32_t value;
   reader.ReadU32(&value);
   EXPECT_EQ(expected_total_size, value);
+  reader.ReadU32(&value);
+  EXPECT_EQ(mp4::FOURCC_MOOV, value);
 
   reader.Skip(BoxByteStream::kDefaultBufferLimit);
   reader.ReadU32(&value);
-  EXPECT_EQ(10u, value);
+  EXPECT_EQ(14u, value);
+  reader.ReadU32(&value);
+  EXPECT_EQ(mp4::FOURCC_TRAK, value);
 
   uint16_t value16;
   reader.ReadU16(&value16);
@@ -133,17 +152,17 @@
     BoxByteStream box_byte_stream;
 
     // <parent>
-    box_byte_stream.StartBox();
+    box_byte_stream.StartBox(mp4::FOURCC_MOOV);
     box_byte_stream.WriteU64(0);
     {
       // <child 1>
-      box_byte_stream.StartBox();
+      box_byte_stream.StartBox(mp4::FOURCC_TRAK);
       EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 2u);
 
       box_byte_stream.WriteU32(0x1617);
       {
         // <grand child 1>
-        box_byte_stream.StartBox();
+        box_byte_stream.StartBox(mp4::FOURCC_MDIA);
         EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 3u);
         box_byte_stream.WriteU16(0);
         box_byte_stream.EndBox();
@@ -153,7 +172,7 @@
       EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 1u);
 
       // <child 2>
-      box_byte_stream.StartBox();
+      box_byte_stream.StartBox(mp4::FOURCC_MVEX);
       EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 2u);
       box_byte_stream.WriteU32(0);
       box_byte_stream.EndBox();
@@ -167,23 +186,32 @@
     base::BigEndianReader reader(written_data.data(), written_data.size());
 
     uint32_t parent;
+    uint32_t fourcc;
     reader.ReadU32(&parent);
-    EXPECT_EQ(34u, parent);
+    EXPECT_EQ(50u, parent);
+    reader.ReadU32(&fourcc);
+    EXPECT_EQ(mp4::FOURCC_MOOV, fourcc);
     reader.Skip(8);
 
     uint32_t child_1;
     reader.ReadU32(&child_1);
-    EXPECT_EQ(14u, child_1);
+    EXPECT_EQ(22u, child_1);
+    reader.ReadU32(&fourcc);
+    EXPECT_EQ(mp4::FOURCC_TRAK, fourcc);
     reader.Skip(4);
 
     uint32_t grand_child_1;
     reader.ReadU32(&grand_child_1);
-    EXPECT_EQ(6u, grand_child_1);
+    EXPECT_EQ(10u, grand_child_1);
+    reader.ReadU32(&fourcc);
+    EXPECT_EQ(mp4::FOURCC_MDIA, fourcc);
     reader.Skip(2);
 
     uint32_t child_2;
     reader.ReadU32(&child_2);
-    EXPECT_EQ(8u, child_2);
+    EXPECT_EQ(12u, child_2);
+    reader.ReadU32(&fourcc);
+    EXPECT_EQ(mp4::FOURCC_MVEX, fourcc);
   }
 
   // Flush use.
@@ -191,23 +219,23 @@
     BoxByteStream box_byte_stream;
 
     // <parent>
-    box_byte_stream.StartBox();
+    box_byte_stream.StartBox(mp4::FOURCC_MOOV);
     box_byte_stream.WriteU64(0);
     {
       // <child 1>
-      box_byte_stream.StartBox();
+      box_byte_stream.StartBox(mp4::FOURCC_TRAK);
       EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 2u);
 
       box_byte_stream.WriteU32(0x1617);
       {
         // <grand child 1>
-        box_byte_stream.StartBox();
+        box_byte_stream.StartBox(mp4::FOURCC_MDIA);
         EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 3u);
         box_byte_stream.WriteU16(0);
       }
 
       // <child 2>
-      box_byte_stream.StartBox();
+      box_byte_stream.StartBox(mp4::FOURCC_MVEX);
       EXPECT_EQ(box_byte_stream.GetSizeOffsetsForTesting().size(), 4u);
       box_byte_stream.WriteU32(0);
     }
@@ -218,23 +246,32 @@
     base::BigEndianReader reader(written_data.data(), written_data.size());
 
     uint32_t parent;
+    uint32_t fourcc;
     reader.ReadU32(&parent);
-    EXPECT_EQ(34u, parent);
+    EXPECT_EQ(50u, parent);
+    reader.ReadU32(&fourcc);
+    EXPECT_EQ(mp4::FOURCC_MOOV, fourcc);
     reader.Skip(8);
 
     uint32_t child_1;
     reader.ReadU32(&child_1);
-    EXPECT_EQ(22u, child_1);
+    EXPECT_EQ(34u, child_1);
+    reader.ReadU32(&fourcc);
+    EXPECT_EQ(mp4::FOURCC_TRAK, fourcc);
     reader.Skip(4);
 
     uint32_t grand_child_1;
     reader.ReadU32(&grand_child_1);
-    EXPECT_EQ(14u, grand_child_1);
+    EXPECT_EQ(22u, grand_child_1);
+    reader.ReadU32(&fourcc);
+    EXPECT_EQ(mp4::FOURCC_MDIA, fourcc);
     reader.Skip(2);
 
     uint32_t child_2;
     reader.ReadU32(&child_2);
-    EXPECT_EQ(8u, child_2);
+    EXPECT_EQ(12u, child_2);
+    reader.ReadU32(&fourcc);
+    EXPECT_EQ(mp4::FOURCC_MVEX, fourcc);
   }
 }
 
diff --git a/media/muxers/mp4_box_writer.cc b/media/muxers/mp4_box_writer.cc
index 3bf888e..1a6f9e6f 100644
--- a/media/muxers/mp4_box_writer.cc
+++ b/media/muxers/mp4_box_writer.cc
@@ -42,22 +42,4 @@
   child_boxes_.push_back(std::move(box_writer));
 }
 
-void Mp4BoxWriter::WriteBox(BoxByteStream& writer, mp4::FourCC fourcc) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  writer.WriteU32(fourcc);
-}
-
-void Mp4BoxWriter::WriteFullBox(BoxByteStream& writer,
-                                mp4::FourCC fourcc,
-                                uint32_t flags,
-                                uint8_t version) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  writer.WriteU32(fourcc);
-
-  writer.WriteU8(version);
-
-  writer.WriteU24(flags);
-}
-
 }  // namespace media
diff --git a/media/muxers/mp4_box_writer.h b/media/muxers/mp4_box_writer.h
index 4542b96d..0e23c3e 100644
--- a/media/muxers/mp4_box_writer.h
+++ b/media/muxers/mp4_box_writer.h
@@ -45,17 +45,6 @@
   // Add child box of the current box.
   void AddChildBox(std::unique_ptr<Mp4BoxWriter> box_writer);
 
-  // Write data of mp4::Box type to the writer.
-  void WriteBox(BoxByteStream& writer, mp4::FourCC fourcc);
-
-  // Write data of mp4::FullBox to the writer.
-  void WriteFullBox(BoxByteStream& writer,
-                    mp4::FourCC fourcc,
-                    uint32_t flags = 0,
-                    // Chromium MP4 Muxer supports 64 bits as a default, but the
-                    // individual box can override it as needed.
-                    uint8_t version = 1);
-
   // Get the Mp4MuxerContext object.
   const Mp4MuxerContext& context() const { return context_; }
 
diff --git a/media/muxers/mp4_movie_box_writer.cc b/media/muxers/mp4_movie_box_writer.cc
index c00ebd0..51c2c2cc 100644
--- a/media/muxers/mp4_movie_box_writer.cc
+++ b/media/muxers/mp4_movie_box_writer.cc
@@ -11,20 +11,66 @@
 #include "base/big_endian.h"
 #include "media/muxers/box_byte_stream.h"
 #include "media/muxers/mp4_muxer_context.h"
+#include "media/muxers/mp4_type_conversion.h"
 #include "media/muxers/output_position_tracker.h"
 
 namespace media {
 
-namespace {}  // namespace
+namespace {
+
+// ISO/IEC 14496-12.
+// A transformation matrix for the video.
+// Video frames are not scaled, rotated, or skewed, and are displayed at
+// their original size with no zoom or depth applied.
+
+// The value 0x00010000 in the top-left and middle element of the
+// matrix specifies the horizontal and vertical scaling factor,
+// respectively. This means that the video frames are not scaled and
+// are displayed at their original size.
+
+// The bottom-right element of the matrix, with a value of 0x40000000,
+// specifies the fixed-point value of the zoom or depth of the video frames.
+// This value is equal to 1.0 in decimal notation, meaning that there
+// is no zoom or depth applied to the video frames.
+constexpr int32_t kDisplayIdentityMatrix[9] = {
+    0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
+
+void WriteIsoTime(BoxByteStream& writer, base::Time time) {
+  uint64_t iso_time =
+      time.ToDeltaSinceWindowsEpoch().InSeconds() - k1601To1904DeltaInSeconds;
+
+  writer.WriteU64(iso_time);
+}
+
+void WriteLowHigh(BoxByteStream& writer, uint32_t value) {
+  writer.WriteU16(value & 0xFFFF);
+  writer.WriteU16(value >> 16);
+}
+
+}  // namespace
 
 // Mp4MovieBoxWriter class.
-Mp4MovieBoxWriter::Mp4MovieBoxWriter(const Mp4MuxerContext& context,
+Mp4MovieBoxWriter::Mp4MovieBoxWriter(const Mp4MuxerContext& input_context,
                                      const mp4::writable_boxes::Movie& box)
-    : Mp4BoxWriter(context), box_(box) {
+    : Mp4BoxWriter(input_context), box_(box) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  AddChildBox(std::make_unique<Mp4MovieHeaderBoxWriter>(context, box_.header));
   AddChildBox(
-      std::make_unique<Mp4MovieExtendsBoxWriter>(context, box_.extends));
+      std::make_unique<Mp4MovieHeaderBoxWriter>(context(), box_.header));
+
+  if (auto video_index = context().GetVideoIndex()) {
+    DCHECK_LE(*video_index, box_.tracks.size());
+    AddChildBox(std::make_unique<Mp4MovieTrackBoxWriter>(
+        context(), box_.tracks[*video_index]));
+  }
+
+  if (auto audio_index = context().GetAudioIndex()) {
+    DCHECK_LE(*audio_index, box_.tracks.size());
+    AddChildBox(std::make_unique<Mp4MovieTrackBoxWriter>(
+        context(), box_.tracks[*audio_index]));
+  }
+
+  AddChildBox(
+      std::make_unique<Mp4MovieExtendsBoxWriter>(context(), box_.extends));
 }
 
 Mp4MovieBoxWriter::~Mp4MovieBoxWriter() = default;
@@ -32,10 +78,7 @@
 void Mp4MovieBoxWriter::Write(BoxByteStream& writer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  writer.StartBox();
-
-  CHECK_EQ(box_.fourcc, mp4::FOURCC_MOOV);
-  WriteBox(writer, box_.fourcc);
+  writer.StartBox(mp4::FOURCC_MOOV);
 
   // Write the children.
   WriteChildren(writer);
@@ -56,26 +99,10 @@
 void Mp4MovieHeaderBoxWriter::Write(BoxByteStream& writer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  writer.StartBox();
+  writer.StartFullBox(mp4::FOURCC_MVHD);
 
-  CHECK_EQ(box_.fourcc, mp4::FOURCC_MVHD);
-  WriteFullBox(writer, box_.fourcc);
-
-  // Convert to ISO time, seconds since midnight, Jan. 1, 1904, in UTC time.
-  // base::Time time1904;
-  // base::Time::FromUTCString("1904-01-01 00:00:00 UTC", &time1904);
-  // 9561628800 = time1904.ToDeltaSinceWindowsEpoch().InSeconds();
-  constexpr int64_t k1601To1904DeltaInSeconds = INT64_C(9561628800);
-
-  uint64_t iso_creation_time =
-      box_.creation_time.ToDeltaSinceWindowsEpoch().InSeconds() -
-      k1601To1904DeltaInSeconds;
-  uint64_t iso_modification_time =
-      box_.modification_time.ToDeltaSinceWindowsEpoch().InSeconds() -
-      k1601To1904DeltaInSeconds;
-
-  writer.WriteU64(iso_creation_time);
-  writer.WriteU64(iso_modification_time);
+  WriteIsoTime(writer, box_.creation_time);
+  WriteIsoTime(writer, box_.modification_time);
   writer.WriteU32(box_.timescale);
   writer.WriteU64(box_.duration.InSeconds());
 
@@ -85,23 +112,6 @@
   writer.WriteU32(0);           // reserved.
   writer.WriteU32(0);           // reserved.
 
-  // ISO/IEC 14496-12.
-  // A transformation matrix for the video.
-  // Video frames are not scaled, rotated, or skewed, and are displayed at
-  // their original size with no zoom or depth applied.
-
-  // The value 0x00010000 in the top-left and middle element of the
-  // matrix specifies the horizontal and vertical scaling factor,
-  // respectively. This means that the video frames are not scaled and
-  // are displayed at their original size.
-
-  // The bottom-right element of the matrix, with a value of 0x40000000,
-  // specifies the fixed-point value of the zoom or depth of the video frames.
-  // This value is equal to 1.0 in decimal notation, meaning that there
-  // is no zoom or depth applied to the video frames.
-  constexpr int32_t kDisplayIdentityMatrix[9] = {
-      0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000};
-
   for (auto* it = std::begin(kDisplayIdentityMatrix);
        it != std::end(kDisplayIdentityMatrix); ++it) {
     writer.WriteU32(*it);
@@ -124,14 +134,16 @@
     : Mp4BoxWriter(input_context), box_(box) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (context().GetVideoIndex().has_value()) {
+  if (auto video_index = context().GetVideoIndex()) {
+    DCHECK_LE(*video_index, box_.track_extends.size());
     AddChildBox(std::make_unique<Mp4MovieTrackExtendsBoxWriter>(
-        context(), box_.track_extends[context().GetVideoIndex().value()]));
+        context(), box_.track_extends[*video_index]));
   }
 
-  if (context().GetAudioIndex().has_value()) {
+  if (auto audio_index = context().GetAudioIndex()) {
+    DCHECK_LE(*audio_index, box_.track_extends.size());
     AddChildBox(std::make_unique<Mp4MovieTrackExtendsBoxWriter>(
-        context(), box_.track_extends[context().GetAudioIndex().value()]));
+        context(), box_.track_extends[*audio_index]));
   }
 }
 
@@ -140,10 +152,7 @@
 void Mp4MovieExtendsBoxWriter::Write(BoxByteStream& writer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  writer.StartBox();
-
-  CHECK_EQ(box_.fourcc, mp4::FOURCC_MVEX);
-  WriteBox(writer, box_.fourcc);
+  writer.StartBox(mp4::FOURCC_MVEX);
 
   // Write the children.
   WriteChildren(writer);
@@ -164,10 +173,7 @@
 void Mp4MovieTrackExtendsBoxWriter::Write(BoxByteStream& writer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  writer.StartBox();
-
-  CHECK_EQ(box_.fourcc, mp4::FOURCC_TREX);
-  WriteFullBox(writer, box_.fourcc);
+  writer.StartFullBox(mp4::FOURCC_TREX);
 
   writer.WriteU32(box_.track_id);
   writer.WriteU32(box_.default_sample_description_index);
@@ -179,4 +185,227 @@
   writer.EndBox();
 }
 
+// Mp4MovieTrackBoxWriter (`trak`) class.
+Mp4MovieTrackBoxWriter::Mp4MovieTrackBoxWriter(
+    const Mp4MuxerContext& context,
+    const mp4::writable_boxes::Track& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  AddChildBox(
+      std::make_unique<Mp4MovieTrackHeaderBoxWriter>(context, box_.header));
+  AddChildBox(std::make_unique<Mp4MovieMediaBoxWriter>(context, box_.media));
+}
+
+Mp4MovieTrackBoxWriter::~Mp4MovieTrackBoxWriter() = default;
+
+void Mp4MovieTrackBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartBox(mp4::FOURCC_TRAK);
+
+  WriteChildren(writer);
+
+  writer.EndBox();
+}
+
+// Mp4MovieTrackHeaderBoxWriter (`tkhd`) class.
+Mp4MovieTrackHeaderBoxWriter::Mp4MovieTrackHeaderBoxWriter(
+    const Mp4MuxerContext& context,
+    const mp4::writable_boxes::TrackHeader& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+Mp4MovieTrackHeaderBoxWriter::~Mp4MovieTrackHeaderBoxWriter() = default;
+
+void Mp4MovieTrackHeaderBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartFullBox(mp4::FOURCC_TKHD, box_.flags, /*version*/ 1);
+
+  WriteIsoTime(writer, box_.creation_time);
+  WriteIsoTime(writer, box_.modification_time);
+
+  writer.WriteU32(box_.track_id);
+  writer.WriteU32(0);  // reserved
+  writer.WriteU64(box_.duration.InSeconds());
+  writer.WriteU32(0);  // reserved;
+  writer.WriteU32(0);  // reserved;
+  writer.WriteU16(0);  // layer, 0 is the normal value.
+  writer.WriteU16(0);  // alternate_group,
+  if (box_.is_audio) {
+    // 1.0 (0x0100) is a full volume for the audio.
+    writer.WriteU16(0x0100);
+  } else {
+    writer.WriteU16(0);
+  }
+  writer.WriteU16(0);  // reserved.
+
+  for (auto* it = std::begin(kDisplayIdentityMatrix);
+       it != std::end(kDisplayIdentityMatrix); ++it) {
+    writer.WriteU32(*it);
+  }
+
+  WriteLowHigh(writer, box_.natural_size.width());
+  WriteLowHigh(writer, box_.natural_size.height());
+
+  writer.EndBox();
+}
+
+// Mp4MovieMediaBoxWriter (`mdia`) class.
+Mp4MovieMediaBoxWriter::Mp4MovieMediaBoxWriter(
+    const Mp4MuxerContext& context,
+    const mp4::writable_boxes::Media& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  AddChildBox(
+      std::make_unique<Mp4MovieMediaHeaderBoxWriter>(context, box_.header));
+  AddChildBox(
+      std::make_unique<Mp4MovieMediaHandlerBoxWriter>(context, box_.handler));
+  AddChildBox(std::make_unique<Mp4MovieMediaInformationBoxWriter>(
+      context, box_.information));
+}
+
+Mp4MovieMediaBoxWriter::~Mp4MovieMediaBoxWriter() = default;
+
+void Mp4MovieMediaBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartBox(mp4::FOURCC_MDIA);
+
+  WriteChildren(writer);
+
+  writer.EndBox();
+}
+
+// Mp4MovieMediaHeaderBoxWriter (`mdhd`) class.
+Mp4MovieMediaHeaderBoxWriter::Mp4MovieMediaHeaderBoxWriter(
+    const Mp4MuxerContext& context,
+    const mp4::writable_boxes::MediaHeader& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+Mp4MovieMediaHeaderBoxWriter::~Mp4MovieMediaHeaderBoxWriter() = default;
+
+void Mp4MovieMediaHeaderBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartFullBox(mp4::FOURCC_MDHD);
+
+  WriteIsoTime(writer, box_.creation_time);
+  WriteIsoTime(writer, box_.modification_time);
+
+  writer.WriteU32(box_.timescale);
+  writer.WriteU64(box_.duration.InSeconds());
+  uint16_t language_code = ConvertIso639LanguageCodeToU16(box_.language);
+  writer.WriteU16(language_code);
+  writer.WriteU16(0);  // pre_defined = 0;
+
+  writer.EndBox();
+}
+
+// Mp4MovieMediaHandlerBoxWriter (`hdlr`) class.
+Mp4MovieMediaHandlerBoxWriter::Mp4MovieMediaHandlerBoxWriter(
+    const Mp4MuxerContext& context,
+    const mp4::writable_boxes::MediaHandler& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+Mp4MovieMediaHandlerBoxWriter::~Mp4MovieMediaHandlerBoxWriter() = default;
+
+void Mp4MovieMediaHandlerBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartFullBox(mp4::FOURCC_HDLR);
+
+  writer.WriteU32(0);  // predefined = 0;
+  writer.WriteU32(box_.handler_type);
+
+  writer.WriteU32(0);  // reserved;
+  writer.WriteU32(0);  // reserved;
+  writer.WriteU32(0);  // reserved;
+
+  // zero-terminated C-style string for name.
+  if (!box_.name.empty()) {
+    writer.WriteBytes(box_.name.c_str(), box_.name.size());
+    if (box_.name.back() != 0) {
+      writer.WriteU8(0);
+    }
+  } else {
+    writer.WriteU8(0);
+  }
+
+  WriteChildren(writer);
+
+  writer.EndBox();
+}
+
+// Mp4MovieMediaInformationBoxWriter (`minf`) class.
+Mp4MovieMediaInformationBoxWriter::Mp4MovieMediaInformationBoxWriter(
+    const Mp4MuxerContext& context,
+    const mp4::writable_boxes::MediaInformation& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  AddChildBox(std::make_unique<Mp4MovieSampleTableBoxWriter>(
+      context, box_.sample_table));
+}
+
+Mp4MovieMediaInformationBoxWriter::~Mp4MovieMediaInformationBoxWriter() =
+    default;
+
+void Mp4MovieMediaInformationBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartBox(mp4::FOURCC_MINF);
+
+  WriteChildren(writer);
+
+  writer.EndBox();
+}
+
+// Mp4MovieSampleTableBoxWriter (`stbl`) class.
+Mp4MovieSampleTableBoxWriter::Mp4MovieSampleTableBoxWriter(
+    const Mp4MuxerContext& context,
+    const mp4::writable_boxes::SampleTable& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  AddChildBox(std::make_unique<Mp4MovieSampleDescriptionBoxWriter>(
+      context, box_.sample_description));
+}
+
+Mp4MovieSampleTableBoxWriter::~Mp4MovieSampleTableBoxWriter() = default;
+
+void Mp4MovieSampleTableBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartBox(mp4::FOURCC_STBL);
+
+  WriteChildren(writer);
+
+  writer.EndBox();
+}
+
+// Mp4MovieSampleDescriptionBoxWriter (`stsd`) class.
+Mp4MovieSampleDescriptionBoxWriter::Mp4MovieSampleDescriptionBoxWriter(
+    const Mp4MuxerContext& context,
+    const mp4::writable_boxes::SampleDescription& box)
+    : Mp4BoxWriter(context), box_(box) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+}
+
+Mp4MovieSampleDescriptionBoxWriter::~Mp4MovieSampleDescriptionBoxWriter() =
+    default;
+
+void Mp4MovieSampleDescriptionBoxWriter::Write(BoxByteStream& writer) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  writer.StartFullBox(mp4::FOURCC_STSD);
+
+  writer.WriteU32(box_.entry_count);
+
+  writer.EndBox();
+}
+
 }  // namespace media
diff --git a/media/muxers/mp4_movie_box_writer.h b/media/muxers/mp4_movie_box_writer.h
index c3e2315d..9250eba6 100644
--- a/media/muxers/mp4_movie_box_writer.h
+++ b/media/muxers/mp4_movie_box_writer.h
@@ -17,28 +17,43 @@
 
 class Mp4MuxerContext;
 
-#define DECLARE_MP4_WRITER_CLASS_WITH_BOX(CLASS_NAME, BOX_TYPE)      \
-  class MEDIA_EXPORT CLASS_NAME : public Mp4BoxWriter {              \
+#define DECLARE_MP4_BOX_WRITER_CLASS(class_name, box_type)           \
+  class MEDIA_EXPORT class_name : public Mp4BoxWriter {              \
    public:                                                           \
-    CLASS_NAME(const Mp4MuxerContext& context, const BOX_TYPE& box); \
-    ~CLASS_NAME() override;                                          \
-    CLASS_NAME(const CLASS_NAME&) = delete;                          \
-    CLASS_NAME& operator=(const CLASS_NAME&) = delete;               \
+    class_name(const Mp4MuxerContext& context, const box_type& box); \
+    ~class_name() override;                                          \
+    class_name(const class_name&) = delete;                          \
+    class_name& operator=(const class_name&) = delete;               \
     void Write(BoxByteStream& writer) override;                      \
                                                                      \
    private:                                                          \
-    const BOX_TYPE& box_;                                            \
+    const box_type& box_;                                            \
     SEQUENCE_CHECKER(sequence_checker_);                             \
-  };
+  }
 
-DECLARE_MP4_WRITER_CLASS_WITH_BOX(Mp4MovieBoxWriter, mp4::writable_boxes::Movie)
-DECLARE_MP4_WRITER_CLASS_WITH_BOX(Mp4MovieHeaderBoxWriter,
-                                  mp4::writable_boxes::MovieHeader)
-DECLARE_MP4_WRITER_CLASS_WITH_BOX(Mp4MovieExtendsBoxWriter,
-                                  mp4::writable_boxes::MovieExtends)
-DECLARE_MP4_WRITER_CLASS_WITH_BOX(Mp4MovieTrackExtendsBoxWriter,
-                                  mp4::writable_boxes::TrackExtends)
-
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieBoxWriter, mp4::writable_boxes::Movie);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieHeaderBoxWriter,
+                             mp4::writable_boxes::MovieHeader);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieExtendsBoxWriter,
+                             mp4::writable_boxes::MovieExtends);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieTrackExtendsBoxWriter,
+                             mp4::writable_boxes::TrackExtends);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieTrackBoxWriter,
+                             mp4::writable_boxes::Track);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieTrackHeaderBoxWriter,
+                             mp4::writable_boxes::TrackHeader);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieMediaBoxWriter,
+                             mp4::writable_boxes::Media);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieMediaHeaderBoxWriter,
+                             mp4::writable_boxes::MediaHeader);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieMediaHandlerBoxWriter,
+                             mp4::writable_boxes::MediaHandler);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieMediaInformationBoxWriter,
+                             mp4::writable_boxes::MediaInformation);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieSampleTableBoxWriter,
+                             mp4::writable_boxes::SampleTable);
+DECLARE_MP4_BOX_WRITER_CLASS(Mp4MovieSampleDescriptionBoxWriter,
+                             mp4::writable_boxes::SampleDescription);
 }  // namespace media
 
 #endif  // MEDIA_MUXERS_MP4_MOVIE_BOX_WRITER_H_
diff --git a/media/muxers/mp4_muxer_box_writer_unittest.cc b/media/muxers/mp4_muxer_box_writer_unittest.cc
index 90e17747..47addd3 100644
--- a/media/muxers/mp4_muxer_box_writer_unittest.cc
+++ b/media/muxers/mp4_muxer_box_writer_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <string>
+#include <type_traits>
 #include <vector>
 
 #include "base/big_endian.h"
@@ -16,11 +17,35 @@
 #include "media/muxers/mp4_box_writer.h"
 #include "media/muxers/mp4_movie_box_writer.h"
 #include "media/muxers/mp4_muxer_context.h"
+#include "media/muxers/mp4_type_conversion.h"
 #include "media/muxers/output_position_tracker.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace media {
 
+namespace {
+
+constexpr uint32_t kDuration1 = 12345u;
+constexpr uint32_t kDuration2 = 12300u;
+constexpr uint32_t kDefaultSampleSize = 1024u;
+constexpr uint32_t kWidth = 1024u;
+constexpr uint32_t kHeight = 780u;
+constexpr uint32_t kVideoTimescale = 30000u;
+constexpr uint32_t kAudioTimescale = 44100u;
+constexpr uint32_t kVideoSampleFlags = 0x112u;
+constexpr uint32_t kAudioSampleFlags = 0x113u;
+constexpr uint16_t kAudioVolume = 0x0100;
+constexpr char kVideoHandlerName[] = "VideoHandler";
+constexpr char kAudioHandlerName[] = "SoundHandler";
+
+uint64_t ConvertTo1904TimeInSeconds(base::Time time) {
+  base::Time time1904;
+  CHECK(base::Time::FromUTCString("1904-01-01 00:00:00 UTC", &time1904));
+  uint64_t iso_time = (time - time1904).InSeconds();
+  return iso_time;
+}
+
+}  // namespace
 class Mp4MuxerBoxWriterTest : public testing::Test {
  public:
   Mp4MuxerBoxWriterTest() = default;
@@ -77,15 +102,11 @@
   base::Time creation_time = base::Time::FromTimeT(0x1234567);
   base::Time modification_time = base::Time::FromTimeT(0x2345678);
   {
-    mp4_moov_box.fourcc = mp4::FOURCC_MOOV;
-    mp4_moov_box.header.fourcc = mp4::FOURCC_MVHD;
     mp4_moov_box.header.creation_time = creation_time;
     mp4_moov_box.header.modification_time = modification_time;
-    mp4_moov_box.header.timescale = 0x7530;
+    mp4_moov_box.header.timescale = kVideoTimescale;
     mp4_moov_box.header.duration = base::Seconds(0);
-    mp4_moov_box.header.next_track_id = 2;
-
-    mp4_moov_box.extends.fourcc = mp4::FOURCC_MVEX;
+    mp4_moov_box.header.next_track_id = 1u;
   }
 
   // Flush at requested.
@@ -109,17 +130,12 @@
   EXPECT_TRUE(reader->ReadChild(&mvhd_box));
   EXPECT_EQ(mvhd_box.version, 1);
 
-  base::Time time1904;
-  CHECK(base::Time::FromUTCString("1904-01-01 00:00:00 UTC", &time1904));
-  uint64_t iso_time_creation_time = (creation_time - time1904).InSeconds();
-  uint64_t iso_time_modification_time =
-      (modification_time - time1904).InSeconds();
-
-  EXPECT_EQ(mvhd_box.creation_time, iso_time_creation_time);
-  EXPECT_EQ(mvhd_box.modification_time, iso_time_modification_time);
-  EXPECT_EQ(mvhd_box.timescale, 0x7530u);
+  EXPECT_EQ(mvhd_box.creation_time, ConvertTo1904TimeInSeconds(creation_time));
+  EXPECT_EQ(mvhd_box.modification_time,
+            ConvertTo1904TimeInSeconds(modification_time));
+  EXPECT_EQ(mvhd_box.timescale, kVideoTimescale);
   EXPECT_EQ(mvhd_box.duration, 0u);
-  EXPECT_EQ(mvhd_box.next_track_id, 2u);
+  EXPECT_EQ(mvhd_box.next_track_id, 1u);
 
   // Once Flush, it needs to reset the internal objects of context and buffer.
   Reset();
@@ -134,32 +150,35 @@
 
   // Populates the boxes during Mp4Muxer::OnEncodedVideo.
   mp4::writable_boxes::Movie mp4_moov_box;
-  mp4_moov_box.fourcc = mp4::FOURCC_MOOV;
-  mp4_moov_box.header.fourcc = mp4::FOURCC_MVHD;
-  mp4_moov_box.extends.fourcc = mp4::FOURCC_MVEX;
-
   {
     mp4::writable_boxes::TrackExtends video_extends;
-    video_extends.fourcc = mp4::FOURCC_TREX;
     video_extends.track_id = 1u;
     video_extends.default_sample_description_index = 1u;
     video_extends.default_sample_duration = base::Seconds(0);
-    video_extends.default_sample_size = 1024u;
-    video_extends.default_sample_flags = 0x112u;
+    video_extends.default_sample_size = kDefaultSampleSize;
+    video_extends.default_sample_flags = kVideoSampleFlags;
     mp4_moov_box.extends.track_extends.push_back(std::move(video_extends));
     context()->SetVideoIndex(0);
+
+    mp4::writable_boxes::Track video_track = {};
+    // Minimum value.
+    mp4_moov_box.tracks.push_back(std::move(video_track));
   }
 
   {
     mp4::writable_boxes::TrackExtends audio_extends;
-    audio_extends.fourcc = mp4::FOURCC_TREX;
     audio_extends.track_id = 2u;
     audio_extends.default_sample_description_index = 1u;
     audio_extends.default_sample_duration = base::Seconds(0);
-    audio_extends.default_sample_size = 989u;
-    audio_extends.default_sample_flags = 0x113;
+    audio_extends.default_sample_size = kDefaultSampleSize;
+    audio_extends.default_sample_flags = kAudioSampleFlags;
     mp4_moov_box.extends.track_extends.push_back(std::move(audio_extends));
     context()->SetAudioIndex(1);
+
+    mp4::writable_boxes::Track audio_track = {};
+
+    // Minimum value.
+    mp4_moov_box.tracks.push_back(std::move(audio_track));
   }
 
   // Flush at requested.
@@ -188,17 +207,161 @@
   EXPECT_EQ(mvex_box.tracks[0].track_id, 1u);
   EXPECT_EQ(mvex_box.tracks[0].default_sample_description_index, 1u);
   EXPECT_EQ(mvex_box.tracks[0].default_sample_duration, 0u);
-  EXPECT_EQ(mvex_box.tracks[0].default_sample_size, 1024u);
-  EXPECT_EQ(mvex_box.tracks[0].default_sample_flags, 0x112u);
+  EXPECT_EQ(mvex_box.tracks[0].default_sample_size, kDefaultSampleSize);
+  EXPECT_EQ(mvex_box.tracks[0].default_sample_flags, kVideoSampleFlags);
 
   EXPECT_EQ(mvex_box.tracks[1].track_id, 2u);
   EXPECT_EQ(mvex_box.tracks[1].default_sample_description_index, 1u);
   EXPECT_EQ(mvex_box.tracks[1].default_sample_duration, 0u);
-  EXPECT_EQ(mvex_box.tracks[1].default_sample_size, 989u);
-  EXPECT_EQ(mvex_box.tracks[1].default_sample_flags, 0x113u);
+  EXPECT_EQ(mvex_box.tracks[1].default_sample_size, kDefaultSampleSize);
+  EXPECT_EQ(mvex_box.tracks[1].default_sample_flags, kAudioSampleFlags);
 
   // Once Flush, it needs to reset the internal objects of context and buffer.
   Reset();
 }
 
+TEST_F(Mp4MuxerBoxWriterTest, Mp4MovieTrackAndMediaHeader) {
+  // Tests `tkhd/mdhd` box writer.
+  base::RunLoop run_loop;
+
+  std::vector<uint8_t> written_data;
+  CreateContext(written_data);
+
+  // Populates the boxes during Mp4Muxer::OnEncodedVideo.
+  constexpr size_t kVideoIndex = 0;
+  constexpr size_t kAudioIndex = 1;
+
+  mp4::writable_boxes::Movie mp4_moov_box;
+  base::Time creation_time = base::Time::FromTimeT(0x1234567);
+  base::Time modification_time = base::Time::FromTimeT(0x2345678);
+  {
+    mp4::writable_boxes::TrackExtends video_extends;
+    mp4_moov_box.extends.track_extends.push_back(std::move(video_extends));
+
+    mp4::writable_boxes::Track video_track = {};
+    using T = std::underlying_type_t<mp4::writable_boxes::TrackHeaderFlags>;
+    video_track.header.flags =
+        (static_cast<T>(mp4::writable_boxes::TrackHeaderFlags::kTrackEnabled) |
+         static_cast<T>(mp4::writable_boxes::TrackHeaderFlags::kTrackInMovie));
+    video_track.header.track_id = 1u;
+    video_track.header.creation_time = creation_time;
+    video_track.header.modification_time = modification_time;
+    video_track.header.duration = base::Seconds(kDuration1);
+    video_track.header.is_audio = false;
+    video_track.header.natural_size = gfx::Size(kWidth, kHeight);
+
+    video_track.media.header.creation_time = creation_time;
+    video_track.media.header.modification_time = modification_time;
+    video_track.media.header.duration = base::Seconds(kDuration1);
+    video_track.media.header.timescale = kVideoTimescale;
+    video_track.media.header.language = "und";
+
+    video_track.media.handler.handler_type = mp4::FOURCC_VIDE;
+    video_track.media.handler.name = kVideoHandlerName;
+
+    mp4_moov_box.tracks.push_back(std::move(video_track));
+    context()->SetVideoIndex(kVideoIndex);
+  }
+
+  {
+    mp4::writable_boxes::TrackExtends audio_extends;
+    mp4_moov_box.extends.track_extends.push_back(std::move(audio_extends));
+
+    mp4::writable_boxes::Track audio_track = {};
+    audio_track.header.track_id = 2u;
+    audio_track.header.creation_time = creation_time;
+    audio_track.header.modification_time = modification_time;
+    audio_track.header.duration = base::Seconds(kDuration2);
+    audio_track.header.is_audio = true;
+    audio_track.header.natural_size = gfx::Size(0, 0);
+
+    audio_track.media.header.creation_time = creation_time;
+    audio_track.media.header.modification_time = modification_time;
+    audio_track.media.header.duration = base::Seconds(kDuration2);
+    audio_track.media.header.timescale = kAudioTimescale;
+    audio_track.media.header.language = "";
+
+    audio_track.media.handler.handler_type = mp4::FOURCC_SOUN;
+    audio_track.media.handler.name = kAudioHandlerName;
+
+    mp4_moov_box.tracks.push_back(std::move(audio_track));
+    context()->SetAudioIndex(kAudioIndex);
+  }
+
+  // Flush at requested.
+  Mp4MovieBoxWriter box_writer(*context(), mp4_moov_box);
+  FlushAndWait(&box_writer);
+
+  // Validation of the written boxes.
+
+  // `written_data` test.
+  std::unique_ptr<mp4::BoxReader> reader;
+  mp4::ParseResult result = mp4::BoxReader::ReadTopLevelBox(
+      written_data.data(), written_data.size(), nullptr, &reader);
+
+  EXPECT_EQ(result, mp4::ParseResult::kOk);
+  EXPECT_TRUE(reader);
+  EXPECT_EQ(mp4::FOURCC_MOOV, reader->type());
+  EXPECT_TRUE(reader->ScanChildren());
+
+  // Track test.
+  std::vector<mp4::Track> track_boxes;
+  EXPECT_TRUE(reader->ReadChildren(&track_boxes));
+
+  // mp4::MovieExtends mvex_box = mvex_boxes[0];
+  EXPECT_EQ(track_boxes.size(), 2u);
+
+  // Track header validation.
+
+  EXPECT_EQ(track_boxes[kVideoIndex].header.track_id, 1u);
+  EXPECT_EQ(track_boxes[kVideoIndex].header.creation_time,
+            ConvertTo1904TimeInSeconds(creation_time));
+  EXPECT_EQ(track_boxes[kVideoIndex].header.modification_time,
+            ConvertTo1904TimeInSeconds(modification_time));
+  EXPECT_EQ(track_boxes[kVideoIndex].header.duration, kDuration1);
+  EXPECT_EQ(track_boxes[kVideoIndex].header.volume, 0);
+  EXPECT_EQ(track_boxes[kVideoIndex].header.width, kWidth);
+  EXPECT_EQ(track_boxes[kVideoIndex].header.height, kHeight);
+
+  EXPECT_EQ(track_boxes[kAudioIndex].header.track_id, 2u);
+  EXPECT_EQ(track_boxes[kAudioIndex].header.creation_time,
+            ConvertTo1904TimeInSeconds(creation_time));
+  EXPECT_EQ(track_boxes[kAudioIndex].header.modification_time,
+            ConvertTo1904TimeInSeconds(modification_time));
+  EXPECT_EQ(track_boxes[kAudioIndex].header.duration, kDuration2);
+  EXPECT_EQ(track_boxes[kAudioIndex].header.volume, kAudioVolume);
+  EXPECT_EQ(track_boxes[kAudioIndex].header.width, 0u);
+  EXPECT_EQ(track_boxes[kAudioIndex].header.height, 0u);
+
+  // Media Header validation.
+  EXPECT_EQ(track_boxes[kAudioIndex].media.header.creation_time,
+            ConvertTo1904TimeInSeconds(creation_time));
+  EXPECT_EQ(track_boxes[kAudioIndex].media.header.modification_time,
+            ConvertTo1904TimeInSeconds(modification_time));
+  EXPECT_EQ(track_boxes[kAudioIndex].media.header.duration, kDuration2);
+  EXPECT_EQ(track_boxes[kAudioIndex].media.header.timescale, kAudioTimescale);
+  EXPECT_EQ(track_boxes[kAudioIndex].media.header.language_code,
+            kUndefinedLanguageCode);
+
+  EXPECT_EQ(track_boxes[kVideoIndex].media.header.creation_time,
+            ConvertTo1904TimeInSeconds(creation_time));
+  EXPECT_EQ(track_boxes[kVideoIndex].media.header.modification_time,
+            ConvertTo1904TimeInSeconds(modification_time));
+  EXPECT_EQ(track_boxes[kVideoIndex].media.header.duration, kDuration1);
+  EXPECT_EQ(track_boxes[kVideoIndex].media.header.timescale, kVideoTimescale);
+  EXPECT_EQ(track_boxes[kVideoIndex].media.header.language_code,
+            kUndefinedLanguageCode);
+
+  // Media Handler validation.
+  EXPECT_EQ(track_boxes[kVideoIndex].media.handler.type,
+            mp4::TrackType::kVideo);
+  EXPECT_EQ(track_boxes[kVideoIndex].media.handler.name, kVideoHandlerName);
+
+  EXPECT_EQ(track_boxes[kAudioIndex].media.handler.type,
+            mp4::TrackType::kAudio);
+  EXPECT_EQ(track_boxes[kAudioIndex].media.handler.name, kAudioHandlerName);
+
+  // Once Flush, it needs to reset the internal objects of context and buffer.
+  Reset();
+}
 }  // namespace media
diff --git a/media/muxers/mp4_muxer_context.cc b/media/muxers/mp4_muxer_context.cc
index 6648cb44..caf3b6b 100644
--- a/media/muxers/mp4_muxer_context.cc
+++ b/media/muxers/mp4_muxer_context.cc
@@ -4,6 +4,7 @@
 
 #include "media/muxers/mp4_muxer_context.h"
 
+#include "media/formats/mp4/writable_box_definitions.h"
 #include "media/muxers/output_position_tracker.h"
 
 namespace media {
diff --git a/media/muxers/mp4_muxer_context.h b/media/muxers/mp4_muxer_context.h
index bff7fc4..645ba884 100644
--- a/media/muxers/mp4_muxer_context.h
+++ b/media/muxers/mp4_muxer_context.h
@@ -5,6 +5,7 @@
 #ifndef MEDIA_MUXERS_MP4_MUXER_CONTEXT_H_
 #define MEDIA_MUXERS_MP4_MUXER_CONTEXT_H_
 
+#include <map>
 #include <memory>
 
 #include "base/sequence_checker.h"
@@ -54,6 +55,7 @@
   absl::optional<size_t> mdat_offset_in_fragment_;
 
   std::unique_ptr<OutputPositionTracker> output_position_tracker_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 };
 
diff --git a/media/muxers/mp4_muxer_context_unittest.cc b/media/muxers/mp4_muxer_context_unittest.cc
index a5c30074..791409f 100644
--- a/media/muxers/mp4_muxer_context_unittest.cc
+++ b/media/muxers/mp4_muxer_context_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
+#include "media/formats/mp4/writable_box_definitions.h"
 #include "media/muxers/mp4_muxer_context.h"
 #include "media/muxers/output_position_tracker.h"
 #include "testing/gtest/include/gtest/gtest.h"
diff --git a/media/muxers/mp4_type_conversion.cc b/media/muxers/mp4_type_conversion.cc
new file mode 100644
index 0000000..90e4632
--- /dev/null
+++ b/media/muxers/mp4_type_conversion.cc
@@ -0,0 +1,34 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/muxers/mp4_type_conversion.h"
+
+#include "base/check_op.h"
+
+namespace media {
+
+// Support ISO-639-2/T language.
+uint16_t ConvertIso639LanguageCodeToU16(const base::StringPiece language) {
+  // Handle undefined or unsupported format.
+  if (language.size() != 3) {
+    return kUndefinedLanguageCode;
+  }
+
+  // 5 bits ASCII.
+  uint16_t code = 0;
+  for (int i = 0; i < 3; i++) {
+    uint8_t c = language[i];
+    c -= 0x60;
+    if (c > 0x1f) {
+      // Invalid character, it will write as an undefined.
+      return kUndefinedLanguageCode;
+    }
+    code <<= 5;
+    code |= c;
+  }
+
+  return code;
+}
+
+}  // namespace media
diff --git a/media/muxers/mp4_type_conversion.h b/media/muxers/mp4_type_conversion.h
new file mode 100644
index 0000000..9096795
--- /dev/null
+++ b/media/muxers/mp4_type_conversion.h
@@ -0,0 +1,27 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_MUXERS_MP4_TYPE_CONVERSION_H_
+#define MEDIA_MUXERS_MP4_TYPE_CONVERSION_H_
+
+#include "base/strings/string_piece.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Convert to ISO time, seconds since midnight, Jan. 1, 1904, in UTC time.
+// base::Time time1904;
+// base::Time::FromUTCString("1904-01-01 00:00:00 UTC", &time1904);
+// 9561628800 = time1904.ToDeltaSinceWindowsEpoch().InSeconds();
+static constexpr int64_t k1601To1904DeltaInSeconds = INT64_C(9561628800);
+
+static constexpr uint16_t kUndefinedLanguageCode =
+    0x55C4;  // "und" on ISO 639-2/T.
+
+uint16_t MEDIA_EXPORT
+ConvertIso639LanguageCodeToU16(const base::StringPiece input_language);
+
+}  // namespace media
+
+#endif  // MEDIA_MUXERS_MP4_TYPE_CONVERSION_H_
diff --git a/media/muxers/mp4_type_conversion_unittest.cc b/media/muxers/mp4_type_conversion_unittest.cc
new file mode 100644
index 0000000..450a5eb
--- /dev/null
+++ b/media/muxers/mp4_type_conversion_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright 2023 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/muxers/mp4_type_conversion.h"
+
+#include "base/big_endian.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+struct LanguageAndCode {
+  std::string language;
+  uint16_t expected_code;
+};
+
+TEST(Mp4TypeConversionTest, Default) {
+  LanguageAndCode test_data[] = {
+      {"ara", 0x0641},
+      {"zho", 0x690f},
+      {"eng", 0x15c7},
+      {"fra", 0x1a41},
+      {"kor", 0x2df2},
+      {"und", kUndefinedLanguageCode},
+      {"", kUndefinedLanguageCode},
+      {"ENG", kUndefinedLanguageCode},
+      {"english", kUndefinedLanguageCode},
+  };
+
+  for (const auto& data : test_data) {
+    uint16_t language_code = ConvertIso639LanguageCodeToU16(data.language);
+    EXPECT_EQ(data.expected_code, language_code);
+  }
+}
+
+}  // namespace media
diff --git a/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
index cedb4c2f..7a4d54a 100644
--- a/skia/config/SkUserConfig.h
+++ b/skia/config/SkUserConfig.h
@@ -251,6 +251,9 @@
 /* Restrict formats for Skia font matching to SFNT type fonts. */
 #define SK_FONT_CONFIG_INTERFACE_ONLY_ALLOW_SFNT_FONTS
 
+// Temporarily enable new strike cache pinning logic, for staging.
+#define SK_STRIKE_CACHE_DOESNT_AUTO_CHECK_PINNERS
+
 #define SK_IGNORE_BLURRED_RRECT_OPT
 #define SK_USE_DISCARDABLE_SCALEDIMAGECACHE
 
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 973eab1..410a885f 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -10674,6 +10674,27 @@
             ]
         }
     ],
+    "PurgeOldCacheEntriesOnTimer": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_V2",
+                    "enable_features": [
+                        "PurgeOldCacheEntriesOnTimer"
+                    ]
+                }
+            ]
+        }
+    ],
     "PushMessagingDisallowSenderIDs": [
         {
             "platforms": [
@@ -13613,6 +13634,21 @@
             ]
         }
     ],
+    "UserInteractiveCompositingMac": [
+        {
+            "platforms": [
+                "mac"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "UserInteractiveCompositingMac"
+                    ]
+                }
+            ]
+        }
+    ],
     "UserLevelMemoryPressureSignalOn4GbDevices": [
         {
             "platforms": [
diff --git a/third_party/blink/renderer/bindings/core/v8/binding_security.cc b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
index 60d1bcec..cef68f7 100644
--- a/third_party/blink/renderer/bindings/core/v8/binding_security.cc
+++ b/third_party/blink/renderer/bindings/core/v8/binding_security.cc
@@ -301,26 +301,6 @@
                          reporting_option);
 }
 
-bool BindingSecurity::ShouldAllowAccessToFrame(
-    const LocalDOMWindow* accessing_window,
-    const Frame* target,
-    ExceptionState& exception_state) {
-  if (!target || !target->GetSecurityContext())
-    return false;
-  return CanAccessWindow(accessing_window, target->DomWindow(),
-                         exception_state);
-}
-
-bool BindingSecurity::ShouldAllowAccessToFrame(
-    const LocalDOMWindow* accessing_window,
-    const Frame* target,
-    ErrorReportOption reporting_option) {
-  if (!target || !target->GetSecurityContext())
-    return false;
-  return CanAccessWindow(accessing_window, target->DomWindow(),
-                         reporting_option);
-}
-
 namespace {
 
 template <typename ExceptionStateOrErrorReportOption>
@@ -344,30 +324,13 @@
   if (accessing_context == target_context)
     return true;
 
-  LocalFrame* target_frame = ToLocalFrameIfNotDetached(target_context);
-  // TODO(dcheng): Why doesn't this code just use DOMWindows throughout? Can't
-  // we just always use ToLocalDOMWindow(context)?
-  if (!target_frame) {
-    // Sandbox detached frames - they can't create cross origin objects.
-    LocalDOMWindow* accessing_window = ToLocalDOMWindow(accessing_context);
-    LocalDOMWindow* target_window = ToLocalDOMWindow(target_context);
-
-    // TODO(https://crbug.com/723057): This is tricky: this intentionally uses
-    // the internal CanAccessWindow() helper rather than ShouldAllowAccessTo().
-    // ShouldAllowAccessTo() unconditionally denies access if the DOMWindow is
-    // not attached to a Frame, but this code is intended for handling the
-    // detached DOMWindow case.
-    return CanAccessWindow(accessing_window, target_window, error_report);
-  }
-
   const DOMWrapperWorld& accessing_world =
       DOMWrapperWorld::World(accessing_context);
   const DOMWrapperWorld& target_world = DOMWrapperWorld::World(target_context);
   CHECK_EQ(accessing_world.GetWorldId(), target_world.GetWorldId());
-
   return !accessing_world.IsMainWorld() ||
-         BindingSecurity::ShouldAllowAccessToFrame(
-             ToLocalDOMWindow(accessing_context), target_frame, error_report);
+         CanAccessWindow(ToLocalDOMWindow(accessing_context),
+                         ToLocalDOMWindow(target_context), error_report);
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/bindings/core/v8/binding_security.h b/third_party/blink/renderer/bindings/core/v8/binding_security.h
index c628127..5d1d8e4d 100644
--- a/third_party/blink/renderer/bindings/core/v8/binding_security.h
+++ b/third_party/blink/renderer/bindings/core/v8/binding_security.h
@@ -40,7 +40,6 @@
 
 class DOMWindow;
 class ExceptionState;
-class Frame;
 class LocalDOMWindow;
 class Location;
 class Node;
@@ -92,16 +91,6 @@
   // These overloads should be used only when checking a general access from
   // one context to another context.  For access to a receiver object or
   // returned object, you should use the above overloads.
-  static bool ShouldAllowAccessToFrame(const LocalDOMWindow* accessing_window,
-                                       const Frame* target,
-                                       ExceptionState&);
-  static bool ShouldAllowAccessToFrame(const LocalDOMWindow* accessing_window,
-                                       const Frame* target,
-                                       ErrorReportOption);
-
-  // These overloads should be used only when checking a general access from
-  // one context to another context.  For access to a receiver object or
-  // returned object, you should use the above overloads.
   static bool ShouldAllowAccessToV8Context(
       v8::Local<v8::Context> accessing_context,
       v8::MaybeLocal<v8::Context> target_context,
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
index 7933a961..e563dc9 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_script_runner.cc
@@ -707,7 +707,6 @@
     v8::Local<v8::Value> argv[],
     v8::Isolate* isolate) {
   LocalDOMWindow* window = DynamicTo<LocalDOMWindow>(context);
-  LocalFrame* frame = window ? window->GetFrame() : nullptr;
   TRACE_EVENT0("v8", "v8.callFunction");
   RuntimeCallStatsScopedTracer rcs_scoped_tracer(isolate);
   RUNTIME_CALL_TIMER_SCOPE(isolate, RuntimeCallStats::CounterId::kV8);
@@ -729,10 +728,10 @@
     DCHECK(!ScriptForbiddenScope::WillBeScriptForbidden());
   }
 
-  DCHECK(!frame ||
-         BindingSecurity::ShouldAllowAccessToFrame(
-             ToLocalDOMWindow(function->GetCreationContextChecked()), frame,
-             BindingSecurity::ErrorReportOption::kDoNotReport));
+  CHECK(!window || !window->GetFrame() ||
+        BindingSecurity::ShouldAllowAccessTo(
+            ToLocalDOMWindow(function->GetCreationContextChecked()), window,
+            BindingSecurity::ErrorReportOption::kDoNotReport));
   v8::Isolate::SafeForTerminationScope safe_for_termination(isolate);
   v8::MicrotasksScope microtasks_scope(isolate, microtask_queue,
                                        v8::MicrotasksScope::kRunMicrotasks);
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_data.cc b/third_party/blink/renderer/core/animation/css/css_animation_data.cc
index 2157468f..401fddb 100644
--- a/third_party/blink/renderer/core/animation/css/css_animation_data.cc
+++ b/third_party/blink/renderer/core/animation/css/css_animation_data.cc
@@ -5,10 +5,11 @@
 #include "third_party/blink/renderer/core/animation/css/css_animation_data.h"
 
 #include "third_party/blink/renderer/core/animation/timing.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 
 namespace blink {
 
-CSSAnimationData::CSSAnimationData() {
+CSSAnimationData::CSSAnimationData() : CSSTimingData(InitialDuration()) {
   name_list_.push_back(InitialName());
   timeline_list_.push_back(InitialTimeline());
   iteration_count_list_.push_back(InitialIterationCount());
@@ -22,6 +23,13 @@
 
 CSSAnimationData::CSSAnimationData(const CSSAnimationData& other) = default;
 
+absl::optional<double> CSSAnimationData::InitialDuration() {
+  if (RuntimeEnabledFeatures::ScrollTimelineEnabled()) {
+    return absl::nullopt;
+  }
+  return 0;
+}
+
 const AtomicString& CSSAnimationData::InitialName() {
   DEFINE_STATIC_LOCAL(const AtomicString, name, ("none"));
   return name;
diff --git a/third_party/blink/renderer/core/animation/css/css_animation_data.h b/third_party/blink/renderer/core/animation/css/css_animation_data.h
index 13c2eef..d74eccf 100644
--- a/third_party/blink/renderer/core/animation/css/css_animation_data.h
+++ b/third_party/blink/renderer/core/animation/css/css_animation_data.h
@@ -85,6 +85,7 @@
     return composition_list_;
   }
 
+  static absl::optional<double> InitialDuration();
   static const AtomicString& InitialName();
   static const StyleTimeline& InitialTimeline();
   static Timing::PlaybackDirection InitialDirection() {
diff --git a/third_party/blink/renderer/core/animation/css/css_timing_data.cc b/third_party/blink/renderer/core/animation/css/css_timing_data.cc
index d506f22..b72826a3 100644
--- a/third_party/blink/renderer/core/animation/css/css_timing_data.cc
+++ b/third_party/blink/renderer/core/animation/css/css_timing_data.cc
@@ -6,10 +6,10 @@
 
 namespace blink {
 
-CSSTimingData::CSSTimingData() {
+CSSTimingData::CSSTimingData(absl::optional<double> initial_duration) {
   delay_start_list_.push_back(InitialDelayStart());
   delay_end_list_.push_back(InitialDelayEnd());
-  duration_list_.push_back(InitialDuration());
+  duration_list_.push_back(initial_duration);
   timing_function_list_.push_back(InitialTimingFunction());
 }
 
diff --git a/third_party/blink/renderer/core/animation/css/css_timing_data.h b/third_party/blink/renderer/core/animation/css/css_timing_data.h
index 65fa20a..c9edbde 100644
--- a/third_party/blink/renderer/core/animation/css/css_timing_data.h
+++ b/third_party/blink/renderer/core/animation/css/css_timing_data.h
@@ -42,7 +42,6 @@
 
   static Timing::Delay InitialDelayStart() { return Timing::Delay(); }
   static Timing::Delay InitialDelayEnd() { return Timing::Delay(); }
-  static absl::optional<double> InitialDuration() { return 0; }
   static scoped_refptr<TimingFunction> InitialTimingFunction() {
     return CubicBezierTimingFunction::Preset(
         CubicBezierTimingFunction::EaseType::EASE);
@@ -54,8 +53,8 @@
   }
 
  protected:
-  CSSTimingData();
-  explicit CSSTimingData(const CSSTimingData&);
+  explicit CSSTimingData(absl::optional<double> initial_duration);
+  CSSTimingData(const CSSTimingData&);
 
   Timing ConvertToTiming(size_t index) const;
   bool TimingMatchForStyleRecalc(const CSSTimingData&) const;
diff --git a/third_party/blink/renderer/core/animation/css/css_transition_data.cc b/third_party/blink/renderer/core/animation/css/css_transition_data.cc
index b0a95876..34383a4f 100644
--- a/third_party/blink/renderer/core/animation/css/css_transition_data.cc
+++ b/third_party/blink/renderer/core/animation/css/css_transition_data.cc
@@ -8,7 +8,7 @@
 
 namespace blink {
 
-CSSTransitionData::CSSTransitionData() {
+CSSTransitionData::CSSTransitionData() : CSSTimingData(InitialDuration()) {
   property_list_.push_back(InitialProperty());
 }
 
diff --git a/third_party/blink/renderer/core/animation/css/css_transition_data.h b/third_party/blink/renderer/core/animation/css/css_transition_data.h
index 0c86ce7..7217fae6 100644
--- a/third_party/blink/renderer/core/animation/css/css_transition_data.h
+++ b/third_party/blink/renderer/core/animation/css/css_transition_data.h
@@ -72,6 +72,8 @@
   }
   Vector<TransitionProperty>& PropertyList() { return property_list_; }
 
+  static absl::optional<double> InitialDuration() { return 0; }
+
   static TransitionProperty InitialProperty() {
     return TransitionProperty(CSSPropertyID::kAll);
   }
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
index 99eec6a02..e25fcfa 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -2230,11 +2230,21 @@
 }
 
 CSSValue* ComputedStyleUtils::ValueForAnimationDurationList(
-    const CSSTimingData* timing_data) {
+    const CSSAnimationData* animation_data) {
   return CreateAnimationValueList(
-      timing_data
-          ? timing_data->DurationList()
-          : Vector<absl::optional<double>>{CSSTimingData::InitialDuration()},
+      animation_data
+          ? animation_data->DurationList()
+          : Vector<absl::optional<double>>{CSSAnimationData::InitialDuration()},
+      &ValueForAnimationDuration);
+}
+
+CSSValue* ComputedStyleUtils::ValueForAnimationDurationList(
+    const CSSTransitionData* transition_data) {
+  return CreateAnimationValueList(
+      transition_data
+          ? transition_data->DurationList()
+          : Vector<
+                absl::optional<double>>{CSSTransitionData::InitialDuration()},
       &ValueForAnimationDuration);
 }
 
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.h b/third_party/blink/renderer/core/css/properties/computed_style_utils.h
index 1f76dc1..fd7d4f0 100644
--- a/third_party/blink/renderer/core/css/properties/computed_style_utils.h
+++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.h
@@ -173,7 +173,8 @@
   static CSSValue* ValueForAnimationDelayStartList(const CSSTimingData*);
   static CSSValue* ValueForAnimationDelayEndList(const CSSTimingData*);
   static CSSValue* ValueForAnimationDirectionList(const CSSAnimationData*);
-  static CSSValue* ValueForAnimationDurationList(const CSSTimingData*);
+  static CSSValue* ValueForAnimationDurationList(const CSSAnimationData*);
+  static CSSValue* ValueForAnimationDurationList(const CSSTransitionData*);
   static CSSValue* ValueForAnimationFillModeList(const CSSAnimationData*);
   static CSSValue* ValueForAnimationIterationCountList(const CSSAnimationData*);
   static CSSValue* ValueForAnimationPlayStateList(const CSSAnimationData*);
diff --git a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
index edd6868..a1595ad 100644
--- a/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
+++ b/third_party/blink/renderer/core/css/properties/longhands/longhands_custom.cc
@@ -361,11 +361,8 @@
 }
 
 const CSSValue* AnimationDuration::InitialValue() const {
-  DEFINE_STATIC_LOCAL(
-      const Persistent<CSSValue>, value,
-      (CSSNumericLiteralValue::Create(CSSTimingData::InitialDuration().value(),
-                                      CSSPrimitiveValue::UnitType::kSeconds)));
-  return value;
+  return ComputedStyleUtils::ValueForAnimationDuration(
+      CSSAnimationData::InitialDuration());
 }
 
 const CSSValue* AnimationFillMode::ParseSingleValue(
@@ -8414,10 +8411,10 @@
 }
 
 const CSSValue* TransitionDuration::InitialValue() const {
-  DEFINE_STATIC_LOCAL(
-      const Persistent<CSSValue>, value,
-      (CSSNumericLiteralValue::Create(CSSTimingData::InitialDuration().value(),
-                                      CSSPrimitiveValue::UnitType::kSeconds)));
+  DEFINE_STATIC_LOCAL(const Persistent<CSSValue>, value,
+                      (CSSNumericLiteralValue::Create(
+                          CSSTransitionData::InitialDuration().value(),
+                          CSSPrimitiveValue::UnitType::kSeconds)));
   return value;
 }
 
diff --git a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
index f06b7ca..209d90e5 100644
--- a/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_cascade_test.cc
@@ -2049,7 +2049,7 @@
   cascade.Apply();
 
   EXPECT_EQ("20s", cascade.ComputedValue("--y"));
-  EXPECT_EQ("0s", cascade.ComputedValue("animation-duration"));
+  EXPECT_EQ("auto", cascade.ComputedValue("animation-duration"));
 }
 
 TEST_F(StyleCascadeTest, IndirectlyAnimationTainted) {
@@ -2061,7 +2061,7 @@
 
   EXPECT_EQ("20s", cascade.ComputedValue("--x"));
   EXPECT_EQ("20s", cascade.ComputedValue("--y"));
-  EXPECT_EQ("0s", cascade.ComputedValue("animation-duration"));
+  EXPECT_EQ("auto", cascade.ComputedValue("animation-duration"));
 }
 
 TEST_F(StyleCascadeTest, AnimationTaintedFallback) {
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index b34c131..579b070 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -6073,6 +6073,64 @@
                RuntimeEnabledFeatures::CSSAnimationDelayStartEndEnabled());
 }
 
+TEST_F(StyleEngineTest, AnimationDurationInitialValueWithScrollTimeline) {
+  ScopedScrollTimelineForTest scroll_timeline_enabled(true);
+
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style>
+      #target1 { /* Implicitly initial */ }
+      #target2 { animation-duration: initial; }
+      #target3 { animation: foo; }
+    </style>
+    <div id=target1></div>
+    <div id=target2></div>
+    <div id=target3></div>
+  )HTML");
+
+  UpdateAllLifecyclePhasesForTest();
+
+  Element* target1 = GetDocument().getElementById("target1");
+  Element* target2 = GetDocument().getElementById("target2");
+  Element* target3 = GetDocument().getElementById("target3");
+
+  ASSERT_TRUE(target1);
+  ASSERT_TRUE(target2);
+  ASSERT_TRUE(target3);
+
+  EXPECT_EQ("auto", ComputedValue(target1, "animation-duration")->CssText());
+  EXPECT_EQ("auto", ComputedValue(target2, "animation-duration")->CssText());
+  EXPECT_EQ("auto", ComputedValue(target3, "animation-duration")->CssText());
+}
+
+TEST_F(StyleEngineTest, AnimationDurationInitialValueWithoutScrollTimeline) {
+  ScopedScrollTimelineForTest scroll_timeline_enabled(false);
+
+  GetDocument().body()->setInnerHTML(R"HTML(
+    <style>
+      #target1 { /* Implicitly initial */ }
+      #target2 { animation-duration: initial; }
+      #target3 { animation: foo; }
+    </style>
+    <div id=target1></div>
+    <div id=target2></div>
+    <div id=target3></div>
+  )HTML");
+
+  UpdateAllLifecyclePhasesForTest();
+
+  Element* target1 = GetDocument().getElementById("target1");
+  Element* target2 = GetDocument().getElementById("target2");
+  Element* target3 = GetDocument().getElementById("target3");
+
+  ASSERT_TRUE(target1);
+  ASSERT_TRUE(target2);
+  ASSERT_TRUE(target3);
+
+  EXPECT_EQ("0s", ComputedValue(target1, "animation-duration")->CssText());
+  EXPECT_EQ("0s", ComputedValue(target2, "animation-duration")->CssText());
+  EXPECT_EQ("0s", ComputedValue(target3, "animation-duration")->CssText());
+}
+
 TEST_F(StyleEngineTest, InitialStyle_Recalc) {
   GetDocument().body()->setInnerHTML(R"HTML(
     <style>
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 7ebcc1c..eec6aa3 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -1194,9 +1194,10 @@
 }
 
 bool WebFrame::ScriptCanAccess(WebFrame* target) {
-  return BindingSecurity::ShouldAllowAccessToFrame(
+  return BindingSecurity::ShouldAllowAccessTo(
       CurrentDOMWindow(V8PerIsolateData::MainThreadIsolate()),
-      ToCoreFrame(*target), BindingSecurity::ErrorReportOption::kDoNotReport);
+      ToCoreFrame(*target)->DomWindow(),
+      BindingSecurity::ErrorReportOption::kDoNotReport);
 }
 
 void WebLocalFrameImpl::StartReload(WebFrameLoadType frame_load_type) {
diff --git a/third_party/blink/renderer/core/html/html_frame_element_base.cc b/third_party/blink/renderer/core/html/html_frame_element_base.cc
index ca9855c..9d72e50 100644
--- a/third_party/blink/renderer/core/html/html_frame_element_base.cc
+++ b/third_party/blink/renderer/core/html/html_frame_element_base.cc
@@ -63,7 +63,7 @@
 
   const KURL& complete_url = GetDocument().CompleteURL(url_);
 
-  if (ContentFrame() && complete_url.ProtocolIsJavaScript()) {
+  if (contentWindow() && complete_url.ProtocolIsJavaScript()) {
     // Check if the caller can execute script in the context of the content
     // frame. NB: This check can be invoked without any JS on the stack for some
     // parser operations. In such case, we use the origin of the frame element's
@@ -72,10 +72,11 @@
     LocalDOMWindow* accessing_window = isolate->InContext()
                                            ? CurrentDOMWindow(isolate)
                                            : GetDocument().domWindow();
-    if (!BindingSecurity::ShouldAllowAccessToFrame(
-            accessing_window, ContentFrame(),
-            BindingSecurity::ErrorReportOption::kReport))
+    if (!BindingSecurity::ShouldAllowAccessTo(
+            accessing_window, contentWindow(),
+            BindingSecurity::ErrorReportOption::kReport)) {
       return false;
+    }
   }
   return true;
 }
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
index 3af639a..38cdea5 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.cc
@@ -560,50 +560,70 @@
   return has_missable_children;
 }
 
-void PrePaintTreeWalk::RebuildContextForMissedDescendant(
-    const LayoutObject& ancestor,
+const NGPhysicalBoxFragment*
+PrePaintTreeWalk::RebuildContextForMissedDescendant(
+    const NGPhysicalBoxFragment& ancestor,
     const LayoutObject& object,
     bool update_tree_builder_context,
     PrePaintTreeWalkContext& context) {
   // Walk up to the ancestor and, on the way down again, adjust the context with
   // info about OOF containing blocks.
-  if (&object == &ancestor)
-    return;
-  RebuildContextForMissedDescendant(ancestor, *object.Parent(),
-                                    update_tree_builder_context, context);
+  if (&object == ancestor.OwnerLayoutBox()) {
+    return &ancestor;
+  }
+  const NGPhysicalBoxFragment* search_fragment =
+      RebuildContextForMissedDescendant(ancestor, *object.Parent(),
+                                        update_tree_builder_context, context);
 
   if (object.IsLayoutFlowThread()) {
     // A flow threads doesn't create fragments. Just ignore it.
-    return;
+    return search_fragment;
   }
 
+  const NGPhysicalBoxFragment* box_fragment = nullptr;
   if (context.tree_builder_context && update_tree_builder_context) {
-    // TODO(crbug.com/1442211): Attempt to find the right physical fragment for
-    // the ancestor (rather than using nullptr), because there are cases where
-    // we'll miss a descendant, even though the ancestor is represented in the
-    // current fragmentainer.
-    //
-    // E.g.:
-    //   <div style="columns:3; column-fill:auto; height:100px;">
-    //     <div id="outer" style="height:300px; transform:rotate(10deg);">
-    //       <div id="middle">
-    //         <div id="abspos" style="position:absolute; top:250px;">
-    //
-    // #middle only exists in the first fragmentainer, whereas #outer and
-    // #abspos exist in three. We're going to miss #abspos in the second and
-    // third fragmentainer, since it's a child of #middle, which doesn't exist
-    // in those fragmentainers. Providing the correct fragment is important for
-    // transforms, and especially for clipping.
-    const NGPhysicalBoxFragment* box_fragment = nullptr;
+    PhysicalOffset paint_offset;
     wtf_size_t fragmentainer_idx = context.current_container.fragmentainer_idx;
 
+    // TODO(mstensho): We're doing a simplified version of what
+    // WalkLayoutObjectChildren() does. Consider refactoring so that we can
+    // share.
+    if (object.IsOutOfFlowPositioned()) {
+      // The fragment tree follows the structure of containing blocks closely,
+      // while here we're walking down the LayoutObject tree spine (which
+      // follows the structure of the flat DOM tree, more or less). This means
+      // that for out-of-flow positioned objects, the fragment of the parent
+      // LayoutObject might not be the right place to search.
+      const ContainingFragment& oof_containing_fragment_info =
+          object.IsFixedPositioned() ? context.fixed_positioned_container
+                                     : context.absolute_positioned_container;
+      search_fragment = oof_containing_fragment_info.fragment;
+      fragmentainer_idx = oof_containing_fragment_info.fragmentainer_idx;
+    }
+    // If we have a parent fragment to search inside, do that. If we find it, we
+    // can use its paint offset and size in the paint property builder. If we
+    // have no parent fragment, or don't find the child, we won't be passing a
+    // fragment to the property builder, and then it needs to behave
+    // accordingly, e.g. assume that the fragment is at the fragmentainer
+    // origin, and has zero block-size.
+    // See e.g. https://www.w3.org/TR/css-break-3/#transforms
+    if (search_fragment) {
+      for (NGLink link : search_fragment->Children()) {
+        if (link->GetLayoutObject() == object) {
+          box_fragment = To<NGPhysicalBoxFragment>(link.get());
+          paint_offset = link.offset;
+          break;
+        }
+      }
+    }
+
     // TODO(mstensho): Some of the bool parameters here are meaningless when
     // only used with PaintPropertyTreeBuilder (only used by
     // PrePaintTreeWalker). Consider cleaning this up, by splitting up
     // NGPrePaintInfo into one walker part and one builder part, so that we
     // don't have to specify them as false here.
     NGPrePaintInfo pre_paint_info(
-        box_fragment, PhysicalOffset(), fragmentainer_idx,
+        box_fragment, paint_offset, fragmentainer_idx,
         /* is_first_for_node */ false, /* is_last_for_node */ false,
         /* is_inside_fragment_child */ false,
         context.current_container.IsInFragmentationContext());
@@ -635,14 +655,11 @@
     builder_context.force_subtree_update_reasons = original_force_update;
   }
 
-  // We don't need to pass a fragment here, since we're not actually going to
-  // search for any descendant fragment. We've already determined which fragment
-  // that we're going to visit (the one we missed), since we're here.
-  UpdateContextForOOFContainer(object, context, /* fragment */ nullptr);
+  UpdateContextForOOFContainer(object, context, box_fragment);
 
   if (!object.CanContainAbsolutePositionObjects() ||
       !context.tree_builder_context) {
-    return;
+    return box_fragment;
   }
 
   PaintPropertyTreeBuilderContext& property_context =
@@ -657,10 +674,11 @@
     property_context.container_for_fixed_position = &object;
     fragment_context.fixed_position = fragment_context.current;
   }
+
+  return box_fragment;
 }
 
 void PrePaintTreeWalk::WalkMissedChildren(
-    const LayoutObject& ancestor,
     const NGPhysicalBoxFragment& fragment,
     const PrePaintTreeWalkContext& context) {
   if (pending_missables_.empty())
@@ -701,7 +719,7 @@
           RuntimeEnabledFeatures::PrePaintAncestorsOfMissedOOFEnabled() &&
           NeedsTreeBuilderContextUpdate(descendant_object, descendant_context);
 
-      RebuildContextForMissedDescendant(ancestor, *descendant_object.Parent(),
+      RebuildContextForMissedDescendant(fragment, *descendant_object.Parent(),
                                         update_tree_builder_context,
                                         descendant_context);
     }
@@ -1142,8 +1160,9 @@
     WalkLayoutObjectChildren(object, traversable_fragment, context);
   }
 
-  if (has_missable_children)
-    WalkMissedChildren(object, *fragment, context);
+  if (has_missable_children) {
+    WalkMissedChildren(*fragment, context);
+  }
 }
 
 void PrePaintTreeWalk::Walk(const LayoutObject& object,
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h
index 3b4d52bd..2055ea267 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk.h
@@ -183,17 +183,29 @@
   bool CollectMissableChildren(PrePaintTreeWalkContext&,
                                const NGPhysicalBoxFragment&);
 
-  // Based on the context established by |ancestor|, modify it to become correct
-  // for |object|, at least as far as OOF containing block info is concerned.
-  void RebuildContextForMissedDescendant(const LayoutObject& ancestor,
-                                         const LayoutObject& object,
-                                         bool update_tree_builder_context,
-                                         PrePaintTreeWalkContext&);
+  // Based on the context established by |ancestor|, modify it to become as
+  // correct as possible for |object|. Any object between the ancestor and the
+  // target object may have paint effects that would be missed otherwise.
+  //
+  // This function will start by walking up to the ancestor recursively, and
+  // then build whatever it can on the way down again. If a physical fragment is
+  // returned, this will be the parent fragment of the next child, so that we
+  // can search for a fragment for the child right there. If the child is
+  // out-of-flow positioned, it will need to locate the correct containing
+  // fragment via other means, though. If it's nullptr, it means that no
+  // fragment exists for the parent (i.e. the node isn't represented in this
+  // fragmentainer), and we need to behave according to specs (assume that a
+  // transform origin is based on a zero-block-size box, zero clip rectangle
+  // size, etc.)
+  const NGPhysicalBoxFragment* RebuildContextForMissedDescendant(
+      const NGPhysicalBoxFragment& ancestor,
+      const LayoutObject& object,
+      bool update_tree_builder_context,
+      PrePaintTreeWalkContext&);
 
   // Walk any missed children (i.e. those collected by CollectMissableChildren()
   // and not walked by Walk()) after child object traversal.
-  void WalkMissedChildren(const LayoutObject& ancestor,
-                          const NGPhysicalBoxFragment&,
+  void WalkMissedChildren(const NGPhysicalBoxFragment&,
                           const PrePaintTreeWalkContext&);
 
   void WalkFragmentationContextRootChildren(const LayoutObject&,
diff --git a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
index 10316ae..241ce16c 100644
--- a/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
+++ b/third_party/blink/renderer/modules/indexeddb/inspector_indexed_db_agent.cc
@@ -127,8 +127,7 @@
         inspected_frames->FrameWithSecurityOrigin(security_origin.fromJust());
   }
   if (!frame) {
-    return base::unexpected(
-        protocol::Response::InvalidParams("specified storage not found"));
+    return base::unexpected(protocol::Response::ServerError(kNoDocumentError));
   }
   return frame;
 }
diff --git a/third_party/blink/renderer/platform/graphics/color.cc b/third_party/blink/renderer/platform/graphics/color.cc
index 3c635585..7b98ce6 100644
--- a/third_party/blink/renderer/platform/graphics/color.cc
+++ b/third_party/blink/renderer/platform/graphics/color.cc
@@ -795,7 +795,7 @@
     case Color::ColorSpace::kLch:
       return "lch";
     case Color::ColorSpace::kOklch:
-      return "oklab";
+      return "oklch";
     case Color::ColorSpace::kSRGBLegacy:
       return "RGB Legacy";
     case Color::ColorSpace::kHSL:
@@ -809,7 +809,6 @@
 }
 
 String Color::SerializeAsCanvasColor() const {
-  // Opaque legacy colors will serialize in a hex format.
   if (IsOpaque() && IsLegacyColor()) {
     return String::Format("#%02x%02x%02x", Red(), Green(), Blue());
   }
@@ -817,145 +816,95 @@
   return SerializeAsCSSColor();
 }
 
-String Color::SerializeAsCSSColor() const {
+static String SerializeLegacyColorAsCSSColor(SkColor4f color) {
+  auto ColorChannelToInt = [](float x) {
+    // Channels that have a value of exactly 0.5 can get incorrectly rounded
+    // down to 127 when being converted to an integer. Add a small epsilon to
+    // avoid this. See crbug.com/1425856.
+    float epsilon = 1e-07;
+    return ClampTo(round((x + epsilon) * 255), 0, 255);
+  };
+
   StringBuilder result;
   result.ReserveCapacity(28);
-
-  switch (color_space_) {
-    case ColorSpace::kSRGBLegacy:
-    case ColorSpace::kHSL:
-    case ColorSpace::kHWB:
-      if (HasTransparency()) {
-        result.Append("rgba(");
-      } else {
-        result.Append("rgb(");
-      }
-
-      result.AppendNumber(Red());
-      result.Append(", ");
-      result.AppendNumber(Green());
-      result.Append(", ");
-      result.AppendNumber(Blue());
-
-      if (HasTransparency()) {
-        result.Append(", ");
-        // See <alphavalue> section in
-        // https://drafts.csswg.org/cssom/#serializing-css-values
-        float rounded = round(AlphaAsInteger() * 100 / 255.0f) / 100;
-        if (round(rounded * 255) == AlphaAsInteger()) {
-          result.AppendNumber(rounded, 2);
-        } else {
-          rounded = round(AlphaAsInteger() * 1000 / 255.0f) / 1000;
-          result.AppendNumber(rounded, 3);
-        }
-      }
-
-      result.Append(')');
-      return result.ToString();
-
-    case ColorSpace::kLab:
-    case ColorSpace::kOklab:
-    case ColorSpace::kLch:
-    case ColorSpace::kOklch:
-      if (color_space_ == ColorSpace::kLab)
-        result.Append("lab(");
-      if (color_space_ == ColorSpace::kOklab)
-        result.Append("oklab(");
-      if (color_space_ == ColorSpace::kLch)
-        result.Append("lch(");
-      if (color_space_ == ColorSpace::kOklch)
-        result.Append("oklch(");
-
-      if (param0_is_none_) {
-        result.Append("none ");
-      } else {
-        // Lightness value in Oklab and Oklch is considered as 0.0 - 1.0 while
-        // we store it internally as 0.0 - 100.0.
-        result.AppendNumber(param0_ /
-                            (color_space_ == ColorSpace::kOklab ||
-                                     color_space_ == ColorSpace::kOklch
-                                 ? 100.0f
-                                 : 1.0f));
-        result.Append(" ");
-      }
-
-      if (param1_is_none_)
-        result.Append("none");
-      else
-        result.AppendNumber(param1_);
-      result.Append(" ");
-
-      if (param2_is_none_)
-        result.Append("none");
-      else
-        result.AppendNumber(param2_);
-
-      if (alpha_ != 1.0 || alpha_is_none_) {
-        result.Append(" / ");
-        if (alpha_is_none_)
-          result.Append("none");
-        else
-          result.AppendNumber(alpha_);
-      }
-      result.Append(")");
-      return result.ToString();
-
-    case ColorSpace::kSRGB:
-    case ColorSpace::kSRGBLinear:
-    case ColorSpace::kDisplayP3:
-    case ColorSpace::kA98RGB:
-    case ColorSpace::kProPhotoRGB:
-    case ColorSpace::kRec2020:
-    case ColorSpace::kXYZD50:
-    case ColorSpace::kXYZD65:
-      result.Append("color(");
-      result.Append(ColorSpaceToString(color_space_));
-
-      result.Append(" ");
-      if (param0_is_none_)
-        result.Append("none");
-      else
-        result.AppendNumber(param0_);
-
-      result.Append(" ");
-      if (param1_is_none_)
-        result.Append("none");
-      else
-        result.AppendNumber(param1_);
-
-      result.Append(" ");
-      if (param2_is_none_)
-        result.Append("none");
-      else
-        result.AppendNumber(param2_);
-
-      if (alpha_ != 1.0 || alpha_is_none_) {
-        result.Append(" / ");
-        if (alpha_is_none_)
-          result.Append("none");
-        else
-          result.AppendNumber(alpha_);
-      }
-      result.Append(")");
-      return result.ToString();
-
-    default:
-      NOTIMPLEMENTED();
-      return "rgb(0, 0, 0)";
+  bool has_transparency = !color.isOpaque();
+  if (has_transparency) {
+    result.Append("rgba(");
+  } else {
+    result.Append("rgb(");
   }
+
+  result.AppendNumber(ColorChannelToInt(color.fR));
+  result.Append(", ");
+  result.AppendNumber(ColorChannelToInt(color.fG));
+  result.Append(", ");
+  result.AppendNumber(ColorChannelToInt(color.fB));
+
+  if (has_transparency) {
+    result.Append(", ");
+    // See <alphavalue> section in
+    // https://drafts.csswg.org/cssom/#serializing-css-values
+    int int_alpha = ColorChannelToInt(color.fA);
+    float rounded = round(int_alpha * 100 / 255.0f) / 100;
+    if (round(rounded * 255) == int_alpha) {
+      result.AppendNumber(rounded, 2);
+    } else {
+      rounded = round(int_alpha * 1000 / 255.0f) / 1000;
+      result.AppendNumber(rounded, 3);
+    }
+  }
+
+  result.Append(')');
+  return result.ToString();
+}
+
+String Color::SerializeAsCSSColor() const {
+  if (IsLegacyColor()) {
+    return SerializeLegacyColorAsCSSColor(toSkColor4f());
+  }
+
+  StringBuilder result;
+  result.ReserveCapacity(28);
+  if (color_space_ == ColorSpace::kLab || color_space_ == ColorSpace::kOklab ||
+      color_space_ == ColorSpace::kLch || color_space_ == ColorSpace::kOklch) {
+    result.Append(ColorSpaceToString(color_space_));
+    result.Append("(");
+  } else {
+    result.Append("color(");
+    result.Append(ColorSpaceToString(color_space_));
+    result.Append(" ");
+  }
+
+  float p0 = param0_;
+  if (color_space_ == ColorSpace::kOklab ||
+      color_space_ == ColorSpace::kOklch) {
+    p0 /= 100.0f;
+  }
+
+  param0_is_none_ ? result.Append("none") : result.AppendNumber(p0);
+  result.Append(" ");
+  param1_is_none_ ? result.Append("none") : result.AppendNumber(param1_);
+  result.Append(" ");
+  param2_is_none_ ? result.Append("none") : result.AppendNumber(param2_);
+
+  if (alpha_ != 1.0 || alpha_is_none_) {
+    result.Append(" / ");
+    alpha_is_none_ ? result.Append("none") : result.AppendNumber(alpha_);
+  }
+  result.Append(")");
+  return result.ToString();
 }
 
 String Color::NameForLayoutTreeAsText() const {
-  if (color_space_ != ColorSpace::kSRGBLegacy &&
-      color_space_ != ColorSpace::kHSL && color_space_ != ColorSpace::kHWB) {
-    // TODO(https://crbug.com/1333988): Determine if CSS Color Level 4 colors
-    // should use this representation here.
+  if (!IsLegacyColor()) {
     return SerializeAsCSSColor();
   }
-  if (AlphaAsInteger() < 0xFF) {
+
+  if (HasTransparency()) {
     return String::Format("#%02X%02X%02X%02X", Red(), Green(), Blue(),
                           AlphaAsInteger());
   }
+
   return String::Format("#%02X%02X%02X", Red(), Green(), Blue());
 }
 
@@ -1014,7 +963,7 @@
 
 Color Color::Blend(const Color& source) const {
   // TODO(https://crbug.com/1333988): Implement CSS Color level 4 blending.
-  if (!AlphaAsInteger() || source.IsOpaque()) {
+  if (IsFullyTransparent() || source.IsOpaque()) {
     return source;
   }
 
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index cf80aa8..7d2cca5 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -452,7 +452,7 @@
     },
     {
       name: "BeforeunloadEventCancelByPreventDefault",
-      status: "stable",
+      status: "experimental",
     },
     {
       name: "BidiCaretAffinity",
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index d52278e0..065b6b10 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -1546,3 +1546,5 @@
 
 # Slow because of too many cycles of waiting for layout and paint
 crbug.com/1310326 http/tests/misc/transforms-usecounter-3d-scene.html [ Slow ]
+
+crbug.com/1307741 external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 4753eb8..cb2c768 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -5561,7 +5561,6 @@
 crbug.com/1298633 http/tests/inspector-protocol/network/cross-origin-isolation/coep-load-error-reporting-worker.js [ Pass Timeout ]
 
 # WebAudio flaky timeout (crbug.com/1307741)
-crbug.com/1307741 [ Mac ] external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/mediastreamaudiosourcenode-routing.html [ Timeout ]
 crbug.com/1307741 webaudio/AudioParam/audioparam-k-rate.html [ Timeout ]
 
 # WebAudio internals flaky GC (crbug.com/1312164): See the bug before removing the "Skip" label.
@@ -6789,12 +6788,6 @@
 crbug.com/1432145 [ Mac10.14 ] virtual/feature-policy-permissions/external/wpt/mediacapture-streams/MediaStreamTrack-MediaElement-disabled-video-is-black.https.html [ Failure Pass ]
 crbug.com/1441416 [ Linux ] external/wpt/js-self-profiling/max-buffer-size.window.html [ Crash Pass ]
 
-# Sheriff 2023-05-02
-crbug.com/1441166 [ Mac ] http/tests/devtools/indexeddb/resources-panel.js [ Failure Pass ]
-crbug.com/1441166 [ Linux ] http/tests/devtools/indexeddb/resources-panel.js [ Failure Pass ]
-# Sheriff 2023-05-08
-crbug.com/1441166 [ Win ] http/tests/devtools/indexeddb/resources-panel.js [ Failure Pass ]
-
 # Sheriff 2023-05-04
 crbug.com/1434164 http/tests/streams/chromium/transferable-streams-optimization.html [ Crash Failure Pass ]
 
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 160f2fc..1af1386 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -250,6 +250,8 @@
              "--force-raster-color-profile=color-spin-gamma24"],
     "expires": "Jul 1, 2023"
   },
+  "The stable suite tests for conformance to HTML/web specifications, and",
+  "detects unintentional changes to web exposed surface, so should not expire.",
   {
     "prefix": "stable",
     "platforms": ["Linux", "Mac", "Win", "Fuchsia"],
@@ -269,7 +271,7 @@
     "args": ["--stable-release-mode",
              "--disable-auto-wpt-origin-isolation",
              "--disable-field-trial-config"],
-    "expires": "Jul 1, 2023"
+    "expires": "never"
   },
   {
     "prefix": "feature-policy-permissions",
@@ -280,13 +282,15 @@
              "--use-fake-ui-for-media-stream"],
     "expires": "Jul 1, 2023"
   },
+  "The origin trials suite tests that OT-controlled features are exposed to",
+  "the web as expected, so should not expire.",
   {
     "prefix": "origin-trials-runtimeflags-disabled",
     "platforms": ["Linux", "Mac", "Win"],
     "bases": ["http/tests/origin_trials/webexposed"],
     "args": ["--disable-origin-trial-controlled-blink-features",
              "--stable-release-mode"],
-    "expires": "Jul 1, 2023"
+    "expires": "never"
   },
   {
     "prefix": "android",
@@ -1691,7 +1695,7 @@
     ],
     "exclusive_tests": "ALL",
     "args": [
-      "--enable-blink-features=SpeculationRulesPrefetchProxy,SpeculationRulesFetchFromHeader,SpeculationRulesDocumentRules,NoVarySearchPrefetch,SpeculationRulesEagerness,SpeculationRulesDocumentRulesSelectorMatches",
+      "--enable-blink-features=SpeculationRulesPrefetchProxy,SpeculationRulesFetchFromHeader,SpeculationRulesDocumentRules,NoVarySearchPrefetch,SpeculationRulesNoVarySearchHint,SpeculationRulesEagerness,SpeculationRulesDocumentRulesSelectorMatches",
       "--enable-features=SpeculationRulesPrefetchProxy,PrefetchUseContentRefactor",
       "--bypass-prefetch-proxy-for-host=not-web-platform.test"
     ],
@@ -1705,7 +1709,7 @@
     ],
     "exclusive_tests": "ALL",
     "args": [
-      "--enable-blink-features=NoVarySearchPrefetch",
+      "--enable-blink-features=NoVarySearchPrefetch,SpeculationRulesNoVarySearchHint",
       "--enable-features=PrefetchUseContentRefactor"
     ],
     "expires": "Jul 1, 2023"
diff --git a/third_party/blink/web_tests/animations/animation-shorthand-removed.html b/third_party/blink/web_tests/animations/animation-shorthand-removed.html
index ee9245d..e7a91695 100644
--- a/third_party/blink/web_tests/animations/animation-shorthand-removed.html
+++ b/third_party/blink/web_tests/animations/animation-shorthand-removed.html
@@ -49,7 +49,7 @@
         values: [ "anim1", "10s", "linear", "5s", "infinite", "alternate", "forwards" ]
       }
     ];
-    const kExpectedResults = [ "none", "0s", "ease", "0s", "1", "normal", "none" ];
+    const kExpectedResults = [ "none", "auto", "ease", "0s", "1", "normal", "none" ];
 
     function start() {
       kElements.forEach(function(curEl) {
diff --git a/third_party/blink/web_tests/animations/animations-parsing-001.html b/third_party/blink/web_tests/animations/animations-parsing-001.html
index c9eb94dc..30da17a 100644
--- a/third_party/blink/web_tests/animations/animations-parsing-001.html
+++ b/third_party/blink/web_tests/animations/animations-parsing-001.html
@@ -98,8 +98,8 @@
 
 test(() => {
   // Test initial value.
-  assert_equals(computedStyle.animationDuration, '0s');
-  assert_equals(computedStyle.webkitAnimationDuration, '0s');
+  assert_equals(computedStyle.animationDuration, 'auto');
+  assert_equals(computedStyle.webkitAnimationDuration, 'auto');
 
   style.animationDuration = "0s";
   assert_not_equals(Object.keys(style).indexOf('animationDuration'), -1);
@@ -134,45 +134,45 @@
   // Negative values are invalid.
   style.animationDuration = "-10ms";
   assert_equals(style.animationDuration, '');
-  assert_equals(computedStyle.animationDuration, '0s');
+  assert_equals(computedStyle.animationDuration, 'auto');
   assert_equals(style.webkitAnimationDuration, '');
-  assert_equals(computedStyle.webkitAnimationDuration, '0s');
+  assert_equals(computedStyle.webkitAnimationDuration, 'auto');
 
   style.animationDuration = "'5ms'";
   assert_equals(style.animationDuration, '');
-  assert_equals(computedStyle.animationDuration, '0s');
+  assert_equals(computedStyle.animationDuration, 'auto');
   assert_equals(style.webkitAnimationDuration, '');
-  assert_equals(computedStyle.webkitAnimationDuration, '0s');
+  assert_equals(computedStyle.webkitAnimationDuration, 'auto');
 
   style.animationDuration = "30px";
   assert_equals(style.animationDuration, '');
-  assert_equals(computedStyle.animationDuration, '0s');
+  assert_equals(computedStyle.animationDuration, 'auto');
   assert_equals(style.webkitAnimationDuration, '');
-  assert_equals(computedStyle.webkitAnimationDuration, '0s');
+  assert_equals(computedStyle.webkitAnimationDuration, 'auto');
 
   style.animationDuration = "solid";
   assert_equals(style.animationDuration, '');
-  assert_equals(computedStyle.animationDuration, '0s');
+  assert_equals(computedStyle.animationDuration, 'auto');
   assert_equals(style.webkitAnimationDuration, '');
-  assert_equals(computedStyle.webkitAnimationDuration, '0s');
+  assert_equals(computedStyle.webkitAnimationDuration, 'auto');
 
   style.animationDuration = "20";
   assert_equals(style.animationDuration, '');
-  assert_equals(computedStyle.animationDuration, '0s');
+  assert_equals(computedStyle.animationDuration, 'auto');
   assert_equals(style.webkitAnimationDuration, '');
-  assert_equals(computedStyle.webkitAnimationDuration, '0s');
+  assert_equals(computedStyle.webkitAnimationDuration, 'auto');
 
   style.animationDuration = "20%";
   assert_equals(style.animationDuration, '');
-  assert_equals(computedStyle.animationDuration, '0s');
+  assert_equals(computedStyle.animationDuration, 'auto');
   assert_equals(style.webkitAnimationDuration, '');
-  assert_equals(computedStyle.webkitAnimationDuration, '0s');
+  assert_equals(computedStyle.webkitAnimationDuration, 'auto');
 
   style.animationDuration = "0s, 20px";
   assert_equals(style.animationDuration, '');
-  assert_equals(computedStyle.animationDuration, '0s');
+  assert_equals(computedStyle.animationDuration, 'auto');
   assert_equals(style.webkitAnimationDuration, '');
-  assert_equals(computedStyle.webkitAnimationDuration, '0s');
+  assert_equals(computedStyle.webkitAnimationDuration, 'auto');
 }, "Invalid animation-duration values.");
 </script>
 </body>
diff --git a/third_party/blink/web_tests/animations/animations-parsing-005.html b/third_party/blink/web_tests/animations/animations-parsing-005.html
index 461d05c..dbcc7fb 100644
--- a/third_party/blink/web_tests/animations/animations-parsing-005.html
+++ b/third_party/blink/web_tests/animations/animations-parsing-005.html
@@ -27,25 +27,25 @@
 test(() => {
   style.animation = "";
   // Test initial value.
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "myShorthandAnim";
   assert_not_equals(Object.keys(style).indexOf('animation'), -1);
   assert_not_equals(Object.keys(style).indexOf('webkitAnimation'), -1);
-  assert_equals(style.animation, '0s ease 0s 1 normal none running myShorthandAnim');
-  assert_equals(computedStyle.animation, '0s ease 0s 1 normal none running myShorthandAnim');
-  assert_equals(style.webkitAnimation, '0s ease 0s 1 normal none running myShorthandAnim');
-  assert_equals(computedStyle.webkitAnimation, '0s ease 0s 1 normal none running myShorthandAnim');
+  assert_equals(style.animation, 'auto ease 0s 1 normal none running myShorthandAnim');
+  assert_equals(computedStyle.animation, 'auto ease 0s 1 normal none running myShorthandAnim');
+  assert_equals(style.webkitAnimation, 'auto ease 0s 1 normal none running myShorthandAnim');
+  assert_equals(computedStyle.webkitAnimation, 'auto ease 0s 1 normal none running myShorthandAnim');
   assert_true(checkAnimationShorthandValue());
 
   style.animation = "none";
   assert_not_equals(Object.keys(style).indexOf('animation'), -1);
   assert_not_equals(Object.keys(style).indexOf('webkitAnimation'), -1);
-  assert_equals(style.animation, '0s ease 0s 1 normal none running none');
-  assert_equals(computedStyle.animation, '0s ease 0s 1 normal none running none');
-  assert_equals(style.webkitAnimation, '0s ease 0s 1 normal none running none');
-  assert_equals(computedStyle.webkitAnimation, '0s ease 0s 1 normal none running none');
+  assert_equals(style.animation, 'auto ease 0s 1 normal none running none');
+  assert_equals(computedStyle.animation, 'auto ease 0s 1 normal none running none');
+  assert_equals(style.webkitAnimation, 'auto ease 0s 1 normal none running none');
+  assert_equals(computedStyle.webkitAnimation, 'auto ease 0s 1 normal none running none');
   assert_true(checkAnimationShorthandValue());
 
   style.animation = "none 20s";
@@ -187,23 +187,23 @@
   assert_true(checkAnimationShorthandValue());
 
   style.animation = "none, none";
-  assert_equals(style.animation, '0s ease 0s 1 normal none running none, 0s ease 0s 1 normal none running none');
-  assert_equals(computedStyle.animation, '0s ease 0s 1 normal none running none, 0s ease 0s 1 normal none running none');
-  assert_equals(style.webkitAnimation, '0s ease 0s 1 normal none running none, 0s ease 0s 1 normal none running none');
-  assert_equals(computedStyle.webkitAnimation, '0s ease 0s 1 normal none running none, 0s ease 0s 1 normal none running none');
+  assert_equals(style.animation, 'auto ease 0s 1 normal none running none, auto ease 0s 1 normal none running none');
+  assert_equals(computedStyle.animation, 'auto ease 0s 1 normal none running none, auto ease 0s 1 normal none running none');
+  assert_equals(style.webkitAnimation, 'auto ease 0s 1 normal none running none, auto ease 0s 1 normal none running none');
+  assert_equals(computedStyle.webkitAnimation, 'auto ease 0s 1 normal none running none, auto ease 0s 1 normal none running none');
 
   style.animation = "ease-in test 20s 10s, none";
-  assert_equals(style.animation, '20s ease-in 10s 1 normal none running test, 0s ease 0s 1 normal none running none');
-  assert_equals(computedStyle.animation, '20s ease-in 10s 1 normal none running test, 0s ease 0s 1 normal none running none');
-  assert_equals(style.webkitAnimation, '20s ease-in 10s 1 normal none running test, 0s ease 0s 1 normal none running none');
-  assert_equals(computedStyle.webkitAnimation, '20s ease-in 10s 1 normal none running test, 0s ease 0s 1 normal none running none');
+  assert_equals(style.animation, '20s ease-in 10s 1 normal none running test, auto ease 0s 1 normal none running none');
+  assert_equals(computedStyle.animation, '20s ease-in 10s 1 normal none running test, auto ease 0s 1 normal none running none');
+  assert_equals(style.webkitAnimation, '20s ease-in 10s 1 normal none running test, auto ease 0s 1 normal none running none');
+  assert_equals(computedStyle.webkitAnimation, '20s ease-in 10s 1 normal none running test, auto ease 0s 1 normal none running none');
   assert_equals(computedStyle.animationName, 'test, none');
 
   style.animation = "none, ease-in test 20s 10s";
-  assert_equals(style.animation, '0s ease 0s 1 normal none running none, 20s ease-in 10s 1 normal none running test');
-  assert_equals(computedStyle.animation, '0s ease 0s 1 normal none running none, 20s ease-in 10s 1 normal none running test');
-  assert_equals(style.webkitAnimation, '0s ease 0s 1 normal none running none, 20s ease-in 10s 1 normal none running test');
-  assert_equals(computedStyle.webkitAnimation, '0s ease 0s 1 normal none running none, 20s ease-in 10s 1 normal none running test');
+  assert_equals(style.animation, 'auto ease 0s 1 normal none running none, 20s ease-in 10s 1 normal none running test');
+  assert_equals(computedStyle.animation, 'auto ease 0s 1 normal none running none, 20s ease-in 10s 1 normal none running test');
+  assert_equals(style.webkitAnimation, 'auto ease 0s 1 normal none running none, 20s ease-in 10s 1 normal none running test');
+  assert_equals(computedStyle.webkitAnimation, 'auto ease 0s 1 normal none running none, 20s ease-in 10s 1 normal none running test');
   assert_equals(computedStyle.animationName, 'none, test');
 
   style.animation = "myShorthandAnim both 20s 10s ease-in paused, myShorthandAnim ease-out 20s";
@@ -232,93 +232,93 @@
 
   style.animation = "solid red green";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "all 30s width height ease-in";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "animName 30s ease-in 20s, 20px";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "test 30s ease-in 20s, step-start(2)";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "ease-in opacity top 20s 10s myAnim, none";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "none, ease-in opacity top 20s 10s myAnim";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = ",";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "running,";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = ", alternate";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = ", commas,";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "test,, 5s";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "2 initial";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "2 inherit";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "2 unset";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 
   style.animation = "2 default";
   assert_equals(style.animation, '');
-  assert_equals(computedStyle.animation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.animation, 'none auto ease 0s 1 normal none running');
   assert_equals(style.webkitAnimation, '');
-  assert_equals(computedStyle.webkitAnimation, 'none 0s ease 0s 1 normal none running');
+  assert_equals(computedStyle.webkitAnimation, 'none auto ease 0s 1 normal none running');
 }, "Invalid animation shorthand values.");
 </script>
 </body>
diff --git a/third_party/blink/web_tests/animations/prefixed/animation-inherit-initial-unprefixed.html b/third_party/blink/web_tests/animations/prefixed/animation-inherit-initial-unprefixed.html
index 5b5e3541..f153da7 100644
--- a/third_party/blink/web_tests/animations/prefixed/animation-inherit-initial-unprefixed.html
+++ b/third_party/blink/web_tests/animations/prefixed/animation-inherit-initial-unprefixed.html
@@ -72,8 +72,8 @@
   assert_equals(computedStyle.animationName, "none");
   assert_equals(computedStyle.webkitAnimationName, "none");
 
-  assert_equals(computedStyle.animationDuration, "0s");
-  assert_equals(computedStyle.webkitAnimationDuration, "0s");
+  assert_equals(computedStyle.animationDuration, "auto");
+  assert_equals(computedStyle.webkitAnimationDuration, "auto");
 
   assert_equals(computedStyle.animationTimingFunction, "ease");
   assert_equals(computedStyle.webkitAnimationTimingFunction, "ease");
diff --git a/third_party/blink/web_tests/animations/prefixed/animation-shorthand-prefixed.html b/third_party/blink/web_tests/animations/prefixed/animation-shorthand-prefixed.html
index 9e00778c..120ed24 100644
--- a/third_party/blink/web_tests/animations/prefixed/animation-shorthand-prefixed.html
+++ b/third_party/blink/web_tests/animations/prefixed/animation-shorthand-prefixed.html
@@ -52,8 +52,8 @@
   "webkitAnimationFillMode"
 ];
 const kExpectedResults = [
-  { id: 'a',  values: [ "none", "0s", "ease", "0s", "1", "normal", "none" ] },
-  { id: 'b',  values: [ "none", "0s", "ease", "0s", "1", "normal", "none" ] },
+  { id: 'a',  values: [ "none", "auto", "ease", "0s", "1", "normal", "none" ] },
+  { id: 'b',  values: [ "none", "auto", "ease", "0s", "1", "normal", "none" ] },
   { id: 'c',  values: [ "anim1", "10s", "ease", "0s", "1", "normal", "none" ] },
   { id: 'd',  values: [ "anim1", "10s", "linear", "0s", "1", "normal", "none" ] },
   { id: 'e',  values: [ "anim1", "10s", "linear", "5s", "1", "normal", "none" ] },
diff --git a/third_party/blink/web_tests/animations/prefixed/animation-shorthand-unprefixed.html b/third_party/blink/web_tests/animations/prefixed/animation-shorthand-unprefixed.html
index 62f9a3f..edb3941 100644
--- a/third_party/blink/web_tests/animations/prefixed/animation-shorthand-unprefixed.html
+++ b/third_party/blink/web_tests/animations/prefixed/animation-shorthand-unprefixed.html
@@ -52,8 +52,8 @@
   "animationFillMode"
 ];
 const kExpectedResults = [
-  { id: 'a',  values: [ "none", "0s", "ease", "0s", "1", "normal", "none" ] },
-  { id: 'b',  values: [ "none", "0s", "ease", "0s", "1", "normal", "none" ] },
+  { id: 'a',  values: [ "none", "auto", "ease", "0s", "1", "normal", "none" ] },
+  { id: 'b',  values: [ "none", "auto", "ease", "0s", "1", "normal", "none" ] },
   { id: 'c',  values: [ "anim1", "10s", "ease", "0s", "1", "normal", "none" ] },
   { id: 'd',  values: [ "anim1", "10s", "linear", "0s", "1", "normal", "none" ] },
   { id: 'e',  values: [ "anim1", "10s", "linear", "5s", "1", "normal", "none" ] },
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/inheritance.html b/third_party/blink/web_tests/external/wpt/css/css-animations/inheritance.html
index 6e7697b..dd9d646 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/inheritance.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/inheritance.html
@@ -17,7 +17,7 @@
 <script>
 assert_not_inherited('animation-delay', '0s', '2s');
 assert_not_inherited('animation-direction', 'normal', 'reverse');
-assert_not_inherited('animation-duration', '0s', '3s');
+assert_not_inherited('animation-duration', 'auto', '3s');
 assert_not_inherited('animation-fill-mode', 'none', 'forwards');
 assert_not_inherited('animation-iteration-count', '1', '4');
 assert_not_inherited('animation-name', 'none', 'spinner');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
index 7cf62fe..f71ecc9 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-FAIL Default animation value assert_equals: expected "0s ease 0s 1 normal none running none" but got "none 0s ease 0s 1 normal none running"
+FAIL Default animation value assert_equals: expected "auto ease 0s 1 normal none running none" but got "none auto ease 0s 1 normal none running"
 PASS Property animation value '1s'
 PASS Property animation value 'cubic-bezier(0, -2, 1, 3)'
 PASS Property animation value '1s -3s'
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html
index f8d34b8..9152c49 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-computed.html
@@ -18,24 +18,24 @@
 // [ none | <keyframes-name> ]
 
 test(() => {
-  assert_equals(getComputedStyle(document.getElementById('target')).animation, "0s ease 0s 1 normal none running none");
+  assert_equals(getComputedStyle(document.getElementById('target')).animation, "auto ease 0s 1 normal none running none");
 }, "Default animation value");
 
 test_computed_value("animation", "1s", "1s ease 0s 1 normal none running none");
-test_computed_value("animation", "cubic-bezier(0, -2, 1, 3)", "0s cubic-bezier(0, -2, 1, 3) 0s 1 normal none running none");
+test_computed_value("animation", "cubic-bezier(0, -2, 1, 3)", "auto cubic-bezier(0, -2, 1, 3) 0s 1 normal none running none");
 test_computed_value("animation", "1s -3s", "1s ease -3s 1 normal none running none");
-test_computed_value("animation", "4", "0s ease 0s 4 normal none running none");
-test_computed_value("animation", "reverse", "0s ease 0s 1 reverse none running none");
-test_computed_value("animation", "both", "0s ease 0s 1 normal both running none");
-test_computed_value("animation", "paused", "0s ease 0s 1 normal none paused none");
-test_computed_value("animation", "none", "0s ease 0s 1 normal none running none");
-test_computed_value("animation", "anim", "0s ease 0s 1 normal none running anim");
+test_computed_value("animation", "4", "auto ease 0s 4 normal none running none");
+test_computed_value("animation", "reverse", "auto ease 0s 1 reverse none running none");
+test_computed_value("animation", "both", "auto ease 0s 1 normal both running none");
+test_computed_value("animation", "paused", "auto ease 0s 1 normal none paused none");
+test_computed_value("animation", "none", "auto ease 0s 1 normal none running none");
+test_computed_value("animation", "anim", "auto ease 0s 1 normal none running anim");
 
 test_computed_value("animation", "anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)",
   "1s cubic-bezier(0, -2, 1, 3) -3s 4 reverse both paused anim");
 
 test_computed_value("animation", "anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)",
-  "0s ease 0s 1 reverse both paused anim, 1s cubic-bezier(0, -2, 1, 3) -3s 4 normal none running none");
+  "auto ease 0s 1 reverse both paused anim, 1s cubic-bezier(0, -2, 1, 3) -3s 4 normal none running none");
 
 // TODO: Add test with a single timing-function keyword.
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-shorthand.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-shorthand.html
index 464e424..8b482ff 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-shorthand.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-shorthand.html
@@ -26,7 +26,7 @@
 });
 
 test_shorthand_value('animation', 'anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)', {
-  'animation-duration': '0s, 1s',
+  'animation-duration': 'auto, 1s',
   'animation-timing-function': 'ease, cubic-bezier(0, -2, 1, 3)',
   'animation-delay': '0s, -3s',
   'animation-iteration-count': '1, 4',
@@ -40,7 +40,7 @@
 });
 
 test_shorthand_value('animation', '4 1s -3s cubic-bezier(0, -2, 1, 3), anim paused both reverse', {
-  'animation-duration': '1s, 0s',
+  'animation-duration': '1s, auto',
   'animation-timing-function': 'cubic-bezier(0, -2, 1, 3), ease',
   'animation-delay': '-3s, 0s',
   'animation-iteration-count': '4, 1',
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-valid.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-valid.html
index 4027ee12..0c5d571 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-valid.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-valid.html
@@ -16,20 +16,20 @@
 // <single-animation-fill-mode> || <single-animation-play-state> ||
 // [ none | <keyframes-name> ]
 test_valid_value("animation", "1s", ["1s", "1s ease 0s 1 normal none running none"]);
-test_valid_value("animation", "cubic-bezier(0, -2, 1, 3)", ["cubic-bezier(0, -2, 1, 3)", "0s cubic-bezier(0, -2, 1, 3) 0s 1 normal none running none"]);
+test_valid_value("animation", "cubic-bezier(0, -2, 1, 3)", ["cubic-bezier(0, -2, 1, 3)", "auto cubic-bezier(0, -2, 1, 3) 0s 1 normal none running none"]);
 test_valid_value("animation", "1s -3s", ["1s -3s", "1s ease -3s 1 normal none running none"]);
-test_valid_value("animation", "4", ["4", "0s ease 0s 4 normal none running none"]);
-test_valid_value("animation", "reverse", ["reverse", "0s ease 0s 1 reverse none running none"]);
-test_valid_value("animation", "both", ["both", "0s ease 0s 1 normal both running none"]);
-test_valid_value("animation", "paused", ["paused", "0s ease 0s 1 normal none paused none"]);
-test_valid_value("animation", "none", ["0s", "none", "0s ease 0s 1 normal none running none"]);
-test_valid_value("animation", "anim", ["anim", "0s ease 0s 1 normal none running anim"]);
+test_valid_value("animation", "4", ["4", "auto ease 0s 4 normal none running none"]);
+test_valid_value("animation", "reverse", ["reverse", "auto ease 0s 1 reverse none running none"]);
+test_valid_value("animation", "both", ["both", "auto ease 0s 1 normal both running none"]);
+test_valid_value("animation", "paused", ["paused", "auto ease 0s 1 normal none paused none"]);
+test_valid_value("animation", "none", ["auto", "none", "auto ease 0s 1 normal none running none"]);
+test_valid_value("animation", "anim", ["anim", "auto ease 0s 1 normal none running anim"]);
 
 test_valid_value("animation", "anim paused both reverse 4 1s -3s cubic-bezier(0, -2, 1, 3)",
   "1s cubic-bezier(0, -2, 1, 3) -3s 4 reverse both paused anim");
 
 test_valid_value("animation", "anim paused both reverse, 4 1s -3s cubic-bezier(0, -2, 1, 3)",
-  ["reverse both paused anim, 1s cubic-bezier(0, -2, 1, 3) -3s 4", "0s ease 0s 1 reverse both paused anim, 1s cubic-bezier(0, -2, 1, 3) -3s 4 normal none running none"]);
+  ["reverse both paused anim, 1s cubic-bezier(0, -2, 1, 3) -3s 4", "auto ease 0s 1 reverse both paused anim, 1s cubic-bezier(0, -2, 1, 3) -3s 4 normal none running none"]);
 
 // TODO: Add test with a single negative time.
 // TODO: Add test with a single timing-function keyword.
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/clipping-001.html b/third_party/blink/web_tests/external/wpt/css/css-break/clipping-001.html
new file mode 100644
index 0000000..da15ec7a2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/clipping-001.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442211">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; gap:0; column-fill:auto; width:100px; height:100px; background:red;">
+  <div style="height:25px; background:green;"></div>
+  <div style="position:relative; overflow:clip; height:150px;">
+    <div>
+      <div style="position:absolute; top:-25px; width:100%;">
+        <div style="height:25px; background:red;"></div>
+        <div style="height:150px; background:green;"></div>
+        <div style="height:25px; background:red;"></div>
+      </div>
+    </div>
+  </div>
+  <div style="height:25px; background:green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/clipping-002.html b/third_party/blink/web_tests/external/wpt/css/css-break/clipping-002.html
new file mode 100644
index 0000000..e1f2c4e1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/clipping-002.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442211">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:4; gap:0; column-fill:auto; width:100px; height:100px; background:red;">
+  <div style="height:25px; background:green;"></div>
+  <div style="position:relative; overflow:clip; height:350px;">
+    <div>
+      <div style="position:absolute; top:-25px; width:100%;">
+        <div style="height:25px; background:red;"></div>
+        <div style="height:350px; background:green;"></div>
+        <div style="height:25px; background:red;"></div>
+      </div>
+    </div>
+  </div>
+  <div style="height:25px; background:green;"></div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/transform-017.html b/third_party/blink/web_tests/external/wpt/css/css-break/transform-017.html
new file mode 100644
index 0000000..b01e3cc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/transform-017.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442211">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:1; gap:0; column-fill:auto; width:100px; height:100px; background:red;">
+  <div style="height:50px;"></div>
+  <div style="transform:scaleY(2); transform-origin:bottom; width:100px; height:50px;">
+    <div style="position:absolute; top:150px;">
+      <div style="position:absolute; top:-150px; width:100px; height:50px; background:green;"></div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/transform-018.html b/third_party/blink/web_tests/external/wpt/css/css-break/transform-018.html
new file mode 100644
index 0000000..dc9a791e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/transform-018.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442211">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="display:flow-root; position:relative; width:100px; height:100px; background:red;">
+  <div style="position:absolute; width:25%; height:40px; background:green;"></div>
+  <div style="position:absolute; right:0; top:0; width:25%; height:30px; background:green;"></div>
+  <div style="position:absolute; right:0; bottom:0; width:25%; height:30px; background:green;"></div>
+  <div style="margin-top:50px; columns:4; gap:0; column-fill:auto; height:50px;">
+    <div style="height:20px;"></div>
+    <div style="transform:scaleY(2); transform-origin:bottom; height:150px;">
+      <div>
+        <div style="position:absolute; width:100%; height:100%; background:green;"></div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/transform-019.html b/third_party/blink/web_tests/external/wpt/css/css-break/transform-019.html
new file mode 100644
index 0000000..758ff71
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/transform-019.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442211">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="columns:2; gap:0; column-fill:auto; width:100px; height:100px; background:red;">
+  <div style="height:50px;">
+    <div style="height:150px; background:green;"></div>
+  </div>
+  <div style="transform:scaleY(2); transform-origin:bottom; width:50px; height:100px;">
+    <div>
+      <div style="position:absolute; top:100px; width:50px; height:25px; background:green;"></div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/transform-020.html b/third_party/blink/web_tests/external/wpt/css/css-break/transform-020.html
new file mode 100644
index 0000000..e15df5f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/transform-020.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442211">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="display:flow-root; position:relative; width:100px; height:100px; background:red;">
+  <div style="position:absolute; width:25%; height:50px; background:green;"></div>
+  <div style="position:absolute; right:0; width:25%; height:50px; background:green;"></div>
+  <div style="columns:4; margin-top:50px; gap:0; column-fill:auto; width:100px; height:50px;">
+    <div style="position:relative; height:50px; background:green;">
+      <div style="position:absolute; transform:scaleY(2); transform-origin:bottom; top:50px; width:100%; height:100px;">
+        <div>
+          <div style="position:absolute; top:50px; width:100%; height:75px; background:green;"></div>
+        </div>
+        <div style="height:50px; background:green;"></div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-break/transform-021.html b/third_party/blink/web_tests/external/wpt/css/css-break/transform-021.html
new file mode 100644
index 0000000..c97ef30
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-break/transform-021.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442211">
+<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="display:flow-root; position:relative; width:100px; height:100px; background:red;">
+  <div style="position:absolute; width:25%; height:50px; background:green;"></div>
+  <div style="position:absolute; right:0; width:25%; height:50px; background:green;"></div>
+  <div style="columns:4; margin-top:50px; gap:0; column-fill:auto; width:100px; height:50px;">
+    <div style="position:relative; height:50px; background:green;">
+      <div style="position:absolute; transform:scaleY(2); transform-origin:bottom; top:50px; width:100%; height:100px;">
+        <div style="position:absolute; top:50px; width:100%; height:75px; background:green;"></div>
+        <div style="height:50px; background:green;"></div>
+      </div>
+    </div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-color-mix-function-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-color-mix-function-expected.txt
deleted file mode 100644
index c763f87..0000000
--- a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-color-mix-function-expected.txt
+++ /dev/null
@@ -1,441 +0,0 @@
-This is a testharness.js-based test.
-Found 437 tests; 427 PASS, 10 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS e.style['color'] = "color-mix(in srgb, red, blue)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, red, blue)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, red calc(20%), blue)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, red calc(var(--v)*1%), blue)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, currentcolor, blue)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, red 60%, blue 40%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch decreasing hue, red, hsl(120, 100%, 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20%), hsl(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, 25% hsl(120deg 10% 20%), hsl(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20%), 25% hsl(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20%), hsl(30deg 30% 40%) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40%) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20%) 30%, hsl(30deg 30% 40%) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20%) 12.5%, hsl(30deg 30% 40%) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20% / .4), hsl(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20%) 25%, hsl(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, 25% hsl(120deg 10% 20% / .4), hsl(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20% / .4), 25% hsl(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20% / .4), hsl(30deg 30% 40% / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20% / .4) 25%, hsl(30deg 30% 40% / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20% / .4) 30%, hsl(30deg 30% 40% / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20% / .4) 12.5%, hsl(30deg 30% 40% / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 10% 20% / .4) 0%, hsl(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(40deg 50% 50%), hsl(60deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(60deg 50% 50%), hsl(40deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(50deg 50% 50%), hsl(330deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(330deg 50% 50%), hsl(50deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(20deg 50% 50%), hsl(320deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(320deg 50% 50%), hsl(20deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl shorter hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl shorter hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl shorter hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl shorter hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl shorter hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl shorter hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl longer hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl longer hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl longer hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl longer hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl longer hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl longer hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl increasing hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl increasing hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl increasing hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl increasing hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl increasing hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl increasing hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl decreasing hue, hsl(40deg 50% 50%), hsl(60deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl decreasing hue, hsl(60deg 50% 50%), hsl(40deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl decreasing hue, hsl(50deg 50% 50%), hsl(330deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl decreasing hue, hsl(330deg 50% 50%), hsl(50deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl decreasing hue, hsl(20deg 50% 50%), hsl(320deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl decreasing hue, hsl(320deg 50% 50%), hsl(20deg 50% 50%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(none none none), hsl(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(none none none), hsl(30deg 40% 80%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 20% 40%), hsl(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 20% none), hsl(30deg 40% 60%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 20% 40%), hsl(30deg 20% none))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(none 20% 40%), hsl(30deg none 80%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 40% 40% / none), hsl(0deg 40% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 40% 40% / none), hsl(0deg 40% 40% / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, hsl(120deg 40% 40% / none), hsl(0deg 40% 40% / none))" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, color(display-p3 0 1 0) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, lab(100 104.3 -50.9) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, lab(0 104.3 -50.9) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, lch(100 116 334) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, lch(0 116 334) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, oklab(1 0.365 -0.16) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, oklab(0 0.365 -0.16) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, oklch(1 0.399 336.3) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hsl, oklch(0 0.399 336.3) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%), hwb(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%) 25%, hwb(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, 25% hwb(120deg 10% 20%), hwb(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%), 25% hwb(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%), hwb(30deg 30% 40%) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%) 25%, hwb(30deg 30% 40%) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%) 30%, hwb(30deg 30% 40%) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%) 12.5%, hwb(30deg 30% 40%) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%) 0%, hwb(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / .4), hwb(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / .4) 25%, hwb(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, 25% hwb(120deg 10% 20% / .4), hwb(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%), 25% hwb(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / .4), hwb(30deg 30% 40% / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / .4) 25%, hwb(30deg 30% 40% / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / .4) 30%, hwb(30deg 30% 40% / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / .4) 12.5%, hwb(30deg 30% 40% / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / .4) 0%, hwb(30deg 30% 40% / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(40deg 30% 40%), hwb(60deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(60deg 30% 40%), hwb(40deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(50deg 30% 40%), hwb(330deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(330deg 30% 40%), hwb(50deg 30% 40%))" should set the property value
-FAIL e.style['color'] = "color-mix(in hwb, hwb(20deg 30% 40%), hwb(320deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb, rgb(153, 102, 77), rgb(153, 77, 128))" but got "color-mix(in hwb, rgb(153, 102, 77), rgb(153, 77, 127))"
-FAIL e.style['color'] = "color-mix(in hwb, hwb(320deg 30% 40%), hwb(20deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb, rgb(153, 77, 128), rgb(153, 102, 77))" but got "color-mix(in hwb, rgb(153, 77, 127), rgb(153, 102, 77))"
-PASS e.style['color'] = "color-mix(in hwb shorter hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb shorter hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb shorter hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb shorter hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))" should set the property value
-FAIL e.style['color'] = "color-mix(in hwb shorter hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb, rgb(153, 102, 77), rgb(153, 77, 128))" but got "color-mix(in hwb, rgb(153, 102, 77), rgb(153, 77, 127))"
-FAIL e.style['color'] = "color-mix(in hwb shorter hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb, rgb(153, 77, 128), rgb(153, 102, 77))" but got "color-mix(in hwb, rgb(153, 77, 127), rgb(153, 102, 77))"
-PASS e.style['color'] = "color-mix(in hwb longer hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb longer hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb longer hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb longer hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))" should set the property value
-FAIL e.style['color'] = "color-mix(in hwb longer hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb longer hue, rgb(153, 102, 77), rgb(153, 77, 128))" but got "color-mix(in hwb longer hue, rgb(153, 102, 77), rgb(153, 77, 127))"
-FAIL e.style['color'] = "color-mix(in hwb longer hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb longer hue, rgb(153, 77, 128), rgb(153, 102, 77))" but got "color-mix(in hwb longer hue, rgb(153, 77, 127), rgb(153, 102, 77))"
-PASS e.style['color'] = "color-mix(in hwb increasing hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb increasing hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb increasing hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb increasing hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))" should set the property value
-FAIL e.style['color'] = "color-mix(in hwb increasing hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb increasing hue, rgb(153, 102, 77), rgb(153, 77, 128))" but got "color-mix(in hwb increasing hue, rgb(153, 102, 77), rgb(153, 77, 127))"
-FAIL e.style['color'] = "color-mix(in hwb increasing hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb increasing hue, rgb(153, 77, 128), rgb(153, 102, 77))" but got "color-mix(in hwb increasing hue, rgb(153, 77, 127), rgb(153, 102, 77))"
-PASS e.style['color'] = "color-mix(in hwb decreasing hue, hwb(40deg 30% 40%), hwb(60deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb decreasing hue, hwb(60deg 30% 40%), hwb(40deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb decreasing hue, hwb(50deg 30% 40%), hwb(330deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb decreasing hue, hwb(330deg 30% 40%), hwb(50deg 30% 40%))" should set the property value
-FAIL e.style['color'] = "color-mix(in hwb decreasing hue, hwb(20deg 30% 40%), hwb(320deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb decreasing hue, rgb(153, 102, 77), rgb(153, 77, 128))" but got "color-mix(in hwb decreasing hue, rgb(153, 102, 77), rgb(153, 77, 127))"
-FAIL e.style['color'] = "color-mix(in hwb decreasing hue, hwb(320deg 30% 40%), hwb(20deg 30% 40%))" should set the property value assert_equals: serialization should be canonical expected "color-mix(in hwb decreasing hue, rgb(153, 77, 128), rgb(153, 102, 77))" but got "color-mix(in hwb decreasing hue, rgb(153, 77, 127), rgb(153, 102, 77))"
-PASS e.style['color'] = "color-mix(in hwb, hwb(none none none), hwb(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(none none none), hwb(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%), hwb(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% none), hwb(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20%), hwb(30deg 30% none))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(none 10% 20%), hwb(30deg none 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / none), hwb(30deg 30% 40%))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / none), hwb(30deg 30% 40% / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, hwb(120deg 10% 20% / none), hwb(30deg 30% 40% / none))" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, color(display-p3 0 1 0) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, lab(100 104.3 -50.9) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, lab(0 104.3 -50.9) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, lch(100 116 334) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, lch(0 116 334) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, oklab(1 0.365 -0.16) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, oklab(0 0.365 -0.16) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, oklch(1 0.399 336.3) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in hwb, oklch(0 0.399 336.3) 100%, rgb(0, 0, 0) 0%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg), lch(50 60 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg) 25%, lch(50 60 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, 25% lch(10 20 30deg), lch(50 60 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg), 25% lch(50 60 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg), lch(50 60 70deg) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg) 25%, lch(50 60 70deg) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg) 30%, lch(50 60 70deg) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg) 12.5%, lch(50 60 70deg) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg) 0%, lch(50 60 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / .4), lch(50 60 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / .4) 25%, lch(50 60 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, 25% lch(10 20 30deg / .4), lch(50 60 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / .4), 25% lch(50 60 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / .4), lch(50 60 70deg / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / .4) 25%, lch(50 60 70deg / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / .4) 30%, lch(50 60 70deg / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / .4) 12.5%, lch(50 60 70deg / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / .4) 0%, lch(50 60 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(100 0 40deg), lch(100 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(100 0 60deg), lch(100 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(100 0 50deg), lch(100 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(100 0 330deg), lch(100 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(100 0 20deg), lch(100 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(100 0 320deg), lch(100 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch shorter hue, lch(100 0 40deg), lch(100 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch shorter hue, lch(100 0 60deg), lch(100 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch shorter hue, lch(100 0 50deg), lch(100 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch shorter hue, lch(100 0 330deg), lch(100 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch shorter hue, lch(100 0 20deg), lch(100 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch shorter hue, lch(100 0 320deg), lch(100 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch longer hue, lch(100 0 40deg), lch(100 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch longer hue, lch(100 0 60deg), lch(100 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch longer hue, lch(100 0 50deg), lch(100 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch longer hue, lch(100 0 330deg), lch(100 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch longer hue, lch(100 0 20deg), lch(100 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch longer hue, lch(100 0 320deg), lch(100 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch increasing hue, lch(100 0 40deg), lch(100 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch increasing hue, lch(100 0 60deg), lch(100 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch increasing hue, lch(100 0 50deg), lch(100 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch increasing hue, lch(100 0 330deg), lch(100 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch increasing hue, lch(100 0 20deg), lch(100 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch increasing hue, lch(100 0 320deg), lch(100 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch decreasing hue, lch(100 0 40deg), lch(100 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch decreasing hue, lch(100 0 60deg), lch(100 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch decreasing hue, lch(100 0 50deg), lch(100 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch decreasing hue, lch(100 0 330deg), lch(100 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch decreasing hue, lch(100 0 20deg), lch(100 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch decreasing hue, lch(100 0 320deg), lch(100 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(none none none), lch(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(none none none), lch(50 60 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg), lch(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 none), lch(50 60 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg), lch(50 60 none))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(none 20 30deg), lch(50 none 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in lch, lch(10 20 30deg / none), lch(50 60 70deg / none))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg), oklch(0.5 0.6 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg) 25%, oklch(0.5 0.6 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, 25% oklch(0.1 0.2 30deg), oklch(0.5 0.6 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg), 25% oklch(0.5 0.6 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg), oklch(0.5 0.6 70deg) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg) 25%, oklch(0.5 0.6 70deg) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg) 30%, oklch(0.5 0.6 70deg) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg) 12.5%, oklch(0.5 0.6 70deg) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg) 0%, oklch(0.5 0.6 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / .4), oklch(0.5 0.6 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / .4) 25%, oklch(0.5 0.6 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, 25% oklch(0.1 0.2 30deg / .4), oklch(0.5 0.6 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / .4), 25% oklch(0.5 0.6 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / .4), oklch(0.5 0.6 70deg / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / .4) 25%, oklch(0.5 0.6 70deg / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / .4) 30%, oklch(0.5 0.6 70deg / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / .4) 12.5%, oklch(0.5 0.6 70deg / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / .4) 0%, oklch(0.5 0.6 70deg / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(1 0 40deg), oklch(1 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(1 0 60deg), oklch(1 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(1 0 50deg), oklch(1 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(1 0 330deg), oklch(1 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(1 0 20deg), oklch(1 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(1 0 320deg), oklch(1 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch shorter hue, oklch(1 0 40deg), oklch(1 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch shorter hue, oklch(1 0 60deg), oklch(1 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch shorter hue, oklch(1 0 50deg), oklch(1 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch shorter hue, oklch(1 0 330deg), oklch(1 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch shorter hue, oklch(1 0 20deg), oklch(1 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch shorter hue, oklch(1 0 320deg), oklch(1 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch longer hue, oklch(1 0 40deg), oklch(1 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch longer hue, oklch(1 0 60deg), oklch(1 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch longer hue, oklch(1 0 50deg), oklch(1 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch longer hue, oklch(1 0 330deg), oklch(1 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch longer hue, oklch(1 0 20deg), oklch(1 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch longer hue, oklch(1 0 320deg), oklch(1 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch increasing hue, oklch(1 0 40deg), oklch(1 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch increasing hue, oklch(1 0 60deg), oklch(1 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch increasing hue, oklch(1 0 50deg), oklch(1 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch increasing hue, oklch(1 0 330deg), oklch(1 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch increasing hue, oklch(1 0 20deg), oklch(1 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch increasing hue, oklch(1 0 320deg), oklch(1 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch decreasing hue, oklch(1 0 40deg), oklch(1 0 60deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch decreasing hue, oklch(1 0 60deg), oklch(1 0 40deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch decreasing hue, oklch(1 0 50deg), oklch(1 0 330deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch decreasing hue, oklch(1 0 330deg), oklch(1 0 50deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch decreasing hue, oklch(1 0 20deg), oklch(1 0 320deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch decreasing hue, oklch(1 0 320deg), oklch(1 0 20deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(none none none), oklch(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(none none none), oklch(0.5 0.6 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg), oklch(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 none), oklch(0.5 0.6 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg), oklch(0.5 0.6 none))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(none 0.2 30deg), oklch(0.5 none 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / none), oklch(0.5 0.6 70deg))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / none), oklch(0.5 0.6 70deg / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in oklch, oklch(0.1 0.2 30deg / none), oklch(0.5 0.6 70deg / none))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30), lab(50 60 70))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30) 25%, lab(50 60 70))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, 25% lab(10 20 30), lab(50 60 70))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30), 25% lab(50 60 70))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30), lab(50 60 70) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30) 25%, lab(50 60 70) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30) 30%, lab(50 60 70) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30) 12.5%, lab(50 60 70) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30) 0%, lab(50 60 70))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / .4), lab(50 60 70 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / .4) 25%, lab(50 60 70 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, 25% lab(10 20 30 / .4), lab(50 60 70 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / .4), 25% lab(50 60 70 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / .4), lab(50 60 70 / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / .4) 25%, lab(50 60 70 / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / .4) 30%, lab(50 60 70 / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / .4) 12.5%, lab(50 60 70 / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / .4) 0%, lab(50 60 70 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(none none none), lab(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(none none none), lab(50 60 70))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30), lab(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 none), lab(50 60 70))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30), lab(50 60 none))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(none 20 30), lab(50 none 70))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / none), lab(50 60 70))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / none), lab(50 60 70 / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in lab, lab(10 20 30 / none), lab(50 60 70 / none))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3), oklab(0.5 0.6 0.7))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3) 25%, oklab(0.5 0.6 0.7))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, 25% oklab(0.1 0.2 0.3), oklab(0.5 0.6 0.7))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3), 25% oklab(0.5 0.6 0.7))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3), oklab(0.5 0.6 0.7) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3) 25%, oklab(0.5 0.6 0.7) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3) 30%, oklab(0.5 0.6 0.7) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3) 12.5%, oklab(0.5 0.6 0.7) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3) 0%, oklab(0.5 0.6 0.7))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / .4), oklab(0.5 0.6 0.7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / .4) 25%, oklab(0.5 0.6 0.7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, 25% oklab(0.1 0.2 0.3 / .4), oklab(0.5 0.6 0.7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / .4), 25% oklab(0.5 0.6 0.7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / .4), oklab(0.5 0.6 0.7 / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / .4) 25%, oklab(0.5 0.6 0.7 / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / .4) 30%, oklab(0.5 0.6 0.7 / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / .4) 12.5%, oklab(0.5 0.6 0.7 / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / .4) 0%, oklab(0.5 0.6 0.7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(none none none), oklab(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(none none none), oklab(0.5 0.6 0.7))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3), oklab(none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 none), oklab(0.5 0.6 0.7))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3), oklab(0.5 0.6 none))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(none 0.2 0.3), oklab(0.5 none 0.7))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / none))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3), color(srgb .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3) 25%, color(srgb .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3), color(srgb .5 .6 .7) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3) 25%, color(srgb .5 .6 .7) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3) 30%, color(srgb .5 .6 .7) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3) 12.5%, color(srgb .5 .6 .7) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3) 0%, color(srgb .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / .5), color(srgb .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 25%, color(srgb .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / .4), color(srgb .5 .6 .7 / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 25%, color(srgb .5 .6 .7 / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 30%, color(srgb .5 .6 .7 / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 12.5%, color(srgb .5 .6 .7 / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / .4) 0%, color(srgb .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb 2 3 4 / 5), color(srgb 4 6 8 / 10))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb -2 -3 -4), color(srgb -4 -6 -8))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb -2 -3 -4 / -5), color(srgb -4 -6 -8 / -10))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb none none none), color(srgb none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb none none none), color(srgb .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3), color(srgb none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 none), color(srgb .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3), color(srgb .5 .6 none))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb none .2 .3), color(srgb .5 none .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / none), color(srgb .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / none), color(srgb .5 .6 .7 / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb, color(srgb .1 .2 .3 / none), color(srgb .5 .6 .7 / none))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 25%, color(srgb-linear .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 .7) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 25%, color(srgb-linear .5 .6 .7) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 30%, color(srgb-linear .5 .6 .7) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 12.5%, color(srgb-linear .5 .6 .7) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3) 0%, color(srgb-linear .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .5), color(srgb-linear .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 25%, color(srgb-linear .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4), color(srgb-linear .5 .6 .7 / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 25%, color(srgb-linear .5 .6 .7 / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 30%, color(srgb-linear .5 .6 .7 / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 12.5%, color(srgb-linear .5 .6 .7 / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / .4) 0%, color(srgb-linear .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear 2 3 4 / 5), color(srgb-linear 4 6 8 / 10))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear -2 -3 -4), color(srgb-linear -4 -6 -8))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear -2 -3 -4 / -5), color(srgb-linear -4 -6 -8 / -10))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear none none none), color(srgb-linear none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear none none none), color(srgb-linear .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 none), color(srgb-linear .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3), color(srgb-linear .5 .6 none))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear none .2 .3), color(srgb-linear .5 none .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7 / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in srgb-linear, color(srgb-linear .1 .2 .3 / none), color(srgb-linear .5 .6 .7 / none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3) 25%, color(xyz .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 .7) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3) 25%, color(xyz .5 .6 .7) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3) 30%, color(xyz .5 .6 .7) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3) 12.5%, color(xyz .5 .6 .7) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3) 0%, color(xyz .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / .5), color(xyz .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 25%, color(xyz .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / .4), color(xyz .5 .6 .7 / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 25%, color(xyz .5 .6 .7 / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 30%, color(xyz .5 .6 .7 / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 12.5%, color(xyz .5 .6 .7 / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / .4) 0%, color(xyz .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz 2 3 4 / 5), color(xyz 4 6 8 / 10))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz -2 -3 -4), color(xyz -4 -6 -8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz -2 -3 -4 / -5), color(xyz -4 -6 -8 / -10))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz none none none), color(xyz none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz none none none), color(xyz .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3), color(xyz none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 none), color(xyz .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3), color(xyz .5 .6 none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz none .2 .3), color(xyz .5 none .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7 / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz, color(xyz .1 .2 .3 / none), color(xyz .5 .6 .7 / none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 25%, color(xyz-d50 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 .7) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 25%, color(xyz-d50 .5 .6 .7) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 30%, color(xyz-d50 .5 .6 .7) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 12.5%, color(xyz-d50 .5 .6 .7) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3) 0%, color(xyz-d50 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .5), color(xyz-d50 .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 25%, color(xyz-d50 .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4), color(xyz-d50 .5 .6 .7 / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 25%, color(xyz-d50 .5 .6 .7 / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 30%, color(xyz-d50 .5 .6 .7 / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 12.5%, color(xyz-d50 .5 .6 .7 / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / .4) 0%, color(xyz-d50 .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 2 3 4 / 5), color(xyz-d50 4 6 8 / 10))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 -2 -3 -4), color(xyz-d50 -4 -6 -8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 -2 -3 -4 / -5), color(xyz-d50 -4 -6 -8 / -10))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 none none none), color(xyz-d50 none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 none none none), color(xyz-d50 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 none), color(xyz-d50 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3), color(xyz-d50 .5 .6 none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 none .2 .3), color(xyz-d50 .5 none .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7 / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d50, color(xyz-d50 .1 .2 .3 / none), color(xyz-d50 .5 .6 .7 / none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 25%, color(xyz-d65 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 .7) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 25%, color(xyz-d65 .5 .6 .7) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 30%, color(xyz-d65 .5 .6 .7) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 12.5%, color(xyz-d65 .5 .6 .7) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3) 0%, color(xyz-d65 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .5), color(xyz-d65 .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 25%, color(xyz-d65 .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4), color(xyz-d65 .5 .6 .7 / .8) 25%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 25%, color(xyz-d65 .5 .6 .7 / .8) 75%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 30%, color(xyz-d65 .5 .6 .7 / .8) 90%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 12.5%, color(xyz-d65 .5 .6 .7 / .8) 37.5%)" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / .4) 0%, color(xyz-d65 .5 .6 .7 / .8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 2 3 4 / 5), color(xyz-d65 4 6 8 / 10))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 -2 -3 -4), color(xyz-d65 -4 -6 -8))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 -2 -3 -4 / -5), color(xyz-d65 -4 -6 -8 / -10))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 none none none), color(xyz-d65 none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 none none none), color(xyz-d65 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 none none none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 none), color(xyz-d65 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3), color(xyz-d65 .5 .6 none))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 none .2 .3), color(xyz-d65 .5 none .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7 / 0.5))" should set the property value
-PASS e.style['color'] = "color-mix(in xyz-d65, color(xyz-d65 .1 .2 .3 / none), color(xyz-d65 .5 .6 .7 / none))" should set the property value
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-hwb.html b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-hwb.html
index 07367ac..dc2cec5 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-hwb.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-color/parsing/color-valid-hwb.html
@@ -28,6 +28,11 @@
 test_valid_value("color", "hwb(120 30% 50% / 0)", "rgba(77, 128, 77, 0)");
 test_valid_value("color", "hwb(none 100% 50% / none)", "rgba(170, 170, 170, 0)");
 test_valid_value("color", "hwb(0 100% 50% / 0)", "rgba(170, 170, 170, 0)");
+
+// Test that rounding happens properly. hwb(320deg 30% 40%) in sRGB has a blue
+// channel of exactly one-half.
+// 0.5 * 255 = 127.5. This value should be rounded UP to 128, not down to 127.
+test_valid_value("color", "hwb(320deg 30% 40%)", "rgb(153, 77, 128)");
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/no-vary-search/prefetch-single-with-hint.https.html b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/no-vary-search/prefetch-single-with-hint.https.html
new file mode 100644
index 0000000..d62788ca
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/no-vary-search/prefetch-single-with-hint.https.html
@@ -0,0 +1,337 @@
+<!DOCTYPE html>
+<title>Use for navigation the requested prefetched response annotated with No-Vary-Search hint, if
+No-Vary-Search headers also match during navigation</title>
+<meta charset="utf-8">
+
+<meta name="variant" content="?1-1">
+<meta name="variant" content="?2-2">
+<meta name="variant" content="?3-3">
+<meta name="variant" content="?4-4">
+<meta name="variant" content="?5-5">
+<meta name="variant" content="?6-6">
+<meta name="variant" content="?7-7">
+<meta name="variant" content="?8-8">
+<meta name="variant" content="?9-9">
+<meta name="variant" content="?10-10">
+<meta name="variant" content="?11-11">
+<meta name="variant" content="?12-12">
+<meta name="variant" content="?13-13">
+<meta name="variant" content="?14-14">
+<meta name="variant" content="?15-15">
+<meta name="variant" content="?16-16">
+<meta name="variant" content="?17-17">
+<meta name="variant" content="?18-18">
+<meta name="variant" content="?19-19">
+<meta name="variant" content="?20-20">
+<meta name="variant" content="?21-21">
+<meta name="variant" content="?22-22">
+<meta name="variant" content="?23-23">
+<meta name="variant" content="?24-24">
+<meta name="variant" content="?25-last">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/utils.js"></script>
+<script src="../resources/utils.sub.js"></script>
+<script src="/common/subset-tests.js"></script>
+
+<script>
+  function addNoVarySearchHeaderUsingQueryParam(url, value){
+    // Use nvs_header query parameter to ask the wpt server
+    // to populate No-Vary-Search response header.
+    if(value){
+      url.searchParams.append("nvs_header", value);
+    }
+  }
+
+  /*
+    remoteAgent: the RemoteContext instance used to communicate between the
+      test and the window where prefetch/navigation is happening
+    noVarySearchHeaderValue: the value of No-Vary-Search header to be populated
+      for the prefetched response
+    noVarySearchHintValue: the value of No-Vary-Search hint passed in
+      as expects_no_vary_search hint in prefetch speculation rules.
+    prefetchQuery: query params to be added to prefetchExecutor url and prefetched
+    navigateQuery: query params to be added to prefetchExecutor url and navigated to
+  */
+  async function prefetchAndNavigate(remoteAgent, noVarySearchHeaderValue, noVarySearchHintValue, prefetchQuery, navigateQuery){
+    /*
+    Flow:
+      * prefetch prefetch_nvs_hint.py?uuid=...&nvs_header=...&otherqueryparams
+      * the prefetch request above includes no_vary_search_hint in the speculation
+        rules
+      * the server blocks progress on this prefetch request on the server side so
+        from the browser perspective the server is "thinking"
+      * the test starts navigation to
+        prefetch_nvs_hint.py?uuid=...&nvs_header=...&otherdifferentqueryparams.
+        This navigation matches by No-Vary-Search hint the above in
+        progress prefetch.
+      * the test fetches prefetch_nvs_hint.py?uuid=...&unblock="unblock"
+        which unblocks the in progress prefetch so that the in-progress
+        navigation can continue
+    */
+    const prefetch_nvs_hint_server_page = "prefetch_nvs_hint.py";
+    const prefetchUrl = remoteAgent.getExecutorURL({executor:prefetch_nvs_hint_server_page});
+    const navigateToUrl = new URL(prefetchUrl);
+    // Add query params to the url to be prefetched.
+    const additionalPrefetchedUrlSearchParams = new URLSearchParams(prefetchQuery);
+    addNoVarySearchHeaderUsingQueryParam(prefetchUrl, noVarySearchHeaderValue);
+    additionalPrefetchedUrlSearchParams.forEach((value, key) => {
+      prefetchUrl.searchParams.append(key, value);
+    });
+
+    await remoteAgent.forceSinglePrefetch(prefetchUrl,
+        {expects_no_vary_search:noVarySearchHintValue});
+
+    // Add new query params to navigateToUrl to match No-Vary-Search test case.
+    const additionalNavigateToUrlSearchParams = new URLSearchParams(navigateQuery);
+    addNoVarySearchHeaderUsingQueryParam(navigateToUrl, noVarySearchHeaderValue);
+    additionalNavigateToUrlSearchParams.forEach((value, key) => {
+      navigateToUrl.searchParams.append(key, value);
+    });
+    // Url used by fetch in order to unblock the prefetched url
+    const nvshint_unblock_url = remoteAgent.getExecutorURL(
+      {executor:prefetch_nvs_hint_server_page, unblock:"unblock"});
+    await remoteAgent.execute_script((unblock_url) => {
+      onbeforeunload = (event) => {
+          fetch(unblock_url);
+      };
+    }, [nvshint_unblock_url]);
+
+    // Try navigating to a non-exact prefetched URL that matches by
+    // No-Vary-Search hint
+    // Wait for the navigation to finish
+    await remoteAgent.navigate(navigateToUrl);
+  }
+
+  function prefetch_no_vary_search_test(description, noVarySearch, noVarySearchHint, prefetchQuery, navigateQuery, shouldUsePrefetch){
+    promise_test(async t => {
+      assert_implements(HTMLScriptElement.supports('speculationrules'), "Speculation Rules not supported");
+      const agent = await spawnWindow(t, {});
+      await prefetchAndNavigate(agent,
+        noVarySearch,
+        noVarySearchHint,
+        prefetchQuery,
+        navigateQuery);
+
+      if(shouldUsePrefetch){
+        assert_prefetched(await agent.getRequestHeaders(),
+          "Navigation didn't use the prefetched response!");
+      }
+      else{
+        assert_not_prefetched(await agent.getRequestHeaders(),
+          "Navigation used the prefetched response!");
+      }
+     }, description);
+  }
+
+  // Test inputs:
+  // - description: a description of the test.
+  // - noVarySearch: No-Vary-Search header value for the response.
+  // - noVarySearchHint: No-Vary-Search hint to include in prefetch
+  //   speculation rules
+  // - prefetchQuery: added to query part of prefetch-executor when prefetching
+  // - navigateQuery: added to query part of prefetch-executor when navigating
+  // - shouldUsePrefetch: if the test case expects the prefetched entry to be
+  //   used or not.
+  [{description:"Use in-flight prefetch as query parameter b has the same value.",
+    noVarySearch: 'params=("a")',
+    noVarySearchHint: 'params=("a")',
+    prefetchQuery: "a=2&b=3",
+    navigateQuery: "b=3",
+    shouldUsePrefetch: true},
+
+   {description:"Don't use in-flight prefetch as there is no No-Vary-Search hint.",
+    noVarySearch: 'params=("a")',
+    noVarySearchHint: '',
+    prefetchQuery: "a=2&b=3",
+    navigateQuery: "b=3",
+    shouldUsePrefetch: false},
+
+   {description:"Don't use in-flight prefetch as the prefetched URL has the extra \"a\" query parameter.",
+    noVarySearch: 'params=("b")',
+    noVarySearchHint: 'params=("b")',
+    prefetchQuery: "a=2&b=3",
+    navigateQuery: "b=2",
+    shouldUsePrefetch: false},
+
+   {description:"Use in-flight prefetch as the URLs do not vary by a and b.",
+    noVarySearch: 'params=("a" "b")',
+    noVarySearchHint: 'params=("a" "b")',
+    prefetchQuery: "a=2&b=3",
+    navigateQuery: "b=2",
+    shouldUsePrefetch: true},
+
+   {description:"Do not use in-flight prefetch as the navigation URL has" +
+                " a different value for the \"b\" query parameter.",
+    noVarySearch: 'params=("a" "b")',
+    noVarySearchHint: 'params=("a")',
+    prefetchQuery: "a=2&b=3",
+    navigateQuery: "b=2",
+    shouldUsePrefetch: false},
+
+   {description:"Use in-flight prefetch as the URLs have the same values for all keys, only differing by order.",
+    noVarySearch: "key-order",
+    noVarySearchHint: "key-order",
+    prefetchQuery: "b=5&a=3&a=4&d=6&c=5&b=3",
+    navigateQuery: "d=6&a=3&b=5&b=3&c=5&a=4",
+    shouldUsePrefetch: true},
+
+   {description:"Use in-flight prefetch as the URLs have the same values for all keys, only differing by order and using ?1 for specifying a true value.",
+    noVarySearch: "key-order=?1",
+    noVarySearchHint: "key-order=?1",
+    prefetchQuery: "b=5&a=3&a=4&d=6&c=5&b=3",
+    navigateQuery: "d=6&a=3&b=5&b=3&c=5&a=4",
+    shouldUsePrefetch: true},
+
+   {description:"Don't use in-flight prefetch as key-order is set to false and the URLs are not identical.",
+    noVarySearch: "key-order=?0",
+    noVarySearchHint: "key-order=?1",
+    prefetchQuery: "b=5&a=3&a=4&d=6&c=5&b=3",
+    navigateQuery: "d=6&a=3&b=5&b=3&c=5&a=4",
+    shouldUsePrefetch: false},
+
+   {description:"Use in-flight prefetch as all query parameters except c can be ignored.",
+    noVarySearch: 'params, except=("c")',
+    noVarySearchHint: 'params, except=("c")',
+    prefetchQuery: "b=5&a=3&d=6&c=3",
+    navigateQuery: "a=1&b=2&c=3",
+    shouldUsePrefetch: true},
+
+   {description:"Use in-flight prefetch as all query parameters except c can be ignored." +
+                " Only the last except matters.",
+    noVarySearch: 'params, except=("b"), except=("c")',
+    noVarySearchHint: 'params, except=("b"), except=("c")',
+    prefetchQuery: "b=5&a=3&d=6&c=3",
+    navigateQuery: "a=1&b=2&c=3",
+    shouldUsePrefetch: true},
+
+   {description:"Don't use in-flight prefetch as even though all query parameters" +
+                " except c can be ignored, c has different value.",
+    noVarySearch: 'params, except=("c")',
+    noVarySearchHint: "params",
+    prefetchQuery: "b=5&a=3&d=6&c=3",
+    navigateQuery: "a=1&b=2&c=5",
+    shouldUsePrefetch: false},
+
+   {description:"Use in-flight prefetch as even though all query parameters" +
+                " except c and d can be ignored, c value matches and d value matches.",
+    noVarySearch: 'params, except=("c" "d")',
+    noVarySearchHint: 'params, except=("c" "d")',
+    prefetchQuery: "b=5&a=3&d=6&c=5",
+    navigateQuery: "d=6&a=1&b=2&c=5",
+    shouldUsePrefetch: true},
+
+   {description:"Use in-flight prefetch as even though all query parameters except" +
+                " c and d can be ignored, c value matches and d value matches." +
+                " Some query parameters to be ignored appear multiple times in the query.",
+    noVarySearch: 'params, except=("c" "d")',
+    noVarySearchHint: 'params',
+    prefetchQuery: "b=5&a=3&a=4&d=6&c=5",
+    navigateQuery: "d=6&a=1&a=2&b=2&b=3&c=5",
+    shouldUsePrefetch: true},
+
+   {description:"Use in-flight prefetch as all query parameters except c can be ignored." +
+                " Allow extension via parameters.",
+    noVarySearch: 'params, except=("c";unknown)',
+    noVarySearchHint: 'params, except=("c";unknown)',
+    prefetchQuery: "b=5&a=3&d=6&c=3",
+    navigateQuery: "a=1&b=2&c=3",
+    shouldUsePrefetch: true},
+
+   {description:"Use in-flight prefetch as query parameter c can be ignored." +
+                " Allow extension via parameters.",
+    noVarySearch: 'params=("c";unknown)',
+    noVarySearchHint: 'params=("c";unknown)',
+    prefetchQuery: "a=2&b=2&c=5",
+    navigateQuery: "a=2&c=3&b=2",
+    shouldUsePrefetch: true},
+
+   {description:"Use in-flight prefetch as the URLs have the values in different order for a." +
+                " Allow extension via parameters.",
+    noVarySearch: "key-order;unknown",
+    noVarySearchHint: "key-order;unknown",
+    prefetchQuery: "b=5&a=3&a=4&d=6&c=5&b=3",
+    navigateQuery: "d=6&a=3&b=5&b=3&c=5&a=4",
+    shouldUsePrefetch: true},
+
+   {description:"Use in-flight prefetch as the URLs do not vary on any query parameters." +
+                " Allow extension via parameters.",
+    noVarySearch: "params;unknown",
+    noVarySearchHint: "params;unknown",
+    prefetchQuery: "",
+    navigateQuery: "b=4&c=5",
+    shouldUsePrefetch: true},
+
+   {description:"Use in-flight prefetch as all query parameters except c can be ignored." +
+                " Allow extension via parameters.",
+    noVarySearch: 'params;unknown, except=("c");unknown',
+    noVarySearchHint: 'params;unknown, except=("c");unknown',
+    prefetchQuery: "b=5&a=3&d=6&c=3",
+    navigateQuery: "a=1&b=2&c=3",
+    shouldUsePrefetch: true},
+
+   {description:"Don't use the in-flight prefetched URL. Empty No-Vary-Search means default URL variance." +
+                " The prefetched and the navigated URLs have to be the same.",
+    noVarySearch: "",
+    noVarySearchHint: "params",
+    prefetchQuery: "b=5&a=3&d=6&c=3",
+    navigateQuery: "a=1&b=2&c=3",
+    shouldUsePrefetch: false},
+
+   {description:"Use the in-flight prefetch. Empty No-Vary-Search means default URL variance." +
+                " The prefetched and the navigated URLs have to be the same.",
+    noVarySearch: "",
+    noVarySearchHint: "",
+    prefetchQuery: "b=5&a=3&d=6&c=3",
+    navigateQuery: "b=5&a=3&d=6&c=3",
+    shouldUsePrefetch: true},
+
+   {description:"Use the in-flight prefetch. Empty No-Vary-Search means default URL variance." +
+                " The prefetched and the navigated URLs have to be the same.",
+    noVarySearch: "",
+    noVarySearchHint: "",
+    prefetchQuery: "",
+    navigateQuery: "",
+    shouldUsePrefetch: true},
+
+   {description:"Use the in-flight prefetch. Non-ASCII key - 2 UTF-8 code units." +
+                " Don't vary the response on the non-ASCII key.",
+    noVarySearch: 'params=("%C2%A2")',
+    noVarySearchHint: 'params=("%C2%A2")',
+    prefetchQuery: "¢=3",
+    navigateQuery: "¢=4",
+    shouldUsePrefetch: true},
+
+   {description:"Use the in-flight prefetch. Non-ASCII key - 2 UTF-8 code units." +
+                " Don't vary the response on the non-ASCII key.",
+    noVarySearch: 'params=("%C2%A2")',
+    noVarySearchHint: 'params=("%C2%A2")',
+    prefetchQuery: "a=2&¢=3",
+    navigateQuery: "¢=4&a=2",
+    shouldUsePrefetch: true},
+
+   {description:"Don't use the in-flight prefetch. Non-ASCII key - 2 UTF-8 code units." +
+                " Vary the response on the non-ASCII key.",
+    noVarySearch: 'params, except=("%C2%A2")',
+    noVarySearchHint: 'params',
+    prefetchQuery: "¢=3",
+    navigateQuery: "¢=4",
+    shouldUsePrefetch: false},
+
+   {description:"Use the in-flight prefetch. Non-ASCII key - 2 UTF-8 code units." +
+                 " Vary the response on the non-ASCII key.",
+    noVarySearch: 'params, except=("%C2%A2")',
+    noVarySearchHint: 'params, except=("%C2%A2")',
+    prefetchQuery: "¢=3&a=4",
+    navigateQuery: "a=5&¢=3",
+    shouldUsePrefetch: true},
+
+  ].forEach(({description, noVarySearch, noVarySearchHint, prefetchQuery, navigateQuery, shouldUsePrefetch}) => {
+    subsetTest(prefetch_no_vary_search_test,
+      description, noVarySearch, noVarySearchHint, prefetchQuery, navigateQuery,
+      shouldUsePrefetch);
+  });
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/prefetch_nvs_hint.py b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/prefetch_nvs_hint.py
new file mode 100644
index 0000000..09c5d2eb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/speculation-rules/prefetch/resources/prefetch_nvs_hint.py
@@ -0,0 +1,34 @@
+import time
+
+def main(request, response):
+  uuid = request.GET[b"uuid"]
+  prefetch = request.headers.get(
+      "Sec-Purpose", b"").decode("utf-8").startswith("prefetch")
+  if b"unblock" in request.GET:
+    request.server.stash.put(uuid, 0)
+    return ''
+
+  if b"nvs_header" in request.GET:
+    nvs_header = request.GET[b"nvs_header"]
+    response.headers.set("No-Vary-Search", nvs_header)
+
+  if prefetch:
+    nvswait = None
+    while nvswait is None:
+      time.sleep(0.1)
+      nvswait = request.server.stash.take(uuid)
+
+  content = (f'<!DOCTYPE html>\n'
+             f'<script src="/common/dispatcher/dispatcher.js"></script>\n'
+             f'<script src="utils.sub.js"></script>\n'
+             f'<script>\n'
+             f'  window.requestHeaders = {{\n'
+             f'    purpose: "{request.headers.get("Purpose", b"").decode("utf-8")}",\n'
+             f'    sec_purpose: "{request.headers.get("Sec-Purpose", b"").decode("utf-8")}",\n'
+             f'    referer: "{request.headers.get("Referer", b"").decode("utf-8")}",\n'
+             f'  }};\n'
+             f'  const uuid = new URLSearchParams(location.search).get("uuid");\n'
+             f'  window.executor = new Executor(uuid);\n'
+             f'</script>\n')
+
+  return content
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
index e2bda98..b9df6de 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-listing-expected.txt
@@ -57,7 +57,7 @@
 animation-delay-end: 0s
 animation-delay-start: 0s
 animation-direction: normal
-animation-duration: 0s
+animation-duration: auto
 animation-fill-mode: none
 animation-iteration-count: 1
 animation-name: none
diff --git a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
index 0d8c0b3..af91557 100644
--- a/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
+++ b/third_party/blink/web_tests/fast/css/getComputedStyle/computed-style-without-renderer-listing-expected.txt
@@ -57,7 +57,7 @@
 animation-delay-end: 0s
 animation-delay-start: 0s
 animation-direction: normal
-animation-duration: 0s
+animation-duration: auto
 animation-fill-mode: none
 animation-iteration-count: 1
 animation-name: none
diff --git a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
index 996af4d..def84dc2 100644
--- a/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
+++ b/third_party/blink/web_tests/svg/css/getComputedStyle-listing-expected.txt
@@ -57,7 +57,7 @@
 animation-delay-end: 0s
 animation-delay-start: 0s
 animation-direction: normal
-animation-duration: 0s
+animation-duration: auto
 animation-fill-mode: none
 animation-iteration-count: 1
 animation-name: none
diff --git a/tools/clang/rewrite_templated_container_fields/RewriteTemplatedPtrFields.cpp b/tools/clang/rewrite_templated_container_fields/RewriteTemplatedPtrFields.cpp
index 67573ae3..740d0ce 100644
--- a/tools/clang/rewrite_templated_container_fields/RewriteTemplatedPtrFields.cpp
+++ b/tools/clang/rewrite_templated_container_fields/RewriteTemplatedPtrFields.cpp
@@ -697,6 +697,27 @@
       lhs.replacement = getReplacementDirective(
           replacement_text, replacement_range, source_manager, ast_context);
       lhs.include_directive = lhs.replacement;
+    } else if (const clang::ParmVarDecl* var_decl =
+                   result.Nodes.getNodeAs<clang::ParmVarDecl>(
+                       "lambda_parmVarDecl")) {
+      auto* type_loc =
+          result.Nodes.getNodeAs<clang::TypeLoc>("template_type_param_loc");
+
+      auto* md = result.Nodes.getNodeAs<clang::CXXMethodDecl>("md");
+
+      clang::SourceRange replacement_range(var_decl->getBeginLoc(),
+                                           type_loc->getEndLoc());
+
+      std::string replacement_text =
+          (md->getParamDecl(var_decl->getFunctionScopeIndex()))
+              ->getType()
+              ->getPointeeType()
+              .getAsString();
+
+      replacement_text = RemovePrefix(replacement_text);
+      lhs.replacement = getReplacementDirective(
+          replacement_text, replacement_range, source_manager, ast_context);
+      lhs.include_directive = lhs.replacement;
     }
 
     Node rhs;
@@ -1052,6 +1073,33 @@
                        has(cxxOperatorCallExpr(has(expr(rhs_expr_variations)))
                                .bind("affectedOpCall"))))));
       match_finder_.addMatcher(affected_op_call, &affected_ptr_expr_rewriter_);
+
+      // handles expressions of the form:
+      // base::ranges::any_of(view->children(), [](const auto* v) {
+      //     ...
+      //   });
+      // where auto* needs to be rewritten into type_name*.
+      auto range_exprs = callExpr(
+          callee(functionDecl(anyOf(
+              matchesName("find"), matchesName("any_of"), matchesName("all_of"),
+              matchesName("transform"), matchesName("copy"),
+              matchesName("accumulate"), matchesName("count")))),
+          hasArgument(0, traverse(clang::TK_IgnoreUnlessSpelledInSource,
+                                  expr(anyOf(rhs_expr_variations, reversed_expr,
+                                             ctn_like_type)))),
+          hasAnyArgument(expr(allOf(
+              traverse(
+                  clang::TK_IgnoreUnlessSpelledInSource,
+                  lambdaExpr(
+                      has(parmVarDecl(
+                              hasTypeLoc(loc(qualType(anything()))
+                                             .bind("template_type_param_loc")),
+                              hasType(pointsTo(templateTypeParmType())))
+                              .bind("lambda_parmVarDecl")))
+                      .bind("lambda_expr")),
+              lambdaExpr(has(cxxRecordDecl(has(functionTemplateDecl(has(
+                  cxxMethodDecl(isTemplateInstantiation()).bind("md")))))))))));
+      match_finder_.addMatcher(range_exprs, &affected_ptr_expr_rewriter_);
     }
 
     // needed for ternary operator expr: (cond) ? true_expr : false_expr;
diff --git a/tools/clang/rewrite_templated_container_fields/tests/various-tests-expected.cc b/tools/clang/rewrite_templated_container_fields/tests/various-tests-expected.cc
index d0efe3c1..2b62f36 100644
--- a/tools/clang/rewrite_templated_container_fields/tests/various-tests-expected.cc
+++ b/tools/clang/rewrite_templated_container_fields/tests/various-tests-expected.cc
@@ -1,8 +1,10 @@
 // Copyright 2023 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+#include <algorithm>
 #include <map>
 #include <memory>
+#include <numeric>
 #include <vector>
 
 #include "base/memory/raw_ptr.h"
@@ -554,7 +556,9 @@
 
 namespace {
 namespace A {
-struct SA {};
+struct SA {
+  int count;
+};
 }  // namespace A
 
 namespace B {
@@ -562,13 +566,64 @@
   // Expected rewrite: std::vector<raw_ptr<const A::SA>> member;
   std::vector<raw_ptr<const A::SA>> member;
 
-  void fct() {
+  bool fct() {
     // This tests whether we properly trim (anonymous namespace):: from the type
     // while conserving constness.
     // Expected rewrite: for(const A::SA* i : member)
     for (const A::SA* i : member) {
       (void)i;
     }
+
+    return std::any_of(
+        member.begin(), member.end(),
+        // Expected rewrite: [](const A::SA* item) { return item != nullptr; });
+        [](const A::SA* item) { return item != nullptr; });
+  }
+
+  std::vector<raw_ptr<const A::SA>>::iterator fct2() {
+    return std::find_if(
+        member.begin(), member.end(),
+        // Expected rewrite: [](const A::SA* item) { return item == nullptr; });
+        [](const A::SA* item) { return item == nullptr; });
+  }
+
+  bool fct3() {
+    return std::all_of(
+        member.begin(), member.end(),
+        // Expected rewrite: [](const A::SA* item) { return item != nullptr; });
+        [](const A::SA* item) { return item != nullptr; });
+  }
+
+  int fct4() {
+    return std::accumulate(member.begin(), member.end(), 1,
+                           // Expected rewrite: [](int num, const A::SA* item) {
+                           [](int num, const A::SA* item) {
+                             return (item != nullptr) ? 1 + num : 0;
+                           });
+  }
+
+  int fct5() {
+    return std::count_if(
+        member.begin(), member.end(),
+        // Expected rewrite: [](const A::SA* item) { return item != nullptr; });
+        [](const A::SA* item) { return item != nullptr; });
+  }
+
+  void fct6() {
+    std::vector<int> copy;
+    std::transform(
+        member.begin(), member.end(), std::back_inserter(copy),
+        // Expected rewrite: [](const A::SA* item) { return item->count; });
+        [](const A::SA* item) { return item->count; });
+  }
+
+  std::vector<const A::SA*> fct7() {
+    std::vector<const A::SA*> copy;
+    std::copy_if(
+        member.begin(), member.end(), std::back_inserter(copy),
+        // Expected rewrite: [](const A::SA* item) { return item != nullptr; });
+        [](const A::SA* item) { return item != nullptr; });
+    return copy;
   }
 };
 }  // namespace B
diff --git a/tools/clang/rewrite_templated_container_fields/tests/various-tests-original.cc b/tools/clang/rewrite_templated_container_fields/tests/various-tests-original.cc
index 99ad93c..f8ff86a8 100644
--- a/tools/clang/rewrite_templated_container_fields/tests/various-tests-original.cc
+++ b/tools/clang/rewrite_templated_container_fields/tests/various-tests-original.cc
@@ -1,8 +1,10 @@
 // Copyright 2023 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+#include <algorithm>
 #include <map>
 #include <memory>
+#include <numeric>
 #include <vector>
 
 #define RAW_PTR_EXCLUSION __attribute__((annotate("raw_ptr_exclusion")))
@@ -547,7 +549,9 @@
 
 namespace {
 namespace A {
-struct SA {};
+struct SA {
+  int count;
+};
 }  // namespace A
 
 namespace B {
@@ -555,13 +559,64 @@
   // Expected rewrite: std::vector<raw_ptr<const A::SA>> member;
   std::vector<const A::SA*> member;
 
-  void fct() {
+  bool fct() {
     // This tests whether we properly trim (anonymous namespace):: from the type
     // while conserving constness.
     // Expected rewrite: for(const A::SA* i : member)
     for (auto* i : member) {
       (void)i;
     }
+
+    return std::any_of(
+        member.begin(), member.end(),
+        // Expected rewrite: [](const A::SA* item) { return item != nullptr; });
+        [](auto* item) { return item != nullptr; });
+  }
+
+  std::vector<const A::SA*>::iterator fct2() {
+    return std::find_if(
+        member.begin(), member.end(),
+        // Expected rewrite: [](const A::SA* item) { return item == nullptr; });
+        [](const auto* item) { return item == nullptr; });
+  }
+
+  bool fct3() {
+    return std::all_of(
+        member.begin(), member.end(),
+        // Expected rewrite: [](const A::SA* item) { return item != nullptr; });
+        [](auto* item) { return item != nullptr; });
+  }
+
+  int fct4() {
+    return std::accumulate(member.begin(), member.end(), 1,
+                           // Expected rewrite: [](int num, const A::SA* item) {
+                           [](int num, const auto* item) {
+                             return (item != nullptr) ? 1 + num : 0;
+                           });
+  }
+
+  int fct5() {
+    return std::count_if(
+        member.begin(), member.end(),
+        // Expected rewrite: [](const A::SA* item) { return item != nullptr; });
+        [](const auto* item) { return item != nullptr; });
+  }
+
+  void fct6() {
+    std::vector<int> copy;
+    std::transform(
+        member.begin(), member.end(), std::back_inserter(copy),
+        // Expected rewrite: [](const A::SA* item) { return item->count; });
+        [](const auto* item) { return item->count; });
+  }
+
+  std::vector<const A::SA*> fct7() {
+    std::vector<const A::SA*> copy;
+    std::copy_if(
+        member.begin(), member.end(), std::back_inserter(copy),
+        // Expected rewrite: [](const A::SA* item) { return item != nullptr; });
+        [](const auto* item) { return item != nullptr; });
+    return copy;
   }
 };
 }  // namespace B
diff --git a/tools/clang/scripts/test_tool.py b/tools/clang/scripts/test_tool.py
index 74f6c892..94ea399 100755
--- a/tools/clang/scripts/test_tool.py
+++ b/tools/clang/scripts/test_tool.py
@@ -36,7 +36,7 @@
   include_path_flags = ' '.join('-I %s' % include_path.replace('\\', '/')
                                 for include_path in include_paths)
   return json.dumps([{'directory': os.path.dirname(f),
-                      'command': 'clang++ -std=c++14 -fsyntax-only %s -c %s' % (
+                      'command': 'clang++ -std=c++17 -fsyntax-only %s -c %s' % (
                           include_path_flags, os.path.basename(f)),
                       'file': os.path.basename(f)} for f in files], indent=2)
 
diff --git a/tools/gdb/gdb_chrome.py b/tools/gdb/gdb_chrome.py
index 2ed7e49..dec00331 100644
--- a/tools/gdb/gdb_chrome.py
+++ b/tools/gdb/gdb_chrome.py
@@ -481,7 +481,7 @@
     return self.val['__a_']['__a_value']
 
 
-pp_set.add_printer('std::Cr::__atomic', '^std::Cr::(__)?atomic<.*>$',
+pp_set.add_printer('std::__Cr::__atomic', '^std::__Cr::(__)?atomic<.*>$',
                    AtomicPrinter)
 
 gdb.printing.register_pretty_printer(gdb, pp_set, replace=_DEBUGGING)
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 1165210..31f8660 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -35353,6 +35353,44 @@
   </description>
 </action>
 
+<action name="TabGroups_SavedTabGroups_Closed">
+  <owner>dljames@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <description>
+    User right-clicked the tab group header on a SavedTabGroup, and pressed the
+    close group button.
+  </description>
+</action>
+
+<action name="TabGroups_SavedTabGroups_Deleted">
+  <owner>dljames@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <description>
+    User performed an action that would delete a SavedTabGroup (Ex: Right-click
+    a button in the bookmarks bar and press delete group, close the last tab in
+    a group, unsave a group). This user action is not emitted when a change
+    originated from the sync service that would cause a group to be deleted.
+  </description>
+</action>
+
+<action name="TabGroups_SavedTabGroups_Focused">
+  <owner>dljames@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <description>
+    User attempted to open a SavedTabGroup that was already open by clicking a
+    SavedTabGroup button in the bookmarks bar, but focused the group instead.
+  </description>
+</action>
+
+<action name="TabGroups_SavedTabGroups_Opened">
+  <owner>dljames@chromium.org</owner>
+  <owner>chrome-desktop-ui-sea@google.com</owner>
+  <description>
+    User opened a SavedTabGroup by clicking a SavedTabGroup button in the
+    bookmarks bar.
+  </description>
+</action>
+
 <action name="TabGroups_SwitchGroupedTab">
   <owner>dpenning@chromium.org</owner>
   <owner>chrome-desktop-ui-sea@google.com</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2f49050..d33ca4e1 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -19738,7 +19738,9 @@
   <int value="8" label="No matching source filter data"/>
   <int value="9" label="Not registered"/>
   <int value="10" label="Prohibited by browser policy"/>
-  <int value="11" label="Report window has passed"/>
+  <int value="11" label="Deduplicated"/>
+  <int value="12" label="Report window has passed"/>
+  <int value="13" label="Excessive reports"/>
 </enum>
 
 <enum name="ConversionNavigationDataHostStatus">
@@ -26107,6 +26109,7 @@
   <int value="8" label="Local card autofill reauthentication flow"/>
   <int value="9" label="Device Lock page"/>
   <int value="10" label="Payment methods mandatory reauth in settings"/>
+  <int value="11" label="Virtual card autofill reauthentication flow"/>
 </enum>
 
 <enum name="DeviceEnumerationResult">
@@ -54428,6 +54431,19 @@
              locked for 9+ seconds and the app was quit by OS or the user)."/>
 </enum>
 
+<enum name="IOSStorageCapacity">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="0 GB"/>
+  <int value="2" label="16 GB"/>
+  <int value="3" label="32 GB"/>
+  <int value="4" label="64 GB"/>
+  <int value="5" label="128 GB"/>
+  <int value="6" label="256 GB"/>
+  <int value="7" label="512 GB"/>
+  <int value="8" label="1 TB"/>
+  <int value="9" label="2 TB"/>
+</enum>
+
 <enum name="IOSTabSwitcherDragDropTabs">
   <int value="0" label="A tab is dragged."/>
   <int value="1" label="A tab is dropped at the same index position."/>
@@ -54697,6 +54713,13 @@
   <int value="15" label="Post launch update ignored"/>
 </enum>
 
+<enum name="ItemSuggestRequestResult">
+  <int value="0" label="Success"/>
+  <int value="1" label="Network error"/>
+  <int value="2" label="JSON parse error"/>
+  <int value="3" label="Content error"/>
+</enum>
+
 <enum name="JankyBoolean">
   <int value="0" label="Non Janky"/>
   <int value="1" label="Janky"/>
diff --git a/tools/metrics/histograms/metadata/gpu/histograms.xml b/tools/metrics/histograms/metadata/gpu/histograms.xml
index 846acba..0636589 100644
--- a/tools/metrics/histograms/metadata/gpu/histograms.xml
+++ b/tools/metrics/histograms/metadata/gpu/histograms.xml
@@ -1563,6 +1563,26 @@
   </token>
 </histogram>
 
+<histogram name="GPU.WebGLDisplayType" enum="EGLDisplayType"
+    expires_after="2023-08-27">
+  <owner>geofflang@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Types of EGL displays used to back WebGL contexts. Recorded at context
+    creation time in the GPU process.
+  </summary>
+</histogram>
+
+<histogram name="GPU.WebGLDisplayTypeLarge" enum="EGLDisplayType"
+    expires_after="2023-08-27">
+  <owner>geofflang@chromium.org</owner>
+  <owner>graphics-dev@chromium.org</owner>
+  <summary>
+    Types of EGL displays used to back large (area &gt;= 128*128) WebGL
+    contexts. Recorded at context creation time in the GPU process.
+  </summary>
+</histogram>
+
 <histogram name="GPU.WebGraphicsContext3D_Init_CanLoseContext"
     enum="GPUWebGraphicsContext3D_Init_CanLoseContext" expires_after="M85">
   <owner>vmiura@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index c806613..7f81b7b 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -2025,6 +2025,76 @@
   </summary>
 </histogram>
 
+<histogram name="IOS.SandboxMetrics.AvailableCapacity" units="MB"
+    expires_after="2023-10-01">
+  <owner>michaeldo@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The total amount of available storage capacity on the device. Logged once
+    per application cold launch, on startup.
+  </summary>
+</histogram>
+
+<histogram name="IOS.SandboxMetrics.AvailableCapacityPercentage" units="%"
+    expires_after="2023-10-01">
+  <owner>michaeldo@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The percentage of the device storage which is available. Logged once per
+    application cold launch, on startup.
+  </summary>
+</histogram>
+
+<histogram name="IOS.SandboxMetrics.CapacityForImportantUsage" units="MB"
+    expires_after="2023-10-01">
+  <owner>michaeldo@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The amount of device storage which is available for important storage.
+    Logged once per application cold launch, on startup.
+  </summary>
+</histogram>
+
+<histogram name="IOS.SandboxMetrics.CapacityForImportantUsagePercentage"
+    units="%" expires_after="2023-10-01">
+  <owner>michaeldo@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The percentage of the device storage which is available for important
+    storage. Logged once per application cold launch, on startup.
+  </summary>
+</histogram>
+
+<histogram name="IOS.SandboxMetrics.CapacityForOpportunisticUsage" units="MB"
+    expires_after="2023-10-01">
+  <owner>michaeldo@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The amount of device storage which is available for opportunistic storage.
+    Logged once per application cold launch, on startup.
+  </summary>
+</histogram>
+
+<histogram name="IOS.SandboxMetrics.CapacityForOpportunisticUsagePercentage"
+    units="%" expires_after="2023-10-01">
+  <owner>michaeldo@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The percentage of the device storage which is available for opportunistic
+    storage. Logged once per application cold launch, on startup.
+  </summary>
+</histogram>
+
+<histogram name="IOS.SandboxMetrics.TotalCapacity" enum="IOSStorageCapacity"
+    expires_after="2023-10-01">
+  <owner>michaeldo@chromium.org</owner>
+  <owner>rohitrao@chromium.org</owner>
+  <summary>
+    The total amount of storage capacity on the device. Logged once per
+    application cold launch, on startup.
+  </summary>
+</histogram>
+
 <histogram name="IOS.SearchExtension.Action" enum="IOSSearchExtensionAction"
     expires_after="2023-09-24">
   <owner>olivierrobin@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
index c0544f1..b3af776 100644
--- a/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
+++ b/tools/metrics/histograms/metadata/new_tab_page/histograms.xml
@@ -639,6 +639,18 @@
   </summary>
 </histogram>
 
+<histogram name="NewTabPage.Drive.ItemSuggestRequestResult"
+    enum="ItemSuggestRequestResult" expires_after="never">
+<!-- expires-never: Used for Chirp monitoring. -->
+
+  <owner>tiborg@chromium.org</owner>
+  <owner>chrome-desktop-ntp@google.com</owner>
+  <summary>
+    Logs the result of a request to the ItemSuggest API. Logged every time the
+    Drive module makes an ItemSuggest request.
+  </summary>
+</histogram>
+
 <histogram name="NewTabPage.FeedPositionSegmentationResult"
     enum="FeedPositionSegmentationResult" expires_after="2023-09-10">
   <owner>hanxi@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 9307190..e5a816b 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -3383,7 +3383,7 @@
   </summary>
 </histogram>
 
-<histogram name="Conversions.AggregatableReport.CreateReportStatus3"
+<histogram name="Conversions.AggregatableReport.CreateReportStatus4"
     enum="ConversionCreateAggregatableReportStatus" expires_after="2023-10-08">
   <owner>linnan@chromium.org</owner>
   <owner>johnidel@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/power/histograms.xml b/tools/metrics/histograms/metadata/power/histograms.xml
index 43cedde..b0ef2d9 100644
--- a/tools/metrics/histograms/metadata/power/histograms.xml
+++ b/tools/metrics/histograms/metadata/power/histograms.xml
@@ -796,7 +796,7 @@
 </histogram>
 
 <histogram name="Power.BacklightLevel{PrivacyScreenState}{PowerSource}"
-    units="%" expires_after="2023-05-12">
+    units="%" expires_after="2024-05-12">
   <owner>puthik@chromium.org</owner>
   <owner>mqg@chromium.org</owner>
   <owner>chromeos-platform-power@google.com</owner>
@@ -821,7 +821,7 @@
 </histogram>
 
 <histogram name="Power.BatteryCapacity.{BatteryCapacityType}" units="mWh"
-    expires_after="2023-05-12">
+    expires_after="2024-05-12">
   <owner>puthik@chromium.org</owner>
   <owner>chromeos-platform-power@google.com</owner>
   <summary>
@@ -1084,7 +1084,7 @@
 </histogram>
 
 <histogram name="Power.BatteryLife.{BatteryCapacityType}" units="minute"
-    expires_after="2023-05-12">
+    expires_after="2024-05-12">
   <owner>puthik@chromium.org</owner>
   <owner>chromeos-platform-power@google.com</owner>
   <summary>
@@ -1099,7 +1099,7 @@
 </histogram>
 
 <histogram name="Power.BatteryLifeWhileSuspended.{BatteryCapacityType}"
-    units="hour" expires_after="2023-05-12">
+    units="hour" expires_after="2024-05-12">
   <owner>puthik@chromium.org</owner>
   <owner>chromeos-platform-power@google.com</owner>
   <summary>
diff --git a/tools/perf/contrib/companion/OWNERS b/tools/perf/contrib/intelligence/OWNERS
similarity index 100%
rename from tools/perf/contrib/companion/OWNERS
rename to tools/perf/contrib/intelligence/OWNERS
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index e6ab872..805b84d 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v34.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "d5fb18033f52e0ca3127c9df8d2f0f286e394b9b",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/c34c76b0a0c322d35c46f6caf154d2e377919da1/trace_processor_shell"
+            "hash": "99d45d8026b4115286a77edcaa91aa31bfe0862b",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/0ce368c86c8aa9cf6338ddcd80c485c3bf1e5e7e/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h b/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h
index ea8bfbdb..6a546bda 100644
--- a/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h
+++ b/ui/ozone/platform/wayland/host/shell_toplevel_wrapper.h
@@ -33,6 +33,8 @@
 // and move.
 class ShellToplevelWrapper {
  public:
+  using ShapeRects = std::vector<gfx::Rect>;
+
   enum class DecorationMode {
     // Initial mode that the surface has till the first configure event.
     kNone,
@@ -181,6 +183,10 @@
   // Sets the persistable window property.
   virtual void SetPersistable(bool persistable) const = 0;
 
+  // Sets the shape of the toplevel window. If `shape_rects` is null this will
+  // unset the window shape.
+  virtual void SetShape(std::unique_ptr<ShapeRects> shape_rects) = 0;
+
   // Casts `this` to XDGToplevelWrapperImpl, if it is of that type.
   virtual XDGToplevelWrapperImpl* AsXDGToplevelWrapper();
 };
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
index ffa862c0..2608064 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.cc
@@ -319,6 +319,13 @@
   return z_order_;
 }
 
+void WaylandToplevelWindow::SetShape(std::unique_ptr<ShapeRects> native_shape,
+                                     const gfx::Transform& transform) {
+  if (shell_toplevel_) {
+    shell_toplevel_->SetShape(std::move(native_shape));
+  }
+}
+
 std::string WaylandToplevelWindow::GetWindowUniqueId() const {
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
   return window_unique_id_;
diff --git a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
index 4ee4266..1209a47d 100644
--- a/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
+++ b/ui/ozone/platform/wayland/host/wayland_toplevel_window.h
@@ -130,6 +130,8 @@
   // `SetUpShellIntegration()`.
   void SetZOrderLevel(ZOrderLevel order) override;
   ZOrderLevel GetZOrderLevel() const override;
+  void SetShape(std::unique_ptr<ShapeRects> native_shape,
+                const gfx::Transform& transform) override;
   std::string GetWindowUniqueId() const override;
   // SetUseNativeFrame and ShouldUseNativeFrame decide on
   // xdg-decoration mode for a window.
diff --git a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
index 6e6c08f..b6dcb0a 100644
--- a/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/host/wayland_window_unittest.cc
@@ -68,6 +68,7 @@
 #include "ui/ozone/platform/wayland/test/test_touch.h"
 #include "ui/ozone/platform/wayland/test/test_util.h"
 #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
+#include "ui/ozone/platform/wayland/test/test_zaura_toplevel.h"
 #include "ui/ozone/platform/wayland/test/wayland_test.h"
 #include "ui/ozone/public/ozone_switches.h"
 #include "ui/platform_window/platform_window.h"
@@ -4539,6 +4540,54 @@
   VerifyAndClearExpectations();
 }
 
+// Asserts the server receives the correct region when SetShape() is called for
+// toplevel windows.
+TEST_P(WaylandWindowTest, SetShape) {
+  // SetShape() is only supported with zaura_shell.
+  if (GetParam().enable_aura_shell != wl::EnableAuraShellProtocol::kEnabled) {
+    GTEST_SKIP();
+  }
+
+  // Define a custom window shape and generate the corresponding region.
+  const PlatformWindow::ShapeRects shape_rects = {{10, 10, 40, 40},
+                                                  {20, 20, 50, 50}};
+  wl::TestRegion shape_region;
+  for (const auto& rect : shape_rects) {
+    shape_region.op(
+        SkIRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()),
+        SkRegion::kUnion_Op);
+  }
+
+  // Set the toplevel window shape.
+  window_->SetShape(std::make_unique<PlatformWindow::ShapeRects>(shape_rects),
+                    {});
+
+  // Validate the server has received the appropriate region for the toplevel.
+  PostToServerAndWait([&](wl::TestWaylandServerThread* server) {
+    auto* surface = server->GetObject<wl::MockSurface>(surface_id_);
+    ASSERT_TRUE(surface);
+
+    wl::TestZAuraToplevel* zaura_toplevel =
+        surface->xdg_surface()->xdg_toplevel()->zaura_toplevel();
+    ASSERT_TRUE(zaura_toplevel);
+    EXPECT_EQ(shape_region, zaura_toplevel->shape());
+  });
+
+  // Unset the toplevel window shape.
+  window_->SetShape(nullptr, {});
+
+  // Validate the server has received and unset the window shape.
+  PostToServerAndWait([&](wl::TestWaylandServerThread* server) {
+    auto* surface = server->GetObject<wl::MockSurface>(surface_id_);
+    ASSERT_TRUE(surface);
+
+    wl::TestZAuraToplevel* zaura_toplevel =
+        surface->xdg_surface()->xdg_toplevel()->zaura_toplevel();
+    ASSERT_TRUE(zaura_toplevel);
+    EXPECT_FALSE(zaura_toplevel->shape().has_value());
+  });
+}
+
 INSTANTIATE_TEST_SUITE_P(XdgVersionStableTest,
                          WaylandWindowTest,
                          Values(wl::ServerConfig{}));
diff --git a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
index 599ea19..cfa978e9 100644
--- a/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
+++ b/ui/ozone/platform/wayland/host/wayland_zaura_shell.cc
@@ -20,7 +20,7 @@
 
 namespace {
 constexpr uint32_t kMinVersion = 1;
-constexpr uint32_t kMaxVersion = 52;
+constexpr uint32_t kMaxVersion = 53;
 }
 
 // static
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
index 3e42b07..d84976f 100644
--- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
+++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.cc
@@ -410,6 +410,19 @@
   }
 }
 
+wl::Object<wl_region> XDGToplevelWrapperImpl::CreateAndAddRegion(
+    const std::vector<gfx::Rect>& shape) {
+  wl::Object<wl_region> region(
+      wl_compositor_create_region(connection_->compositor()));
+
+  for (const auto& rect : shape) {
+    wl_region_add(region.get(), rect.x(), rect.y(), rect.width(),
+                  rect.height());
+  }
+
+  return region;
+}
+
 XDGSurfaceWrapperImpl* XDGToplevelWrapperImpl::xdg_surface_wrapper() const {
   DCHECK(xdg_surface_wrapper_.get());
   return xdg_surface_wrapper_.get();
@@ -629,6 +642,15 @@
   }
 }
 
+void XDGToplevelWrapperImpl::SetShape(std::unique_ptr<ShapeRects> shape_rects) {
+  if (aura_toplevel_ && zaura_toplevel_get_version(aura_toplevel_.get()) >=
+                            ZAURA_TOPLEVEL_SET_SHAPE_SINCE_VERSION) {
+    zaura_toplevel_set_shape(
+        aura_toplevel_.get(),
+        shape_rects ? CreateAndAddRegion(*shape_rects).get() : nullptr);
+  }
+}
+
 void XDGToplevelWrapperImpl::ShowSnapPreview(
     WaylandWindowSnapDirection snap_direction,
     bool allow_haptic_feedback) {
diff --git a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
index d5c45a450..75a1426f 100644
--- a/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
+++ b/ui/ozone/platform/wayland/host/xdg_toplevel_wrapper_impl.h
@@ -69,6 +69,7 @@
   void ShowSnapPreview(WaylandWindowSnapDirection snap_direction,
                        bool allow_haptic_feedback) override;
   void SetPersistable(bool persistable) const override;
+  void SetShape(std::unique_ptr<ShapeRects> shape_rects) override;
 
   XDGToplevelWrapperImpl* AsXDGToplevelWrapper() override;
 
@@ -123,6 +124,9 @@
   // Called when raster scale is changed.
   void OnConfigureRasterScale(double scale);
 
+  // Creates a wl_region from `shape_rects`.
+  wl::Object<wl_region> CreateAndAddRegion(const ShapeRects& shape_rects);
+
   // Ground surface for this toplevel wrapper.
   std::unique_ptr<XDGSurfaceWrapperImpl> xdg_surface_wrapper_;
 
diff --git a/ui/ozone/platform/wayland/test/mock_xdg_surface.h b/ui/ozone/platform/wayland/test/mock_xdg_surface.h
index 26ca6ec..a8cd0b1 100644
--- a/ui/ozone/platform/wayland/test/mock_xdg_surface.h
+++ b/ui/ozone/platform/wayland/test/mock_xdg_surface.h
@@ -25,6 +25,7 @@
 extern const struct zxdg_toplevel_v6_interface kMockZxdgToplevelV6Impl;
 
 class MockXdgTopLevel;
+class TestZAuraToplevel;
 
 // Manage xdg_surface, zxdg_surface_v6 and zxdg_toplevel for providing desktop
 // UI.
@@ -93,7 +94,14 @@
   const gfx::Size& max_size() const { return max_size_; }
   void set_max_size(const gfx::Size& max_size) { max_size_ = max_size; }
 
+  TestZAuraToplevel* zaura_toplevel() { return zaura_toplevel_; }
+  void set_zaura_toplevel(TestZAuraToplevel* zaura_toplevel) {
+    zaura_toplevel_ = zaura_toplevel;
+  }
+
  private:
+  raw_ptr<TestZAuraToplevel> zaura_toplevel_ = nullptr;
+
   gfx::Size min_size_;
   gfx::Size max_size_;
 
diff --git a/ui/ozone/platform/wayland/test/test_zaura_shell.cc b/ui/ozone/platform/wayland/test/test_zaura_shell.cc
index 0017859d..0b61cdc 100644
--- a/ui/ozone/platform/wayland/test/test_zaura_shell.cc
+++ b/ui/ozone/platform/wayland/test/test_zaura_shell.cc
@@ -5,6 +5,7 @@
 #include "ui/ozone/platform/wayland/test/test_zaura_shell.h"
 
 #include "base/notreached.h"
+#include "ui/ozone/platform/wayland/test/mock_xdg_surface.h"
 #include "ui/ozone/platform/wayland/test/server_object.h"
 #include "ui/ozone/platform/wayland/test/test_output.h"
 #include "ui/ozone/platform/wayland/test/test_zaura_output.h"
@@ -16,7 +17,7 @@
 
 namespace {
 
-constexpr uint32_t kZAuraShellVersion = 44;
+constexpr uint32_t kZAuraShellVersion = 53;
 constexpr uint32_t kZAuraOutputVersion = 44;
 
 void GetAuraSurface(wl_client* client,
@@ -50,9 +51,13 @@
                                    wl_resource* resource,
                                    uint32_t id,
                                    wl_resource* toplevel) {
-  CreateResourceWithImpl<TestZAuraToplevel>(client, &zaura_toplevel_interface,
-                                            kZAuraShellVersion,
-                                            &kTestZAuraToplevelImpl, id);
+  wl_resource* zaura_toplevel_resource =
+      CreateResourceWithImpl<TestZAuraToplevel>(
+          client, &zaura_toplevel_interface, kZAuraShellVersion,
+          &kTestZAuraToplevelImpl, id);
+  auto* xdg_toplevel = GetUserDataAs<MockXdgTopLevel>(toplevel);
+  xdg_toplevel->set_zaura_toplevel(
+      GetUserDataAs<TestZAuraToplevel>(zaura_toplevel_resource));
 }
 
 void GetAuraPopupForXdgPopup(wl_client* client,
diff --git a/ui/ozone/platform/wayland/test/test_zaura_toplevel.cc b/ui/ozone/platform/wayland/test/test_zaura_toplevel.cc
index 0c88ff74..06d613b 100644
--- a/ui/ozone/platform/wayland/test/test_zaura_toplevel.cc
+++ b/ui/ozone/platform/wayland/test/test_zaura_toplevel.cc
@@ -104,6 +104,49 @@
   NOTIMPLEMENTED_LOG_ONCE();
 }
 
+void SetScaleFactor(wl_client* client,
+                    wl_resource* resource,
+                    uint32_t scale_factor_as_uint) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void SetSnapPrimary(wl_client* client,
+                    wl_resource* resource,
+                    uint32_t snap_ratio_as_uint) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void SetSnapSecondary(wl_client* client,
+                      wl_resource* resource,
+                      uint32_t snap_ratio_as_uint) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void IntentToSnap(wl_client* client,
+                  wl_resource* resource,
+                  uint32_t snap_direction) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void UnsetSnap(wl_client* client, wl_resource* resource) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void SetPersistable(wl_client* client,
+                    wl_resource* resource,
+                    uint32_t persistable) {
+  NOTIMPLEMENTED_LOG_ONCE();
+}
+
+void SetShape(wl_client* client,
+              wl_resource* resource,
+              wl_resource* region_resource) {
+  GetUserDataAs<TestZAuraToplevel>(resource)->set_shape(
+      region_resource ? absl::optional<TestRegion>(
+                            *GetUserDataAs<TestRegion>(region_resource))
+                      : absl::nullopt);
+}
+
 }  // namespace
 
 TestZAuraToplevel::TestZAuraToplevel(wl_resource* resource)
@@ -129,6 +172,13 @@
     &Activate,
     &Dectivate,
     &SetFullscreenMode,
+    &SetScaleFactor,
+    &SetSnapPrimary,
+    &SetSnapSecondary,
+    &IntentToSnap,
+    &UnsetSnap,
+    &SetPersistable,
+    &SetShape,
 };
 
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_zaura_toplevel.h b/ui/ozone/platform/wayland/test/test_zaura_toplevel.h
index dac7216..5f76499 100644
--- a/ui/ozone/platform/wayland/test/test_zaura_toplevel.h
+++ b/ui/ozone/platform/wayland/test/test_zaura_toplevel.h
@@ -7,7 +7,9 @@
 
 #include <aura-shell-server-protocol.h>
 
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/ozone/platform/wayland/test/server_object.h"
+#include "ui/ozone/platform/wayland/test/test_region.h"
 
 namespace wl {
 
@@ -22,6 +24,15 @@
   TestZAuraToplevel& operator=(const TestZAuraToplevel&) = delete;
 
   ~TestZAuraToplevel() override;
+
+  // TODO(tluk): `shape_` should really not have a public setter method, the
+  // member should instead only be set by the handler that responds to
+  // aura_toplevel.set_shape events from the server.
+  const absl::optional<TestRegion>& shape() const { return shape_; }
+  void set_shape(const absl::optional<TestRegion>& shape) { shape_ = shape; }
+
+ private:
+  absl::optional<TestRegion> shape_;
 };
 
 }  // namespace wl
diff --git a/ui/webui/resources/cr_components/history_clusters/cluster.ts b/ui/webui/resources/cr_components/history_clusters/cluster.ts
index 378da231..5160fcb 100644
--- a/ui/webui/resources/cr_components/history_clusters/cluster.ts
+++ b/ui/webui/resources/cr_components/history_clusters/cluster.ts
@@ -191,7 +191,7 @@
 
   private onOpenAllVisits_() {
     BrowserProxyImpl.getInstance().handler.openVisitUrlsInTabGroup(
-        this.cluster.visits);
+        this.cluster.visits, this.cluster.tabGroupName ?? null);
 
     MetricsProxyImpl.getInstance().recordClusterAction(
         ClusterAction.kOpenedInTabGroup, this.index);
diff --git a/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom b/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
index 05e3d077..16112cec 100644
--- a/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
+++ b/ui/webui/resources/cr_components/history_clusters/history_clusters.mojom
@@ -101,8 +101,9 @@
   // resolves with whether the request succeeded in the History backend layer.
   RemoveVisits(array<URLVisit> visits) => (bool success);
 
-  // Requests to open the URLs in `visits` in a new tab group.
-  OpenVisitUrlsInTabGroup(array<URLVisit> visits);
+  // Requests to open the URLs in `visits` in a new tab group. The new tab group
+  // has an optional name `tab_group_name`.
+  OpenVisitUrlsInTabGroup(array<URLVisit> visits, string? tab_group_name);
 
   // Records visit actions.
   RecordVisitAction(VisitAction visit_action,